diff --git a/.circleci/config.yml b/.circleci/config.yml
index 443207945..f38c5ba29 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -264,22 +264,44 @@ jobs:
path: /tmp/test-artifacts/conformance-coverage.html
build-lotus-soup:
description: |
- Compile `lotus-soup` Testground test plan using the current version of Lotus.
+ Compile `lotus-soup` Testground test plan
parameters:
<<: *test-params
executor: << parameters.executor >>
steps:
- install-deps
- prepare
- - run: cd extern/oni && git submodule sync
- - run: cd extern/oni && git submodule update --init
- run: cd extern/filecoin-ffi && make
- - run:
- name: "replace lotus, filecoin-ffi, blst and fil-blst deps"
- command: cd extern/oni/lotus-soup && go mod edit -replace github.com/filecoin-project/lotus=../../../ && go mod edit -replace github.com/filecoin-project/filecoin-ffi=../../filecoin-ffi && go mod edit -replace github.com/supranational/blst=../../blst
- run:
name: "build lotus-soup testplan"
- command: pushd extern/oni/lotus-soup && go build -tags=testground .
+ command: pushd testplans/lotus-soup && go build -tags=testground .
+ trigger-testplans:
+ description: |
+ Trigger `lotus-soup` test cases on TaaS
+ parameters:
+ <<: *test-params
+ executor: << parameters.executor >>
+ steps:
+ - install-deps
+ - prepare
+ - run:
+ name: "download testground"
+ command: wget https://gist.github.com/nonsense/5fbf3167cac79945f658771aed32fc44/raw/2e17eb0debf7ec6bdf027c1bdafc2c92dd97273b/testground-d3e9603 -O ~/testground-cli && chmod +x ~/testground-cli
+ - run:
+ name: "prepare .env.toml"
+ command: pushd testplans/lotus-soup && mkdir -p $HOME/testground && cp env-ci.toml $HOME/testground/.env.toml && echo 'endpoint="https://ci.testground.ipfs.team"' >> $HOME/testground/.env.toml && echo 'user="circleci"' >> $HOME/testground/.env.toml
+ - run:
+ name: "prepare testground home dir"
+ command: mkdir -p $HOME/testground/plans && mv testplans/lotus-soup testplans/graphsync $HOME/testground/plans/
+ - run:
+ name: "trigger deals baseline testplan on taas"
+ command: ~/testground-cli run composition -f $HOME/testground/plans/lotus-soup/_compositions/baseline-k8s-3-1.toml --metadata-commit=$CIRCLE_SHA1 --metadata-repo=filecoin-project/lotus --metadata-branch=$CIRCLE_BRANCH
+ - run:
+ name: "trigger payment channel stress testplan on taas"
+ command: ~/testground-cli run composition -f $HOME/testground/plans/lotus-soup/_compositions/paych-stress-k8s.toml --metadata-commit=$CIRCLE_SHA1 --metadata-repo=filecoin-project/lotus --metadata-branch=$CIRCLE_BRANCH
+ - run:
+ name: "trigger graphsync testplan on taas"
+ command: ~/testground-cli run composition -f $HOME/testground/plans/graphsync/_compositions/stress-k8s.toml --metadata-commit=$CIRCLE_SHA1 --metadata-repo=filecoin-project/lotus --metadata-branch=$CIRCLE_BRANCH
build-macos:
@@ -473,6 +495,11 @@ workflows:
packages: "./conformance"
vectors-branch: master
- build-lotus-soup
+ - trigger-testplans:
+ filters:
+ branches:
+ only:
+ - master
- build-debug
- build-all:
requires:
diff --git a/.gitmodules b/.gitmodules
index 5d82758a2..127386beb 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -7,9 +7,6 @@
[submodule "extern/test-vectors"]
path = extern/test-vectors
url = https://github.com/filecoin-project/test-vectors.git
-[submodule "extern/oni"]
- path = extern/oni
- url = https://github.com/filecoin-project/oni.git
[submodule "extern/blst"]
path = extern/blst
url = https://github.com/supranational/blst.git
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f290faf83..fccbb9263 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,15 @@
# Lotus changelog
+# 1.2.1 / 2020-11-20
+
+This is a very small release of Lotus that fixes an issue users are experiencing when importing snapshots. There is no need to upgrade unless you experience an issue with creating a new `datastore` directory in the Lotus repo.
+
+## Changes
+
+- fix blockstore directory not created automatically (https://github.com/filecoin-project/lotus/pull/4922)
+- WindowPoStScheduler.checkSectors() delete useless judgment (https://github.com/filecoin-project/lotus/pull/4918)
+
+
# 1.2.0 / 2020-11-18
This is a mandatory release of Lotus that introduces the second post-liftoff upgrade to the Filecoin network. The network upgrade occurs at height 265200, before which time all nodes must have update to this release (or later). This release also bumps the required version of Go to 1.15.
diff --git a/Makefile b/Makefile
index 9c77aa8ee..ef6158376 100644
--- a/Makefile
+++ b/Makefile
@@ -5,9 +5,9 @@ all: build
unexport GOFLAGS
-GOVERSION:=$(shell go version | cut -d' ' -f 3 | awk -F. '{printf "%d%03d", $$2, $$3}')
-ifeq ($(shell expr $(GOVERSION) \< 15005), 1)
-$(warning Your Golang version is go 1.$(shell expr $(GOVERSION) / 1000).$(shell expr $(GOVERSION) % 1000))
+GOVERSION:=$(shell go version | cut -d' ' -f 3 | sed 's/^go//' | awk -F. '{printf "%d%03d%03d", $$1, $$2, $$3}')
+ifeq ($(shell expr $(GOVERSION) \< 1015005), 1)
+$(warning Your Golang version is go$(shell expr $(GOVERSION) / 1000000).$(shell expr $(GOVERSION) % 1000000 / 1000).$(shell expr $(GOVERSION) % 1000))
$(error Update Golang to version to at least 1.15.5)
endif
diff --git a/README.md b/README.md
index fa432bf7d..59927faa3 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@
-
+
diff --git a/api/api_common.go b/api/api_common.go
index 5b036d1f6..30a52e866 100644
--- a/api/api_common.go
+++ b/api/api_common.go
@@ -46,6 +46,11 @@ type Common interface {
// usage and current rate per protocol
NetBandwidthStatsByProtocol(ctx context.Context) (map[protocol.ID]metrics.Stats, error)
+ // ConnectionGater API
+ NetBlockAdd(ctx context.Context, acl NetBlockList) error
+ NetBlockRemove(ctx context.Context, acl NetBlockList) error
+ NetBlockList(ctx context.Context) (NetBlockList, error)
+
// MethodGroup: Common
// ID returns peerID of libp2p node backing this API
diff --git a/api/api_full.go b/api/api_full.go
index d5e97d3a6..1edebc4a6 100644
--- a/api/api_full.go
+++ b/api/api_full.go
@@ -2,6 +2,7 @@ package api
import (
"context"
+ "encoding/json"
"fmt"
"time"
@@ -785,6 +786,22 @@ type StartDealParams struct {
VerifiedDeal bool
}
+func (s *StartDealParams) UnmarshalJSON(raw []byte) (err error) {
+ type sdpAlias StartDealParams
+
+ sdp := sdpAlias{
+ FastRetrieval: true,
+ }
+
+ if err := json.Unmarshal(raw, &sdp); err != nil {
+ return err
+ }
+
+ *s = StartDealParams(sdp)
+
+ return nil
+}
+
type IpldObject struct {
Cid cid.Cid
Obj interface{}
diff --git a/api/api_gateway.go b/api/api_gateway.go
index 07fb5deb3..c76c1672d 100644
--- a/api/api_gateway.go
+++ b/api/api_gateway.go
@@ -39,6 +39,7 @@ type GatewayAPI interface {
StateMinerProvingDeadline(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*dline.Info, error)
StateMinerPower(context.Context, address.Address, types.TipSetKey) (*MinerPower, error)
StateNetworkVersion(context.Context, types.TipSetKey) (network.Version, error)
+ StateSectorGetInfo(ctx context.Context, maddr address.Address, n abi.SectorNumber, tsk types.TipSetKey) (*miner.SectorOnChainInfo, error)
StateVerifiedClientStatus(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*abi.StoragePower, error)
StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64) (*MsgLookup, error)
}
diff --git a/api/api_storage.go b/api/api_storage.go
index 738a05e09..5d0ec73a7 100644
--- a/api/api_storage.go
+++ b/api/api_storage.go
@@ -19,6 +19,7 @@ import (
"github.com/filecoin-project/lotus/extern/sector-storage/fsutil"
"github.com/filecoin-project/lotus/extern/sector-storage/stores"
"github.com/filecoin-project/lotus/extern/sector-storage/storiface"
+ "github.com/filecoin-project/specs-storage/storage"
)
// StorageMiner is a low-level interface to the Filecoin network storage miner node
@@ -116,6 +117,8 @@ type StorageMiner interface {
// LOTUS_BACKUP_BASE_PATH environment variable set to some path, and that
// the path specified when calling CreateBackup is within the base path
CreateBackup(ctx context.Context, fpath string) error
+
+ CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof, sectors []storage.SectorRef, expensive bool) (map[abi.SectorNumber]string, error)
}
type SealRes struct {
diff --git a/api/api_worker.go b/api/api_worker.go
index 805b23bc1..e85f1e7d4 100644
--- a/api/api_worker.go
+++ b/api/api_worker.go
@@ -23,6 +23,9 @@ type WorkerAPI interface {
storiface.WorkerCalls
+ TaskDisable(ctx context.Context, tt sealtasks.TaskType) error
+ TaskEnable(ctx context.Context, tt sealtasks.TaskType) error
+
// Storage / Other
Remove(ctx context.Context, sector abi.SectorID) error
diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go
index 214f56422..3cf0647e3 100644
--- a/api/apistruct/struct.go
+++ b/api/apistruct/struct.go
@@ -60,6 +60,9 @@ type CommonStruct struct {
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"`
+ NetBlockAdd func(ctx context.Context, acl api.NetBlockList) error `perm:"admin"`
+ NetBlockRemove func(ctx context.Context, acl api.NetBlockList) error `perm:"admin"`
+ NetBlockList func(ctx context.Context) (api.NetBlockList, error) `perm:"read"`
ID func(context.Context) (peer.ID, error) `perm:"read"`
Version func(context.Context) (api.Version, error) `perm:"read"`
@@ -360,6 +363,8 @@ type StorageMinerStruct struct {
PiecesGetCIDInfo func(ctx context.Context, payloadCid cid.Cid) (*piecestore.CIDInfo, error) `perm:"read"`
CreateBackup func(ctx context.Context, fpath string) error `perm:"admin"`
+
+ CheckProvable func(ctx context.Context, pp abi.RegisteredPoStProof, sectors []storage.SectorRef, expensive bool) (map[abi.SectorNumber]string, error) `perm:"admin"`
}
}
@@ -385,6 +390,9 @@ type WorkerStruct struct {
ReadPiece func(context.Context, io.Writer, storage.SectorRef, storiface.UnpaddedByteIndex, abi.UnpaddedPieceSize) (storiface.CallID, error) `perm:"admin"`
Fetch func(context.Context, storage.SectorRef, storiface.SectorFileType, storiface.PathType, storiface.AcquireMode) (storiface.CallID, error) `perm:"admin"`
+ TaskDisable func(ctx context.Context, tt sealtasks.TaskType) error `perm:"admin"`
+ TaskEnable func(ctx context.Context, tt sealtasks.TaskType) error `perm:"admin"`
+
Remove func(ctx context.Context, sector abi.SectorID) error `perm:"admin"`
StorageAddLocal func(ctx context.Context, path string) error `perm:"admin"`
@@ -424,6 +432,7 @@ type GatewayStruct struct {
StateMarketBalance func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (api.MarketBalance, error)
StateMarketStorageDeal func(ctx context.Context, dealId abi.DealID, tsk types.TipSetKey) (*api.MarketDeal, error)
StateNetworkVersion func(ctx context.Context, tsk types.TipSetKey) (stnetwork.Version, error)
+ StateSectorGetInfo func(ctx context.Context, maddr address.Address, n abi.SectorNumber, tsk types.TipSetKey) (*miner.SectorOnChainInfo, error)
StateVerifiedClientStatus func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*abi.StoragePower, error)
StateWaitMsg func(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error)
}
@@ -495,6 +504,18 @@ func (c *CommonStruct) NetBandwidthStatsByProtocol(ctx context.Context) (map[pro
return c.Internal.NetBandwidthStatsByProtocol(ctx)
}
+func (c *CommonStruct) NetBlockAdd(ctx context.Context, acl api.NetBlockList) error {
+ return c.Internal.NetBlockAdd(ctx, acl)
+}
+
+func (c *CommonStruct) NetBlockRemove(ctx context.Context, acl api.NetBlockList) error {
+ return c.Internal.NetBlockRemove(ctx, acl)
+}
+
+func (c *CommonStruct) NetBlockList(ctx context.Context) (api.NetBlockList, error) {
+ return c.Internal.NetBlockList(ctx)
+}
+
func (c *CommonStruct) NetAgentVersion(ctx context.Context, p peer.ID) (string, error) {
return c.Internal.NetAgentVersion(ctx, p)
}
@@ -1495,6 +1516,10 @@ func (c *StorageMinerStruct) CreateBackup(ctx context.Context, fpath string) err
return c.Internal.CreateBackup(ctx, fpath)
}
+func (c *StorageMinerStruct) CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof, sectors []storage.SectorRef, expensive bool) (map[abi.SectorNumber]string, error) {
+ return c.Internal.CheckProvable(ctx, pp, sectors, expensive)
+}
+
// WorkerStruct
func (w *WorkerStruct) Version(ctx context.Context) (build.Version, error) {
@@ -1557,6 +1582,14 @@ func (w *WorkerStruct) Fetch(ctx context.Context, id storage.SectorRef, fileType
return w.Internal.Fetch(ctx, id, fileType, ptype, am)
}
+func (w *WorkerStruct) TaskDisable(ctx context.Context, tt sealtasks.TaskType) error {
+ return w.Internal.TaskDisable(ctx, tt)
+}
+
+func (w *WorkerStruct) TaskEnable(ctx context.Context, tt sealtasks.TaskType) error {
+ return w.Internal.TaskEnable(ctx, tt)
+}
+
func (w *WorkerStruct) Remove(ctx context.Context, sector abi.SectorID) error {
return w.Internal.Remove(ctx, sector)
}
@@ -1681,6 +1714,10 @@ func (g GatewayStruct) StateNetworkVersion(ctx context.Context, tsk types.TipSet
return g.Internal.StateNetworkVersion(ctx, tsk)
}
+func (g GatewayStruct) StateSectorGetInfo(ctx context.Context, maddr address.Address, n abi.SectorNumber, tsk types.TipSetKey) (*miner.SectorOnChainInfo, error) {
+ return g.Internal.StateSectorGetInfo(ctx, maddr, n, tsk)
+}
+
func (g GatewayStruct) StateVerifiedClientStatus(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*abi.StoragePower, error) {
return g.Internal.StateVerifiedClientStatus(ctx, addr, tsk)
}
diff --git a/api/docgen/docgen.go b/api/docgen/docgen.go
index bc29cf91d..074b6a787 100644
--- a/api/docgen/docgen.go
+++ b/api/docgen/docgen.go
@@ -234,6 +234,9 @@ func init() {
},
})
addExample(storiface.ErrorCode(0))
+ addExample(map[abi.SectorNumber]string{
+ 123: "can't acquire read lock",
+ })
// worker specific
addExample(storiface.AcquireMove)
@@ -241,6 +244,7 @@ func init() {
addExample(map[sealtasks.TaskType]struct{}{
sealtasks.TTPreCommit2: {},
})
+ addExample(sealtasks.TTCommit2)
}
func exampleValue(method string, t, parent reflect.Type) interface{} {
diff --git a/api/test/ccupgrade.go b/api/test/ccupgrade.go
index 4f6b39701..eedcec6ca 100644
--- a/api/test/ccupgrade.go
+++ b/api/test/ccupgrade.go
@@ -89,7 +89,7 @@ func testCCUpgrade(t *testing.T, b APIBuilder, blocktime time.Duration, upgradeH
t.Fatal(err)
}
- MakeDeal(t, ctx, 6, client, miner, false, false)
+ MakeDeal(t, ctx, 6, client, miner, false, false, 0)
// Validate upgrade
diff --git a/api/test/deals.go b/api/test/deals.go
index b81099d90..1189f070e 100644
--- a/api/test/deals.go
+++ b/api/test/deals.go
@@ -12,6 +12,8 @@ import (
"testing"
"time"
+ "github.com/filecoin-project/go-state-types/abi"
+
"github.com/stretchr/testify/require"
"github.com/ipfs/go-cid"
@@ -31,7 +33,7 @@ import (
ipld "github.com/ipfs/go-ipld-format"
)
-func TestDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration, carExport, fastRet bool) {
+func TestDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration, carExport, fastRet bool, startEpoch abi.ChainEpoch) {
ctx := context.Background()
n, sn := b(t, OneFull, OneMiner)
@@ -60,14 +62,14 @@ func TestDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration, carExport
}
}()
- MakeDeal(t, ctx, 6, client, miner, carExport, fastRet)
+ MakeDeal(t, ctx, 6, client, miner, carExport, fastRet, startEpoch)
atomic.AddInt64(&mine, -1)
fmt.Println("shutting down mining")
<-done
}
-func TestDoubleDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration) {
+func TestDoubleDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration, startEpoch abi.ChainEpoch) {
ctx := context.Background()
n, sn := b(t, OneFull, OneMiner)
@@ -97,15 +99,15 @@ func TestDoubleDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration) {
}
}()
- MakeDeal(t, ctx, 6, client, miner, false, false)
- MakeDeal(t, ctx, 7, client, miner, false, false)
+ MakeDeal(t, ctx, 6, client, miner, false, false, startEpoch)
+ MakeDeal(t, ctx, 7, client, miner, false, false, startEpoch)
atomic.AddInt64(&mine, -1)
fmt.Println("shutting down mining")
<-done
}
-func MakeDeal(t *testing.T, ctx context.Context, rseed int, client api.FullNode, miner TestStorageNode, carExport, fastRet bool) {
+func MakeDeal(t *testing.T, ctx context.Context, rseed int, client api.FullNode, miner TestStorageNode, carExport, fastRet bool, startEpoch abi.ChainEpoch) {
res, data, err := CreateClientFile(ctx, client, rseed)
if err != nil {
t.Fatal(err)
@@ -114,7 +116,7 @@ func MakeDeal(t *testing.T, ctx context.Context, rseed int, client api.FullNode,
fcid := res.Root
fmt.Println("FILE CID: ", fcid)
- deal := startDeal(t, ctx, miner, client, fcid, fastRet)
+ deal := startDeal(t, ctx, miner, client, fcid, fastRet, startEpoch)
// TODO: this sleep is only necessary because deals don't immediately get logged in the dealstore, we should fix this
time.Sleep(time.Second)
@@ -149,7 +151,7 @@ func CreateClientFile(ctx context.Context, client api.FullNode, rseed int) (*api
return res, data, nil
}
-func TestFastRetrievalDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration) {
+func TestFastRetrievalDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration, startEpoch abi.ChainEpoch) {
ctx := context.Background()
n, sn := b(t, OneFull, OneMiner)
@@ -189,7 +191,7 @@ func TestFastRetrievalDealFlow(t *testing.T, b APIBuilder, blocktime time.Durati
fmt.Println("FILE CID: ", fcid)
- deal := startDeal(t, ctx, miner, client, fcid, true)
+ deal := startDeal(t, ctx, miner, client, fcid, true, startEpoch)
waitDealPublished(t, ctx, miner, deal)
fmt.Println("deal published, retrieving")
@@ -203,7 +205,7 @@ func TestFastRetrievalDealFlow(t *testing.T, b APIBuilder, blocktime time.Durati
<-done
}
-func TestSenondDealRetrieval(t *testing.T, b APIBuilder, blocktime time.Duration) {
+func TestSecondDealRetrieval(t *testing.T, b APIBuilder, blocktime time.Duration) {
ctx := context.Background()
n, sn := b(t, OneFull, OneMiner)
@@ -252,13 +254,13 @@ func TestSenondDealRetrieval(t *testing.T, b APIBuilder, blocktime time.Duration
t.Fatal(err)
}
- deal1 := startDeal(t, ctx, miner, client, fcid1, true)
+ deal1 := startDeal(t, ctx, miner, client, fcid1, true, 0)
// TODO: this sleep is only necessary because deals don't immediately get logged in the dealstore, we should fix this
time.Sleep(time.Second)
waitDealSealed(t, ctx, miner, client, deal1, true)
- deal2 := startDeal(t, ctx, miner, client, fcid2, true)
+ deal2 := startDeal(t, ctx, miner, client, fcid2, true, 0)
time.Sleep(time.Second)
waitDealSealed(t, ctx, miner, client, deal2, false)
@@ -278,7 +280,7 @@ func TestSenondDealRetrieval(t *testing.T, b APIBuilder, blocktime time.Duration
<-done
}
-func startDeal(t *testing.T, ctx context.Context, miner TestStorageNode, client api.FullNode, fcid cid.Cid, fastRet bool) *cid.Cid {
+func startDeal(t *testing.T, ctx context.Context, miner TestStorageNode, client api.FullNode, fcid cid.Cid, fastRet bool, startEpoch abi.ChainEpoch) *cid.Cid {
maddr, err := miner.ActorAddress(ctx)
if err != nil {
t.Fatal(err)
@@ -296,6 +298,7 @@ func startDeal(t *testing.T, ctx context.Context, miner TestStorageNode, client
Wallet: addr,
Miner: maddr,
EpochPrice: types.NewInt(1000000),
+ DealStartEpoch: startEpoch,
MinBlocksDuration: uint64(build.MinDealDuration),
FastRetrieval: fastRet,
})
@@ -313,7 +316,7 @@ loop:
t.Fatal(err)
}
switch di.State {
- case storagemarket.StorageDealSealing:
+ case storagemarket.StorageDealAwaitingPreCommit, storagemarket.StorageDealSealing:
if noseal {
return
}
@@ -353,7 +356,7 @@ func waitDealPublished(t *testing.T, ctx context.Context, miner TestStorageNode,
t.Fatal("deal failed")
case storagemarket.StorageDealError:
t.Fatal("deal errored", di.Message)
- case storagemarket.StorageDealFinalizing, storagemarket.StorageDealSealing, storagemarket.StorageDealActive:
+ case storagemarket.StorageDealFinalizing, storagemarket.StorageDealAwaitingPreCommit, storagemarket.StorageDealSealing, storagemarket.StorageDealActive:
fmt.Println("COMPLETE", di)
return
}
diff --git a/api/test/mining.go b/api/test/mining.go
index 11953b95d..8f3689333 100644
--- a/api/test/mining.go
+++ b/api/test/mining.go
@@ -186,7 +186,7 @@ func TestDealMining(t *testing.T, b APIBuilder, blocktime time.Duration, carExpo
}
}()
- deal := startDeal(t, ctx, provider, client, fcid, false)
+ deal := startDeal(t, ctx, provider, client, fcid, false, 0)
// TODO: this sleep is only necessary because deals don't immediately get logged in the dealstore, we should fix this
time.Sleep(time.Second)
diff --git a/api/types.go b/api/types.go
index a69aa28d9..1318c7f43 100644
--- a/api/types.go
+++ b/api/types.go
@@ -107,3 +107,9 @@ func NewDataTransferChannel(hostID peer.ID, channelState datatransfer.ChannelSta
}
return channel
}
+
+type NetBlockList struct {
+ Peers []peer.ID
+ IPAddrs []string
+ IPSubnets []string
+}
diff --git a/build/version.go b/build/version.go
index 1a6a4e5b7..c3ed584b0 100644
--- a/build/version.go
+++ b/build/version.go
@@ -29,7 +29,7 @@ func buildType() string {
}
// BuildVersion is the local build version, set by build system
-const BuildVersion = "1.2.0"
+const BuildVersion = "1.2.1"
func UserVersion() string {
return BuildVersion + buildType() + CurrentCommit
diff --git a/chain/actors/policy/policy.go b/chain/actors/policy/policy.go
index a19a43aaa..31d83cd3d 100644
--- a/chain/actors/policy/policy.go
+++ b/chain/actors/policy/policy.go
@@ -28,7 +28,7 @@ const (
func SetSupportedProofTypes(types ...abi.RegisteredSealProof) {
miner0.SupportedProofTypes = make(map[abi.RegisteredSealProof]struct{}, len(types))
miner2.PreCommitSealProofTypesV0 = make(map[abi.RegisteredSealProof]struct{}, len(types))
- miner2.PreCommitSealProofTypesV7 = make(map[abi.RegisteredSealProof]struct{}, len(types))
+ miner2.PreCommitSealProofTypesV7 = make(map[abi.RegisteredSealProof]struct{}, len(types)*2)
miner2.PreCommitSealProofTypesV8 = make(map[abi.RegisteredSealProof]struct{}, len(types))
AddSupportedProofTypes(types...)
diff --git a/chain/events/events.go b/chain/events/events.go
index e35e91366..dcdf6c162 100644
--- a/chain/events/events.go
+++ b/chain/events/events.go
@@ -129,7 +129,7 @@ func (e *Events) listenHeadChangesOnce(ctx context.Context) error {
}
if err := e.tsc.add(cur[0].Val); err != nil {
- log.Warn("tsc.add: adding current tipset failed: %w", err)
+ log.Warnf("tsc.add: adding current tipset failed: %v", err)
}
e.readyOnce.Do(func() {
diff --git a/chain/exchange/peer_tracker.go b/chain/exchange/peer_tracker.go
index 902baadce..835a5b8a4 100644
--- a/chain/exchange/peer_tracker.go
+++ b/chain/exchange/peer_tracker.go
@@ -38,20 +38,26 @@ func newPeerTracker(lc fx.Lifecycle, h host.Host, pmgr *peermgr.PeerMgr) *bsPeer
pmgr: pmgr,
}
- sub, err := h.EventBus().Subscribe(new(peermgr.NewFilPeer))
+ evtSub, err := h.EventBus().Subscribe(new(peermgr.FilPeerEvt))
if err != nil {
panic(err)
}
go func() {
- for newPeer := range sub.Out() {
- bsPt.addPeer(newPeer.(peermgr.NewFilPeer).Id)
+ for evt := range evtSub.Out() {
+ pEvt := evt.(peermgr.FilPeerEvt)
+ switch pEvt.Type {
+ case peermgr.AddFilPeerEvt:
+ bsPt.addPeer(pEvt.ID)
+ case peermgr.RemoveFilPeerEvt:
+ bsPt.removePeer(pEvt.ID)
+ }
}
}()
lc.Append(fx.Hook{
OnStop: func(ctx context.Context) error {
- return sub.Close()
+ return evtSub.Close()
},
})
diff --git a/chain/gen/genesis/f01_init.go b/chain/gen/genesis/f01_init.go
index 667079a6d..24f06f2b6 100644
--- a/chain/gen/genesis/f01_init.go
+++ b/chain/gen/genesis/f01_init.go
@@ -21,7 +21,7 @@ import (
bstore "github.com/filecoin-project/lotus/lib/blockstore"
)
-func SetupInitActor(bs bstore.Blockstore, netname string, initialActors []genesis.Actor, rootVerifier genesis.Actor) (int64, *types.Actor, map[address.Address]address.Address, error) {
+func SetupInitActor(bs bstore.Blockstore, netname string, initialActors []genesis.Actor, rootVerifier genesis.Actor, remainder genesis.Actor) (int64, *types.Actor, map[address.Address]address.Address, error) {
if len(initialActors) > MaxAccounts {
return 0, nil, nil, xerrors.New("too many initial actors")
}
@@ -90,6 +90,33 @@ func SetupInitActor(bs bstore.Blockstore, netname string, initialActors []genesi
}
}
+ setupMsig := func(meta json.RawMessage) error {
+ var ainfo genesis.MultisigMeta
+ if err := json.Unmarshal(meta, &ainfo); err != nil {
+ return xerrors.Errorf("unmarshaling account meta: %w", err)
+ }
+ for _, e := range ainfo.Signers {
+ if _, ok := keyToId[e]; ok {
+ continue
+ }
+ fmt.Printf("init set %s t0%d\n", e, counter)
+
+ value := cbg.CborInt(counter)
+ if err := amap.Put(abi.AddrKey(e), &value); err != nil {
+ return err
+ }
+ counter = counter + 1
+ var err error
+ keyToId[e], err = address.NewIDAddress(uint64(value))
+ if err != nil {
+ return err
+ }
+
+ }
+
+ return nil
+ }
+
if rootVerifier.Type == genesis.TAccount {
var ainfo genesis.AccountMeta
if err := json.Unmarshal(rootVerifier.Meta, &ainfo); err != nil {
@@ -100,28 +127,15 @@ func SetupInitActor(bs bstore.Blockstore, netname string, initialActors []genesi
return 0, nil, nil, err
}
} else if rootVerifier.Type == genesis.TMultisig {
- var ainfo genesis.MultisigMeta
- if err := json.Unmarshal(rootVerifier.Meta, &ainfo); err != nil {
- return 0, nil, nil, xerrors.Errorf("unmarshaling account meta: %w", err)
+ err := setupMsig(rootVerifier.Meta)
+ if err != nil {
+ return 0, nil, nil, xerrors.Errorf("setting up root verifier msig: %w", err)
}
- for _, e := range ainfo.Signers {
- if _, ok := keyToId[e]; ok {
- continue
- }
- fmt.Printf("init set %s t0%d\n", e, counter)
+ }
- value := cbg.CborInt(counter)
- if err := amap.Put(abi.AddrKey(e), &value); err != nil {
- return 0, nil, nil, err
- }
- counter = counter + 1
- var err error
- keyToId[e], err = address.NewIDAddress(uint64(value))
- if err != nil {
- return 0, nil, nil, err
- }
-
- }
+ err := setupMsig(remainder.Meta)
+ if err != nil {
+ return 0, nil, nil, xerrors.Errorf("setting up remainder msig: %w", err)
}
amapaddr, err := amap.Root()
diff --git a/chain/gen/genesis/genesis.go b/chain/gen/genesis/genesis.go
index e441af7ae..ef81410bb 100644
--- a/chain/gen/genesis/genesis.go
+++ b/chain/gen/genesis/genesis.go
@@ -134,7 +134,7 @@ func MakeInitialStateTree(ctx context.Context, bs bstore.Blockstore, template ge
// Create init actor
- idStart, initact, keyIDs, err := SetupInitActor(bs, template.NetworkName, template.Accounts, template.VerifregRootKey)
+ idStart, initact, keyIDs, err := SetupInitActor(bs, template.NetworkName, template.Accounts, template.VerifregRootKey, template.RemainderAccount)
if err != nil {
return nil, nil, xerrors.Errorf("setup init actor: %w", err)
}
diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go
index a507b60cf..806882a15 100644
--- a/chain/messagepool/messagepool.go
+++ b/chain/messagepool/messagepool.go
@@ -240,10 +240,13 @@ func (ms *msgSet) add(m *types.SignedMessage, mp *MessagePool, strict, untrusted
// check if RBF passes
minPrice := ComputeMinRBF(exms.Message.GasPremium)
if types.BigCmp(m.Message.GasPremium, minPrice) >= 0 {
- log.Infow("add with RBF", "oldpremium", exms.Message.GasPremium,
+ log.Debugw("add with RBF", "oldpremium", exms.Message.GasPremium,
"newpremium", m.Message.GasPremium, "addr", m.Message.From, "nonce", m.Message.Nonce)
} else {
- log.Info("add with duplicate nonce")
+ log.Debugf("add with duplicate nonce. message from %s with nonce %d already in mpool,"+
+ " increase GasPremium to %s from %s to trigger replace by fee: %s",
+ m.Message.From, m.Message.Nonce, minPrice, m.Message.GasPremium,
+ ErrRBFTooLowPremium)
return false, xerrors.Errorf("message from %s with nonce %d already in mpool,"+
" increase GasPremium to %s from %s to trigger replace by fee: %w",
m.Message.From, m.Message.Nonce, minPrice, m.Message.GasPremium,
diff --git a/chain/stmgr/forks.go b/chain/stmgr/forks.go
index 97c4ce401..3bdf23f59 100644
--- a/chain/stmgr/forks.go
+++ b/chain/stmgr/forks.go
@@ -6,30 +6,13 @@ import (
"encoding/binary"
"math"
- "github.com/filecoin-project/specs-actors/v2/actors/migration/nv7"
-
- "github.com/filecoin-project/specs-actors/v2/actors/migration/nv4"
-
- "github.com/filecoin-project/lotus/chain/actors/builtin"
-
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-state-types/network"
- "github.com/ipfs/go-cid"
- cbor "github.com/ipfs/go-ipld-cbor"
- "golang.org/x/xerrors"
-
- builtin0 "github.com/filecoin-project/specs-actors/actors/builtin"
- miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
- multisig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig"
- power0 "github.com/filecoin-project/specs-actors/actors/builtin/power"
- adt0 "github.com/filecoin-project/specs-actors/actors/util/adt"
-
- "github.com/filecoin-project/specs-actors/actors/migration/nv3"
-
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors/adt"
+ "github.com/filecoin-project/lotus/chain/actors/builtin"
init_ "github.com/filecoin-project/lotus/chain/actors/builtin/init"
"github.com/filecoin-project/lotus/chain/actors/builtin/multisig"
"github.com/filecoin-project/lotus/chain/state"
@@ -38,6 +21,17 @@ import (
"github.com/filecoin-project/lotus/chain/vm"
bstore "github.com/filecoin-project/lotus/lib/blockstore"
"github.com/filecoin-project/lotus/lib/bufbstore"
+ builtin0 "github.com/filecoin-project/specs-actors/actors/builtin"
+ miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
+ multisig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig"
+ power0 "github.com/filecoin-project/specs-actors/actors/builtin/power"
+ "github.com/filecoin-project/specs-actors/actors/migration/nv3"
+ adt0 "github.com/filecoin-project/specs-actors/actors/util/adt"
+ "github.com/filecoin-project/specs-actors/v2/actors/migration/nv4"
+ "github.com/filecoin-project/specs-actors/v2/actors/migration/nv7"
+ "github.com/ipfs/go-cid"
+ cbor "github.com/ipfs/go-ipld-cbor"
+ "golang.org/x/xerrors"
)
// UpgradeFunc is a migration function run at every upgrade.
@@ -83,8 +77,9 @@ func DefaultUpgradeSchedule() UpgradeSchedule {
Expensive: true,
Migration: UpgradeActorsV2,
}, {
- Height: build.UpgradeTapeHeight,
- Network: network.Version5,
+ Height: build.UpgradeTapeHeight,
+ Network: network.Version5,
+ Migration: nil,
}, {
Height: build.UpgradeLiftoffHeight,
Network: network.Version5,
diff --git a/chain/stmgr/utils.go b/chain/stmgr/utils.go
index fb0b91378..1e29e72d8 100644
--- a/chain/stmgr/utils.go
+++ b/chain/stmgr/utils.go
@@ -300,7 +300,11 @@ func GetStorageDeal(ctx context.Context, sm *StateManager, dealID abi.DealID, ts
if err != nil {
return nil, err
} else if !found {
- return nil, xerrors.Errorf("deal %d not found", dealID)
+ return nil, xerrors.Errorf(
+ "deal %d not found "+
+ "- deal may not have completed sealing before deal proposal "+
+ "start epoch, or deal may have been slashed",
+ dealID)
}
states, err := state.States()
diff --git a/chain/store/store.go b/chain/store/store.go
index f4ce8112b..2ca09dfe9 100644
--- a/chain/store/store.go
+++ b/chain/store/store.go
@@ -726,12 +726,32 @@ func (cs *ChainStore) AddToTipSetTracker(b *types.BlockHeader) error {
log.Debug("tried to add block to tipset tracker that was already there")
return nil
}
+ h, err := cs.GetBlock(oc)
+ if err == nil && h != nil {
+ if h.Miner == b.Miner {
+ log.Warnf("Have multiple blocks from miner %s at height %d in our tipset cache %s-%s", b.Miner, b.Height, b.Cid(), h.Cid())
+ }
+ }
+ }
+ // This function is called 5 times per epoch on average
+ // It is also called with tipsets that are done with initial validation
+ // so they cannot be from the future.
+ // We are guaranteed not to use tipsets older than 900 epochs (fork limit)
+ // This means that we ideally want to keep only most recent 900 epochs in here
+ // Golang's map iteration starts at a random point in a map.
+ // With 5 tries per epoch, and 900 entries to keep, on average we will have
+ // ~136 garbage entires in the `cs.tipsets` map. (solve for 1-(1-x/(900+x))^5 == 0.5)
+ // Seems good enough to me
+
+ for height := range cs.tipsets {
+ if height < b.Height-build.Finality {
+ delete(cs.tipsets, height)
+ }
+ break
}
cs.tipsets[b.Height] = append(tss, b.Cid())
- // TODO: do we want to look for slashable submissions here? might as well...
-
return nil
}
@@ -797,7 +817,7 @@ func (cs *ChainStore) expandTipset(b *types.BlockHeader) (*types.TipSet, error)
return types.NewTipSet(all)
}
- inclMiners := map[address.Address]bool{b.Miner: true}
+ inclMiners := map[address.Address]cid.Cid{b.Miner: b.Cid()}
for _, bhc := range tsets {
if bhc == b.Cid() {
continue
@@ -808,14 +828,14 @@ func (cs *ChainStore) expandTipset(b *types.BlockHeader) (*types.TipSet, error)
return nil, xerrors.Errorf("failed to load block (%s) for tipset expansion: %w", bhc, err)
}
- if inclMiners[h.Miner] {
- log.Warnf("Have multiple blocks from miner %s at height %d in our tipset cache", h.Miner, h.Height)
+ if cid, found := inclMiners[h.Miner]; found {
+ log.Warnf("Have multiple blocks from miner %s at height %d in our tipset cache %s-%s", h.Miner, h.Height, h.Cid(), cid)
continue
}
if types.CidArrsEqual(h.Parents, b.Parents) {
all = append(all, h)
- inclMiners[h.Miner] = true
+ inclMiners[h.Miner] = bhc
}
}
diff --git a/chain/sub/incoming.go b/chain/sub/incoming.go
index dd8d1684b..f161bca57 100644
--- a/chain/sub/incoming.go
+++ b/chain/sub/incoming.go
@@ -6,9 +6,18 @@ import (
"fmt"
"time"
- "golang.org/x/xerrors"
-
address "github.com/filecoin-project/go-address"
+ "github.com/filecoin-project/lotus/build"
+ "github.com/filecoin-project/lotus/chain"
+ "github.com/filecoin-project/lotus/chain/messagepool"
+ "github.com/filecoin-project/lotus/chain/stmgr"
+ "github.com/filecoin-project/lotus/chain/store"
+ "github.com/filecoin-project/lotus/chain/types"
+ "github.com/filecoin-project/lotus/lib/blockstore"
+ "github.com/filecoin-project/lotus/lib/sigs"
+ "github.com/filecoin-project/lotus/metrics"
+ "github.com/filecoin-project/lotus/node/impl/client"
+ blockadt "github.com/filecoin-project/specs-actors/actors/util/adt"
lru "github.com/hashicorp/golang-lru"
blocks "github.com/ipfs/go-block-format"
bserv "github.com/ipfs/go-blockservice"
@@ -21,19 +30,7 @@ import (
cbg "github.com/whyrusleeping/cbor-gen"
"go.opencensus.io/stats"
"go.opencensus.io/tag"
-
- blockadt "github.com/filecoin-project/specs-actors/actors/util/adt"
-
- "github.com/filecoin-project/lotus/build"
- "github.com/filecoin-project/lotus/chain"
- "github.com/filecoin-project/lotus/chain/messagepool"
- "github.com/filecoin-project/lotus/chain/stmgr"
- "github.com/filecoin-project/lotus/chain/store"
- "github.com/filecoin-project/lotus/chain/types"
- "github.com/filecoin-project/lotus/lib/blockstore"
- "github.com/filecoin-project/lotus/lib/sigs"
- "github.com/filecoin-project/lotus/metrics"
- "github.com/filecoin-project/lotus/node/impl/client"
+ "golang.org/x/xerrors"
)
var log = logging.Logger("sub")
@@ -342,16 +339,14 @@ func (bv *BlockValidator) validateLocalBlock(ctx context.Context, msg *pubsub.Me
if size := msg.Size(); size > 1<<20-1<<15 {
log.Errorf("ignoring oversize block (%dB)", size)
- ctx, _ = tag.New(ctx, tag.Insert(metrics.FailureType, "oversize_block"))
- stats.Record(ctx, metrics.BlockValidationFailure.M(1))
+ recordFailure(ctx, metrics.BlockValidationFailure, "oversize_block")
return pubsub.ValidationIgnore
}
blk, what, err := bv.decodeAndCheckBlock(msg)
if err != nil {
log.Errorf("got invalid local block: %s", err)
- ctx, _ = tag.New(ctx, tag.Insert(metrics.FailureType, what))
- stats.Record(ctx, metrics.BlockValidationFailure.M(1))
+ recordFailure(ctx, metrics.BlockValidationFailure, what)
return pubsub.ValidationIgnore
}
diff --git a/chain/sync.go b/chain/sync.go
index d5c6f73fd..61c1d2094 100644
--- a/chain/sync.go
+++ b/chain/sync.go
@@ -1331,7 +1331,7 @@ loop:
continue
}
if !xerrors.Is(err, bstore.ErrNotFound) {
- log.Warn("loading local tipset: %s", err)
+ log.Warnf("loading local tipset: %s", err)
}
// NB: GetBlocks validates that the blocks are in-fact the ones we
diff --git a/chain/types/fil.go b/chain/types/fil.go
index 3dabb5e77..6742dd180 100644
--- a/chain/types/fil.go
+++ b/chain/types/fil.go
@@ -23,6 +23,29 @@ func (f FIL) Unitless() string {
return strings.TrimRight(strings.TrimRight(r.FloatString(18), "0"), ".")
}
+var unitPrefixes = []string{"a", "f", "p", "n", "μ", "m"}
+
+func (f FIL) Short() string {
+ n := BigInt(f)
+
+ dn := uint64(1)
+ var prefix string
+ for _, p := range unitPrefixes {
+ if n.LessThan(NewInt(dn * 1000)) {
+ prefix = p
+ break
+ }
+ dn *= 1000
+ }
+
+ r := new(big.Rat).SetFrac(f.Int, big.NewInt(int64(dn)))
+ if r.Sign() == 0 {
+ return "0"
+ }
+
+ return strings.TrimRight(strings.TrimRight(r.FloatString(3), "0"), ".") + " " + prefix + "FIL"
+}
+
func (f FIL) Format(s fmt.State, ch rune) {
switch ch {
case 's', 'v':
diff --git a/chain/types/fil_test.go b/chain/types/fil_test.go
new file mode 100644
index 000000000..6cbc44c5f
--- /dev/null
+++ b/chain/types/fil_test.go
@@ -0,0 +1,68 @@
+package types
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestFilShort(t *testing.T) {
+ for _, s := range []struct {
+ fil string
+ expect string
+ }{
+
+ {fil: "1", expect: "1 FIL"},
+ {fil: "1.1", expect: "1.1 FIL"},
+ {fil: "12", expect: "12 FIL"},
+ {fil: "123", expect: "123 FIL"},
+ {fil: "123456", expect: "123456 FIL"},
+ {fil: "123.23", expect: "123.23 FIL"},
+ {fil: "123456.234", expect: "123456.234 FIL"},
+ {fil: "123456.2341234", expect: "123456.234 FIL"},
+ {fil: "123456.234123445", expect: "123456.234 FIL"},
+
+ {fil: "0.1", expect: "100 mFIL"},
+ {fil: "0.01", expect: "10 mFIL"},
+ {fil: "0.001", expect: "1 mFIL"},
+
+ {fil: "0.0001", expect: "100 μFIL"},
+ {fil: "0.00001", expect: "10 μFIL"},
+ {fil: "0.000001", expect: "1 μFIL"},
+
+ {fil: "0.0000001", expect: "100 nFIL"},
+ {fil: "0.00000001", expect: "10 nFIL"},
+ {fil: "0.000000001", expect: "1 nFIL"},
+
+ {fil: "0.0000000001", expect: "100 pFIL"},
+ {fil: "0.00000000001", expect: "10 pFIL"},
+ {fil: "0.000000000001", expect: "1 pFIL"},
+
+ {fil: "0.0000000000001", expect: "100 fFIL"},
+ {fil: "0.00000000000001", expect: "10 fFIL"},
+ {fil: "0.000000000000001", expect: "1 fFIL"},
+
+ {fil: "0.0000000000000001", expect: "100 aFIL"},
+ {fil: "0.00000000000000001", expect: "10 aFIL"},
+ {fil: "0.000000000000000001", expect: "1 aFIL"},
+
+ {fil: "0.0000012", expect: "1.2 μFIL"},
+ {fil: "0.00000123", expect: "1.23 μFIL"},
+ {fil: "0.000001234", expect: "1.234 μFIL"},
+ {fil: "0.0000012344", expect: "1.234 μFIL"},
+ {fil: "0.00000123444", expect: "1.234 μFIL"},
+
+ {fil: "0.0002212", expect: "221.2 μFIL"},
+ {fil: "0.00022123", expect: "221.23 μFIL"},
+ {fil: "0.000221234", expect: "221.234 μFIL"},
+ {fil: "0.0002212344", expect: "221.234 μFIL"},
+ {fil: "0.00022123444", expect: "221.234 μFIL"},
+ } {
+ s := s
+ t.Run(s.fil, func(t *testing.T) {
+ f, err := ParseFIL(s.fil)
+ require.NoError(t, err)
+ require.Equal(t, s.expect, f.Short())
+ })
+ }
+}
diff --git a/chain/vm/invoker.go b/chain/vm/invoker.go
index e22d69653..a5610736a 100644
--- a/chain/vm/invoker.go
+++ b/chain/vm/invoker.go
@@ -36,9 +36,9 @@ type ActorPredicate func(vmr.Runtime, rtt.VMActor) error
func ActorsVersionPredicate(ver actors.Version) ActorPredicate {
return func(rt vmr.Runtime, v rtt.VMActor) error {
- nver := actors.VersionForNetwork(rt.NetworkVersion())
- if nver != ver {
- return xerrors.Errorf("actor %s is a version %d actor; chain only supports actor version %d at height %d", v.Code(), ver, nver, rt.CurrEpoch())
+ aver := actors.VersionForNetwork(rt.NetworkVersion())
+ if aver != ver {
+ return xerrors.Errorf("actor %s is a version %d actor; chain only supports actor version %d at height %d and nver %d", v.Code(), ver, aver, rt.CurrEpoch(), rt.NetworkVersion())
}
return nil
}
diff --git a/chain/vm/runtime.go b/chain/vm/runtime.go
index a7e666d2f..cdb1720de 100644
--- a/chain/vm/runtime.go
+++ b/chain/vm/runtime.go
@@ -5,6 +5,7 @@ import (
"context"
"encoding/binary"
"fmt"
+ gruntime "runtime"
"time"
"github.com/filecoin-project/go-address"
@@ -534,7 +535,7 @@ func (rt *Runtime) chargeGasInternal(gas GasCharge, skip int) aerrors.ActorError
if EnableGasTracing {
var callers [10]uintptr
- cout := 0 //gruntime.Callers(2+skip, callers[:])
+ cout := gruntime.Callers(2+skip, callers[:])
now := build.Clock.Now()
if rt.lastGasCharge != nil {
diff --git a/chain/vm/syscalls.go b/chain/vm/syscalls.go
index bf879f8fc..ba6ee2f1d 100644
--- a/chain/vm/syscalls.go
+++ b/chain/vm/syscalls.go
@@ -7,29 +7,26 @@ import (
goruntime "runtime"
"sync"
- "github.com/filecoin-project/go-state-types/network"
-
- "github.com/filecoin-project/lotus/chain/actors/policy"
-
- "github.com/filecoin-project/go-address"
"github.com/ipfs/go-cid"
cbor "github.com/ipfs/go-ipld-cbor"
"github.com/minio/blake2b-simd"
mh "github.com/multiformats/go-multihash"
"golang.org/x/xerrors"
+ "github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/crypto"
+ "github.com/filecoin-project/go-state-types/network"
"github.com/filecoin-project/lotus/chain/actors/adt"
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
+ "github.com/filecoin-project/lotus/chain/actors/policy"
"github.com/filecoin-project/lotus/chain/state"
"github.com/filecoin-project/lotus/chain/types"
+ "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper"
"github.com/filecoin-project/lotus/lib/sigs"
runtime2 "github.com/filecoin-project/specs-actors/v2/actors/runtime"
proof2 "github.com/filecoin-project/specs-actors/v2/actors/runtime/proof"
-
- "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper"
)
func init() {
@@ -306,7 +303,7 @@ func (ss *syscallShim) BatchVerifySeals(inp map[address.Address][]proof2.SealVer
sema <- struct{}{}
if err := ss.VerifySeal(svi); err != nil {
- log.Warnw("seal verify in batch failed", "miner", ma, "index", ix, "err", err)
+ log.Warnw("seal verify in batch failed", "miner", ma, "sectorNumber", svi.SectorID.Number, "err", err)
res[ix] = false
} else {
res[ix] = true
diff --git a/chain/wallet/wallet.go b/chain/wallet/wallet.go
index 46ff92861..884557520 100644
--- a/chain/wallet/wallet.go
+++ b/chain/wallet/wallet.go
@@ -6,18 +6,16 @@ import (
"strings"
"sync"
+ "github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/crypto"
logging "github.com/ipfs/go-log/v2"
"golang.org/x/xerrors"
- "github.com/filecoin-project/go-address"
-
"github.com/filecoin-project/lotus/api"
- _ "github.com/filecoin-project/lotus/lib/sigs/bls" // enable bls signatures
- _ "github.com/filecoin-project/lotus/lib/sigs/secp" // enable secp signatures
-
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/lib/sigs"
+ _ "github.com/filecoin-project/lotus/lib/sigs/bls" // enable bls signatures
+ _ "github.com/filecoin-project/lotus/lib/sigs/secp" // enable secp signatures
)
var log = logging.Logger("wallet")
@@ -270,7 +268,7 @@ func (w *LocalWallet) WalletHas(ctx context.Context, addr address.Address) (bool
return k != nil, nil
}
-func (w *LocalWallet) WalletDelete(ctx context.Context, addr address.Address) error {
+func (w *LocalWallet) walletDelete(ctx context.Context, addr address.Address) error {
k, err := w.findKey(addr)
if err != nil {
@@ -305,18 +303,29 @@ func (w *LocalWallet) WalletDelete(ctx context.Context, addr address.Address) er
delete(w.keys, addr)
- def, err := w.GetDefault()
- if err != nil {
- return xerrors.Errorf("getting default address: %w", err)
- }
+ return nil
+}
- if def == addr {
- err = w.SetDefault(address.Undef)
- if err != nil {
- return xerrors.Errorf("unsetting default address: %w", err)
+func (w *LocalWallet) deleteDefault() {
+ w.lk.Lock()
+ defer w.lk.Unlock()
+ if err := w.keystore.Delete(KDefault); err != nil {
+ if !xerrors.Is(err, types.ErrKeyInfoNotFound) {
+ log.Warnf("failed to unregister current default key: %s", err)
}
}
+}
+func (w *LocalWallet) WalletDelete(ctx context.Context, addr address.Address) error {
+ if err := w.walletDelete(ctx, addr); err != nil {
+ return xerrors.Errorf("wallet delete: %w", err)
+ }
+
+ if def, err := w.GetDefault(); err == nil {
+ if def == addr {
+ w.deleteDefault()
+ }
+ }
return nil
}
diff --git a/cli/chain.go b/cli/chain.go
index 0fa999d17..643de8f5b 100644
--- a/cli/chain.go
+++ b/cli/chain.go
@@ -466,6 +466,9 @@ var chainInspectUsage = &cli.Command{
code, err := lookupActorCode(m.Message.To)
if err != nil {
+ if strings.Contains(err.Error(), types.ErrActorNotFound.Error()) {
+ continue
+ }
return err
}
diff --git a/cli/client.go b/cli/client.go
index 20a7231da..c3cde8bc3 100644
--- a/cli/client.go
+++ b/cli/client.go
@@ -326,9 +326,9 @@ var clientDealCmd = &cli.Command{
Value: true,
},
&cli.BoolFlag{
- Name: "verified-deal",
- Usage: "indicate that the deal counts towards verified client total",
- Value: false,
+ Name: "verified-deal",
+ Usage: "indicate that the deal counts towards verified client total",
+ DefaultText: "true if client is verified, false otherwise",
},
&cli.StringFlag{
Name: "provider-collateral",
@@ -655,19 +655,19 @@ uiLoop:
state = "find"
}
case "find":
- asks, err := getAsks(ctx, api)
+ asks, err := GetAsks(ctx, api)
if err != nil {
return err
}
for _, ask := range asks {
- if ask.MinPieceSize > ds.PieceSize {
+ if ask.Ask.MinPieceSize > ds.PieceSize {
continue
}
- if ask.MaxPieceSize < ds.PieceSize {
+ if ask.Ask.MaxPieceSize < ds.PieceSize {
continue
}
- candidateAsks = append(candidateAsks, ask)
+ candidateAsks = append(candidateAsks, ask.Ask)
}
afmt.Printf("Found %d candidate asks\n", len(candidateAsks))
@@ -1191,6 +1191,11 @@ var clientDealStatsCmd = &cli.Command{
var clientListAsksCmd = &cli.Command{
Name: "list-asks",
Usage: "List asks for top miners",
+ Flags: []cli.Flag{
+ &cli.BoolFlag{
+ Name: "by-ping",
+ },
+ },
Action: func(cctx *cli.Context) error {
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
@@ -1199,17 +1204,26 @@ var clientListAsksCmd = &cli.Command{
defer closer()
ctx := ReqContext(cctx)
- asks, err := getAsks(ctx, api)
+ asks, err := GetAsks(ctx, api)
if err != nil {
return err
}
- for _, ask := range asks {
- fmt.Printf("%s: min:%s max:%s price:%s/GiB/Epoch verifiedPrice:%s/GiB/Epoch\n", ask.Miner,
+ if cctx.Bool("by-ping") {
+ sort.Slice(asks, func(i, j int) bool {
+ return asks[i].Ping < asks[j].Ping
+ })
+ }
+
+ for _, a := range asks {
+ ask := a.Ask
+
+ fmt.Printf("%s: min:%s max:%s price:%s/GiB/Epoch verifiedPrice:%s/GiB/Epoch ping:%s\n", ask.Miner,
types.SizeStr(types.NewInt(uint64(ask.MinPieceSize))),
types.SizeStr(types.NewInt(uint64(ask.MaxPieceSize))),
types.FIL(ask.Price),
types.FIL(ask.VerifiedPrice),
+ a.Ping,
)
}
@@ -1217,7 +1231,12 @@ var clientListAsksCmd = &cli.Command{
},
}
-func getAsks(ctx context.Context, api lapi.FullNode) ([]*storagemarket.StorageAsk, error) {
+type QueriedAsk struct {
+ Ask *storagemarket.StorageAsk
+ Ping time.Duration
+}
+
+func GetAsks(ctx context.Context, api lapi.FullNode) ([]QueriedAsk, error) {
color.Blue(".. getting miner list")
miners, err := api.StateListMiners(ctx, types.EmptyTSK)
if err != nil {
@@ -1272,7 +1291,7 @@ loop:
color.Blue(".. querying asks")
- var asks []*storagemarket.StorageAsk
+ var asks []QueriedAsk
var queried, got int64
done = make(chan struct{})
@@ -1308,9 +1327,19 @@ loop:
return
}
+ rt := time.Now()
+
+ _, err = api.ClientQueryAsk(ctx, *mi.PeerId, miner)
+ if err != nil {
+ return
+ }
+
atomic.AddInt64(&got, 1)
lk.Lock()
- asks = append(asks, ask)
+ asks = append(asks, QueriedAsk{
+ Ask: ask,
+ Ping: time.Now().Sub(rt),
+ })
lk.Unlock()
}(miner)
}
@@ -1328,7 +1357,7 @@ loop2:
fmt.Printf("\r* Queried %d asks, got %d responses\n", atomic.LoadInt64(&queried), atomic.LoadInt64(&got))
sort.Slice(asks, func(i, j int) bool {
- return asks[i].Price.LessThan(asks[j].Price)
+ return asks[i].Ask.Price.LessThan(asks[j].Ask.Price)
})
return asks, nil
@@ -1825,6 +1854,11 @@ var clientCancelTransfer = &cli.Command{
Usage: "specify only transfers where peer is/is not initiator",
Value: true,
},
+ &cli.DurationFlag{
+ Name: "cancel-timeout",
+ Usage: "time to wait for cancel to be sent to storage provider",
+ Value: 5 * time.Second,
+ },
},
Action: func(cctx *cli.Context) error {
if !cctx.Args().Present() {
@@ -1868,7 +1902,9 @@ var clientCancelTransfer = &cli.Command{
}
}
- return api.ClientCancelDataTransfer(ctx, transferID, other, initiator)
+ timeoutCtx, cancel := context.WithTimeout(ctx, cctx.Duration("cancel-timeout"))
+ defer cancel()
+ return api.ClientCancelDataTransfer(timeoutCtx, transferID, other, initiator)
},
}
diff --git a/cli/mpool.go b/cli/mpool.go
index 4979f6ddc..fe36c0c7d 100644
--- a/cli/mpool.go
+++ b/cli/mpool.go
@@ -233,6 +233,7 @@ var mpoolStat = &cli.Command{
addr string
past, cur, future uint64
belowCurr, belowPast uint64
+ gasLimit big.Int
}
buckets := map[address.Address]*statBucket{}
@@ -274,6 +275,7 @@ var mpoolStat = &cli.Command{
var s mpStat
s.addr = a.String()
+ s.gasLimit = big.Zero()
for _, m := range bkt.msgs {
if m.Message.Nonce < act.Nonce {
@@ -290,6 +292,8 @@ var mpoolStat = &cli.Command{
if m.Message.GasFeeCap.LessThan(minBF) {
s.belowPast++
}
+
+ s.gasLimit = big.Add(s.gasLimit, types.NewInt(uint64(m.Message.GasLimit)))
}
out = append(out, s)
@@ -300,6 +304,7 @@ var mpoolStat = &cli.Command{
})
var total mpStat
+ total.gasLimit = big.Zero()
for _, stat := range out {
total.past += stat.past
@@ -307,12 +312,13 @@ var mpoolStat = &cli.Command{
total.future += stat.future
total.belowCurr += stat.belowCurr
total.belowPast += stat.belowPast
+ total.gasLimit = big.Add(total.gasLimit, stat.gasLimit)
- fmt.Printf("%s: Nonce past: %d, cur: %d, future: %d; FeeCap cur: %d, min-%d: %d \n", stat.addr, stat.past, stat.cur, stat.future, stat.belowCurr, cctx.Int("basefee-lookback"), stat.belowPast)
+ fmt.Printf("%s: Nonce past: %d, cur: %d, future: %d; FeeCap cur: %d, min-%d: %d, gasLimit: %s\n", stat.addr, stat.past, stat.cur, stat.future, stat.belowCurr, cctx.Int("basefee-lookback"), stat.belowPast, stat.gasLimit)
}
fmt.Println("-----")
- fmt.Printf("total: Nonce past: %d, cur: %d, future: %d; FeeCap cur: %d, min-%d: %d \n", total.past, total.cur, total.future, total.belowCurr, cctx.Int("basefee-lookback"), total.belowPast)
+ fmt.Printf("total: Nonce past: %d, cur: %d, future: %d; FeeCap cur: %d, min-%d: %d, gasLimit: %s\n", total.past, total.cur, total.future, total.belowCurr, cctx.Int("basefee-lookback"), total.belowPast, total.gasLimit)
return nil
},
@@ -442,7 +448,9 @@ var mpoolReplaceCmd = &cli.Command{
messagepool.CapGasFee(mff, &msg, mss.Get().MaxFee)
} else {
- msg.GasLimit = cctx.Int64("gas-limit")
+ if cctx.IsSet("gas-limit") {
+ msg.GasLimit = cctx.Int64("gas-limit")
+ }
msg.GasPremium, err = types.BigFromString(cctx.String("gas-premium"))
if err != nil {
return fmt.Errorf("parsing gas-premium: %w", err)
diff --git a/cli/net.go b/cli/net.go
index 9c40c70c7..56f0bf5f9 100644
--- a/cli/net.go
+++ b/cli/net.go
@@ -18,6 +18,7 @@ import (
"github.com/filecoin-project/go-address"
+ atypes "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/lib/addrutil"
)
@@ -34,6 +35,7 @@ var netCmd = &cli.Command{
netScores,
NetReachability,
NetBandwidthCmd,
+ NetBlockCmd,
},
}
@@ -375,3 +377,202 @@ var NetBandwidthCmd = &cli.Command{
},
}
+
+var NetBlockCmd = &cli.Command{
+ Name: "block",
+ Usage: "Manage network connection gating rules",
+ Subcommands: []*cli.Command{
+ NetBlockAddCmd,
+ NetBlockRemoveCmd,
+ NetBlockListCmd,
+ },
+}
+
+var NetBlockAddCmd = &cli.Command{
+ Name: "add",
+ Usage: "Add connection gating rules",
+ Subcommands: []*cli.Command{
+ NetBlockAddPeer,
+ NetBlockAddIP,
+ NetBlockAddSubnet,
+ },
+}
+
+var NetBlockAddPeer = &cli.Command{
+ Name: "peer",
+ Usage: "Block a peer",
+ ArgsUsage: " ...",
+ Action: func(cctx *cli.Context) error {
+ api, closer, err := GetAPI(cctx)
+ if err != nil {
+ return err
+ }
+ defer closer()
+ ctx := ReqContext(cctx)
+
+ var peers []peer.ID
+ for _, s := range cctx.Args().Slice() {
+ p, err := peer.Decode(s)
+ if err != nil {
+ return err
+ }
+
+ peers = append(peers, p)
+ }
+
+ return api.NetBlockAdd(ctx, atypes.NetBlockList{Peers: peers})
+ },
+}
+
+var NetBlockAddIP = &cli.Command{
+ Name: "ip",
+ Usage: "Block an IP address",
+ ArgsUsage: " ...",
+ Action: func(cctx *cli.Context) error {
+ api, closer, err := GetAPI(cctx)
+ if err != nil {
+ return err
+ }
+ defer closer()
+ ctx := ReqContext(cctx)
+
+ return api.NetBlockAdd(ctx, atypes.NetBlockList{IPAddrs: cctx.Args().Slice()})
+ },
+}
+
+var NetBlockAddSubnet = &cli.Command{
+ Name: "subnet",
+ Usage: "Block an IP subnet",
+ ArgsUsage: " ...",
+ Action: func(cctx *cli.Context) error {
+ api, closer, err := GetAPI(cctx)
+ if err != nil {
+ return err
+ }
+ defer closer()
+ ctx := ReqContext(cctx)
+
+ return api.NetBlockAdd(ctx, atypes.NetBlockList{IPSubnets: cctx.Args().Slice()})
+ },
+}
+
+var NetBlockRemoveCmd = &cli.Command{
+ Name: "remove",
+ Usage: "Remove connection gating rules",
+ Subcommands: []*cli.Command{
+ NetBlockRemovePeer,
+ NetBlockRemoveIP,
+ NetBlockRemoveSubnet,
+ },
+}
+
+var NetBlockRemovePeer = &cli.Command{
+ Name: "peer",
+ Usage: "Unblock a peer",
+ ArgsUsage: " ...",
+ Action: func(cctx *cli.Context) error {
+ api, closer, err := GetAPI(cctx)
+ if err != nil {
+ return err
+ }
+ defer closer()
+ ctx := ReqContext(cctx)
+
+ var peers []peer.ID
+ for _, s := range cctx.Args().Slice() {
+ p, err := peer.Decode(s)
+ if err != nil {
+ return err
+ }
+
+ peers = append(peers, p)
+ }
+
+ return api.NetBlockRemove(ctx, atypes.NetBlockList{Peers: peers})
+ },
+}
+
+var NetBlockRemoveIP = &cli.Command{
+ Name: "ip",
+ Usage: "Unblock an IP address",
+ ArgsUsage: " ...",
+ Action: func(cctx *cli.Context) error {
+ api, closer, err := GetAPI(cctx)
+ if err != nil {
+ return err
+ }
+ defer closer()
+ ctx := ReqContext(cctx)
+
+ return api.NetBlockRemove(ctx, atypes.NetBlockList{IPAddrs: cctx.Args().Slice()})
+ },
+}
+
+var NetBlockRemoveSubnet = &cli.Command{
+ Name: "subnet",
+ Usage: "Unblock an IP subnet",
+ ArgsUsage: " ...",
+ Action: func(cctx *cli.Context) error {
+ api, closer, err := GetAPI(cctx)
+ if err != nil {
+ return err
+ }
+ defer closer()
+ ctx := ReqContext(cctx)
+
+ return api.NetBlockRemove(ctx, atypes.NetBlockList{IPSubnets: cctx.Args().Slice()})
+ },
+}
+
+var NetBlockListCmd = &cli.Command{
+ Name: "list",
+ Usage: "list connection gating rules",
+ Action: func(cctx *cli.Context) error {
+ api, closer, err := GetAPI(cctx)
+ if err != nil {
+ return err
+ }
+ defer closer()
+ ctx := ReqContext(cctx)
+
+ acl, err := api.NetBlockList(ctx)
+ if err != nil {
+ return err
+ }
+
+ if len(acl.Peers) != 0 {
+ sort.Slice(acl.Peers, func(i, j int) bool {
+ return strings.Compare(string(acl.Peers[i]), string(acl.Peers[j])) > 0
+ })
+
+ fmt.Println("Blocked Peers:")
+ for _, p := range acl.Peers {
+ fmt.Printf("\t%s\n", p)
+ }
+ }
+
+ if len(acl.IPAddrs) != 0 {
+ sort.Slice(acl.IPAddrs, func(i, j int) bool {
+ return strings.Compare(acl.IPAddrs[i], acl.IPAddrs[j]) < 0
+ })
+
+ fmt.Println("Blocked IPs:")
+ for _, a := range acl.IPAddrs {
+ fmt.Printf("\t%s\n", a)
+ }
+ }
+
+ if len(acl.IPSubnets) != 0 {
+ sort.Slice(acl.IPSubnets, func(i, j int) bool {
+ return strings.Compare(acl.IPSubnets[i], acl.IPSubnets[j]) < 0
+ })
+
+ fmt.Println("Blocked Subnets:")
+ for _, n := range acl.IPSubnets {
+ fmt.Printf("\t%s\n", n)
+ }
+ }
+
+ return nil
+ },
+}
diff --git a/cli/state.go b/cli/state.go
index 31537cc89..bef864dd2 100644
--- a/cli/state.go
+++ b/cli/state.go
@@ -72,6 +72,7 @@ var stateCmd = &cli.Command{
stateMinerInfo,
stateMarketCmd,
stateExecTraceCmd,
+ stateNtwkVersionCmd,
},
}
@@ -127,6 +128,7 @@ var stateMinerInfo = &cli.Command{
}
fmt.Printf("%s ", a)
}
+ fmt.Println()
fmt.Printf("Consensus Fault End:\t%d\n", mi.ConsensusFaultElapsed)
fmt.Printf("SectorSize:\t%s (%d)\n", types.SizeStr(types.NewInt(uint64(mi.SectorSize))), mi.SectorSize)
@@ -1758,6 +1760,9 @@ var stateSectorCmd = &cli.Command{
if err != nil {
return err
}
+ if si == nil {
+ return xerrors.Errorf("sector %d for miner %s not found", sid, maddr)
+ }
fmt.Println("SectorNumber: ", si.SectorNumber)
fmt.Println("SealProof: ", si.SealProof)
@@ -1831,3 +1836,35 @@ var stateMarketBalanceCmd = &cli.Command{
return nil
},
}
+
+var stateNtwkVersionCmd = &cli.Command{
+ Name: "network-version",
+ Usage: "Returns the network version",
+ Action: func(cctx *cli.Context) error {
+ if cctx.Args().Present() {
+ return ShowHelp(cctx, fmt.Errorf("doesn't expect any arguments"))
+ }
+
+ api, closer, err := GetFullNodeAPI(cctx)
+ if err != nil {
+ return err
+ }
+ defer closer()
+
+ ctx := ReqContext(cctx)
+
+ ts, err := LoadTipSet(ctx, cctx, api)
+ if err != nil {
+ return err
+ }
+
+ nv, err := api.StateNetworkVersion(ctx, ts.Key())
+ if err != nil {
+ return err
+ }
+
+ fmt.Printf("Network Version: %d\n", nv)
+
+ return nil
+ },
+}
diff --git a/cli/test/client.go b/cli/test/client.go
index 95abd39c2..4a49f732a 100644
--- a/cli/test/client.go
+++ b/cli/test/client.go
@@ -43,13 +43,14 @@ func RunClientTest(t *testing.T, cmds []*lcli.Command, clientNode test.TestNode)
require.Regexp(t, regexp.MustCompile("Ask:"), out)
// Create a deal (non-interactive)
- // client deal 1000000attofil
+ // client deal --start-epoch= 1000000attofil
res, _, err := test.CreateClientFile(ctx, clientNode, 1)
require.NoError(t, err)
+ startEpoch := fmt.Sprintf("--start-epoch=%d", 2<<12)
dataCid := res.Root
price := "1000000attofil"
duration := fmt.Sprintf("%d", build.MinDealDuration)
- out = clientCLI.RunCmd("client", "deal", dataCid.String(), minerAddr.String(), price, duration)
+ out = clientCLI.RunCmd("client", "deal", startEpoch, dataCid.String(), minerAddr.String(), price, duration)
fmt.Println("client deal", out)
// Create a deal (interactive)
@@ -82,7 +83,7 @@ func RunClientTest(t *testing.T, cmds []*lcli.Command, clientNode test.TestNode)
fmt.Println("list-deals:\n", out)
lines := strings.Split(out, "\n")
- require.Len(t, lines, 2)
+ require.GreaterOrEqual(t, len(lines), 2)
re := regexp.MustCompile(`\s+`)
parts := re.Split(lines[1], -1)
if len(parts) < 4 {
@@ -111,7 +112,7 @@ func dealComplete(t *testing.T, dealStatus string) bool {
switch dealStatus {
case "StorageDealFailing", "StorageDealError":
t.Fatal(xerrors.Errorf("Storage deal failed with status: " + dealStatus))
- case "StorageDealStaged", "StorageDealSealing", "StorageDealActive", "StorageDealExpired", "StorageDealSlashed":
+ case "StorageDealStaged", "StorageDealAwaitingPreCommit", "StorageDealSealing", "StorageDealActive", "StorageDealExpired", "StorageDealSlashed":
return true
}
diff --git a/cmd/lotus-bench/import.go b/cmd/lotus-bench/import.go
index f8752fc99..9fa6731aa 100644
--- a/cmd/lotus-bench/import.go
+++ b/cmd/lotus-bench/import.go
@@ -60,7 +60,7 @@ type TipSetExec struct {
var importBenchCmd = &cli.Command{
Name: "import",
- Usage: "benchmark chain import and validation",
+ Usage: "Benchmark chain import and validation",
Subcommands: []*cli.Command{
importAnalyzeCmd,
},
diff --git a/cmd/lotus-bench/main.go b/cmd/lotus-bench/main.go
index f502e429c..b246aedbb 100644
--- a/cmd/lotus-bench/main.go
+++ b/cmd/lotus-bench/main.go
@@ -40,8 +40,10 @@ import (
var log = logging.Logger("lotus-bench")
type BenchResults struct {
- SectorSize abi.SectorSize
+ SectorSize abi.SectorSize
+ SectorNumber int
+ SealingSum SealingResult
SealingResults []SealingResult
PostGenerateCandidates time.Duration
@@ -56,6 +58,26 @@ type BenchResults struct {
VerifyWindowPostHot time.Duration
}
+func (bo *BenchResults) SumSealingTime() error {
+ if len(bo.SealingResults) <= 0 {
+ return xerrors.Errorf("BenchResults SealingResults len <= 0")
+ }
+ if len(bo.SealingResults) != bo.SectorNumber {
+ return xerrors.Errorf("BenchResults SealingResults len(%d) != bo.SectorNumber(%d)", len(bo.SealingResults), bo.SectorNumber)
+ }
+
+ for _, sealing := range bo.SealingResults {
+ bo.SealingSum.AddPiece += sealing.AddPiece
+ bo.SealingSum.PreCommit1 += sealing.PreCommit1
+ bo.SealingSum.PreCommit2 += sealing.PreCommit2
+ bo.SealingSum.Commit1 += sealing.Commit1
+ bo.SealingSum.Commit2 += sealing.Commit2
+ bo.SealingSum.Verify += sealing.Verify
+ bo.SealingSum.Unseal += sealing.Unseal
+ }
+ return nil
+}
+
type SealingResult struct {
AddPiece time.Duration
PreCommit1 time.Duration
@@ -95,12 +117,13 @@ func main() {
}
var sealBenchCmd = &cli.Command{
- Name: "sealing",
+ Name: "sealing",
+ Usage: "Benchmark seal and winning post and window post",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "storage-dir",
Value: "~/.lotus-bench",
- Usage: "Path to the storage directory that will store sectors long term",
+ Usage: "path to the storage directory that will store sectors long term",
},
&cli.StringFlag{
Name: "sector-size",
@@ -132,16 +155,22 @@ var sealBenchCmd = &cli.Command{
Name: "skip-unseal",
Usage: "skip the unseal portion of the benchmark",
},
+ &cli.StringFlag{
+ Name: "ticket-preimage",
+ Usage: "ticket random",
+ },
&cli.StringFlag{
Name: "save-commit2-input",
- Usage: "Save commit2 input to a file",
+ Usage: "save commit2 input to a file",
},
&cli.IntFlag{
Name: "num-sectors",
+ Usage: "select number of sectors to seal",
Value: 1,
},
&cli.IntFlag{
Name: "parallel",
+ Usage: "num run in parallel",
Value: 1,
},
},
@@ -213,7 +242,8 @@ var sealBenchCmd = &cli.Command{
sectorSize := abi.SectorSize(sectorSizeInt)
// Only fetch parameters if actually needed
- if !c.Bool("skip-commit2") {
+ skipc2 := c.Bool("skip-commit2")
+ if !skipc2 {
if err := paramfetch.GetParams(lcli.ReqContext(c), build.ParametersJSON(), uint64(sectorSize)); err != nil {
return xerrors.Errorf("getting params: %w", err)
}
@@ -228,6 +258,8 @@ var sealBenchCmd = &cli.Command{
return err
}
+ sectorNumber := c.Int("num-sectors")
+
var sealTimings []SealingResult
var sealedSectors []saproof2.SectorInfo
@@ -238,18 +270,11 @@ var sealBenchCmd = &cli.Command{
PreCommit2: 1,
Commit: 1,
}
- sealTimings, sealedSectors, err = runSeals(sb, sbfs, c.Int("num-sectors"), parCfg, mid, sectorSize, []byte(c.String("ticket-preimage")), c.String("save-commit2-input"), c.Bool("skip-commit2"), c.Bool("skip-unseal"))
+ sealTimings, sealedSectors, err = runSeals(sb, sbfs, sectorNumber, parCfg, mid, sectorSize, []byte(c.String("ticket-preimage")), c.String("save-commit2-input"), skipc2, c.Bool("skip-unseal"))
if err != nil {
return xerrors.Errorf("failed to run seals: %w", err)
}
- }
-
- beforePost := time.Now()
-
- var challenge [32]byte
- rand.Read(challenge[:])
-
- if robench != "" {
+ } else {
// TODO: implement sbfs.List() and use that for all cases (preexisting sectorbuilder or not)
// TODO: this assumes we only ever benchmark a preseal
@@ -282,10 +307,19 @@ var sealBenchCmd = &cli.Command{
bo := BenchResults{
SectorSize: sectorSize,
+ SectorNumber: sectorNumber,
SealingResults: sealTimings,
}
+ if err := bo.SumSealingTime(); err != nil {
+ return err
+ }
- if !c.Bool("skip-commit2") {
+ var challenge [32]byte
+ rand.Read(challenge[:])
+
+ beforePost := time.Now()
+
+ if !skipc2 {
log.Info("generating winning post candidates")
wipt, err := spt(sectorSize).RegisteredWinningPoStProof()
if err != nil {
@@ -420,21 +454,21 @@ var sealBenchCmd = &cli.Command{
fmt.Println(string(data))
} else {
- fmt.Printf("----\nresults (v28) (%d)\n", sectorSize)
+ fmt.Printf("----\nresults (v28) SectorSize:(%d), SectorNumber:(%d)\n", sectorSize, sectorNumber)
if robench == "" {
- fmt.Printf("seal: addPiece: %s (%s)\n", bo.SealingResults[0].AddPiece, bps(bo.SectorSize, bo.SealingResults[0].AddPiece)) // TODO: average across multiple sealings
- fmt.Printf("seal: preCommit phase 1: %s (%s)\n", bo.SealingResults[0].PreCommit1, bps(bo.SectorSize, bo.SealingResults[0].PreCommit1))
- fmt.Printf("seal: preCommit phase 2: %s (%s)\n", bo.SealingResults[0].PreCommit2, bps(bo.SectorSize, bo.SealingResults[0].PreCommit2))
- fmt.Printf("seal: commit phase 1: %s (%s)\n", bo.SealingResults[0].Commit1, bps(bo.SectorSize, bo.SealingResults[0].Commit1))
- fmt.Printf("seal: commit phase 2: %s (%s)\n", bo.SealingResults[0].Commit2, bps(bo.SectorSize, bo.SealingResults[0].Commit2))
- fmt.Printf("seal: verify: %s\n", bo.SealingResults[0].Verify)
+ fmt.Printf("seal: addPiece: %s (%s)\n", bo.SealingSum.AddPiece, bps(bo.SectorSize, bo.SectorNumber, bo.SealingSum.AddPiece))
+ fmt.Printf("seal: preCommit phase 1: %s (%s)\n", bo.SealingSum.PreCommit1, bps(bo.SectorSize, bo.SectorNumber, bo.SealingSum.PreCommit1))
+ fmt.Printf("seal: preCommit phase 2: %s (%s)\n", bo.SealingSum.PreCommit2, bps(bo.SectorSize, bo.SectorNumber, bo.SealingSum.PreCommit2))
+ fmt.Printf("seal: commit phase 1: %s (%s)\n", bo.SealingSum.Commit1, bps(bo.SectorSize, bo.SectorNumber, bo.SealingSum.Commit1))
+ fmt.Printf("seal: commit phase 2: %s (%s)\n", bo.SealingSum.Commit2, bps(bo.SectorSize, bo.SectorNumber, bo.SealingSum.Commit2))
+ fmt.Printf("seal: verify: %s\n", bo.SealingSum.Verify)
if !c.Bool("skip-unseal") {
- fmt.Printf("unseal: %s (%s)\n", bo.SealingResults[0].Unseal, bps(bo.SectorSize, bo.SealingResults[0].Unseal))
+ fmt.Printf("unseal: %s (%s)\n", bo.SealingSum.Unseal, bps(bo.SectorSize, bo.SectorNumber, bo.SealingSum.Unseal))
}
fmt.Println("")
}
- if !c.Bool("skip-commit2") {
- fmt.Printf("generate candidates: %s (%s)\n", bo.PostGenerateCandidates, bps(bo.SectorSize*abi.SectorSize(len(bo.SealingResults)), bo.PostGenerateCandidates))
+ if !skipc2 {
+ fmt.Printf("generate candidates: %s (%s)\n", bo.PostGenerateCandidates, bps(bo.SectorSize, len(bo.SealingResults), bo.PostGenerateCandidates))
fmt.Printf("compute winning post proof (cold): %s\n", bo.PostWinningProofCold)
fmt.Printf("compute winning post proof (hot): %s\n", bo.PostWinningProofHot)
fmt.Printf("verify winning post proof (cold): %s\n", bo.VerifyWinningPostCold)
@@ -467,8 +501,7 @@ func runSeals(sb *ffiwrapper.Sealer, sbfs *basicfs.Provider, numSectors int, par
if numSectors%par.PreCommit1 != 0 {
return nil, nil, fmt.Errorf("parallelism factor must cleanly divide numSectors")
}
-
- for i := abi.SectorNumber(1); i <= abi.SectorNumber(numSectors); i++ {
+ for i := abi.SectorNumber(0); i < abi.SectorNumber(numSectors); i++ {
sid := storage.SectorRef{
ID: abi.SectorID{
Miner: mid,
@@ -489,7 +522,7 @@ func runSeals(sb *ffiwrapper.Sealer, sbfs *basicfs.Provider, numSectors int, par
pieces = append(pieces, pi)
- sealTimings[i-1].AddPiece = time.Since(start)
+ sealTimings[i].AddPiece = time.Since(start)
}
sectorsPerWorker := numSectors / par.PreCommit1
@@ -498,10 +531,9 @@ func runSeals(sb *ffiwrapper.Sealer, sbfs *basicfs.Provider, numSectors int, par
for wid := 0; wid < par.PreCommit1; wid++ {
go func(worker int) {
sealerr := func() error {
- start := 1 + (worker * sectorsPerWorker)
+ start := worker * sectorsPerWorker
end := start + sectorsPerWorker
for i := abi.SectorNumber(start); i < abi.SectorNumber(end); i++ {
- ix := int(i - 1)
sid := storage.SectorRef{
ID: abi.SectorID{
Miner: mid,
@@ -516,8 +548,8 @@ func runSeals(sb *ffiwrapper.Sealer, sbfs *basicfs.Provider, numSectors int, par
ticket := abi.SealRandomness(trand[:])
log.Infof("[%d] Running replication(1)...", i)
- pieces := []abi.PieceInfo{pieces[ix]}
- pc1o, err := sb.SealPreCommit1(context.TODO(), sid, ticket, pieces)
+ piece := []abi.PieceInfo{pieces[i]}
+ pc1o, err := sb.SealPreCommit1(context.TODO(), sid, ticket, piece)
if err != nil {
return xerrors.Errorf("commit: %w", err)
}
@@ -535,7 +567,7 @@ func runSeals(sb *ffiwrapper.Sealer, sbfs *basicfs.Provider, numSectors int, par
precommit2 := time.Now()
<-preCommit2Sema
- sealedSectors[ix] = saproof2.SectorInfo{
+ sealedSectors[i] = saproof2.SectorInfo{
SealProof: sid.ProofType,
SectorNumber: i,
SealedCID: cids.Sealed,
@@ -549,7 +581,7 @@ func runSeals(sb *ffiwrapper.Sealer, sbfs *basicfs.Provider, numSectors int, par
commitSema <- struct{}{}
commitStart := time.Now()
log.Infof("[%d] Generating PoRep for sector (1)", i)
- c1o, err := sb.SealCommit1(context.TODO(), sid, ticket, seed.Value, pieces, cids)
+ c1o, err := sb.SealCommit1(context.TODO(), sid, ticket, seed.Value, piece, cids)
if err != nil {
return err
}
@@ -630,12 +662,12 @@ func runSeals(sb *ffiwrapper.Sealer, sbfs *basicfs.Provider, numSectors int, par
}
unseal := time.Now()
- sealTimings[ix].PreCommit1 = precommit1.Sub(start)
- sealTimings[ix].PreCommit2 = precommit2.Sub(pc2Start)
- sealTimings[ix].Commit1 = sealcommit1.Sub(commitStart)
- sealTimings[ix].Commit2 = sealcommit2.Sub(sealcommit1)
- sealTimings[ix].Verify = verifySeal.Sub(sealcommit2)
- sealTimings[ix].Unseal = unseal.Sub(verifySeal)
+ sealTimings[i].PreCommit1 = precommit1.Sub(start)
+ sealTimings[i].PreCommit2 = precommit2.Sub(pc2Start)
+ sealTimings[i].Commit1 = sealcommit1.Sub(commitStart)
+ sealTimings[i].Commit2 = sealcommit2.Sub(sealcommit1)
+ sealTimings[i].Verify = verifySeal.Sub(sealcommit2)
+ sealTimings[i].Unseal = unseal.Sub(verifySeal)
}
return nil
}()
@@ -658,8 +690,9 @@ func runSeals(sb *ffiwrapper.Sealer, sbfs *basicfs.Provider, numSectors int, par
}
var proveCmd = &cli.Command{
- Name: "prove",
- Usage: "Benchmark a proof computation",
+ Name: "prove",
+ Usage: "Benchmark a proof computation",
+ ArgsUsage: "[input.json]",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "no-gpu",
@@ -711,8 +744,6 @@ var proveCmd = &cli.Command{
return err
}
- start := time.Now()
-
ref := storage.SectorRef{
ID: abi.SectorID{
Miner: abi.ActorID(mid),
@@ -721,6 +752,9 @@ var proveCmd = &cli.Command{
ProofType: spt(abi.SectorSize(c2in.SectorSize)),
}
+ fmt.Printf("----\nstart proof computation\n")
+ start := time.Now()
+
proof, err := sb.SealCommit2(context.TODO(), ref, c2in.Phase1Out)
if err != nil {
return err
@@ -733,13 +767,14 @@ var proveCmd = &cli.Command{
fmt.Printf("----\nresults (v28) (%d)\n", c2in.SectorSize)
dur := sealCommit2.Sub(start)
- fmt.Printf("seal: commit phase 2: %s (%s)\n", dur, bps(abi.SectorSize(c2in.SectorSize), dur))
+ fmt.Printf("seal: commit phase 2: %s (%s)\n", dur, bps(abi.SectorSize(c2in.SectorSize), 1, dur))
return nil
},
}
-func bps(data abi.SectorSize, d time.Duration) string {
- bdata := new(big.Int).SetUint64(uint64(data))
+func bps(sectorSize abi.SectorSize, sectorNum int, d time.Duration) string {
+ bdata := new(big.Int).SetUint64(uint64(sectorSize))
+ bdata = bdata.Mul(bdata, big.NewInt(int64(sectorNum)))
bdata = bdata.Mul(bdata, big.NewInt(time.Second.Nanoseconds()))
bps := bdata.Div(bdata, big.NewInt(d.Nanoseconds()))
return types.SizeStr(types.BigInt{Int: bps}) + "/s"
diff --git a/cmd/lotus-fountain/main.go b/cmd/lotus-fountain/main.go
index ea7190e83..931978d96 100644
--- a/cmd/lotus-fountain/main.go
+++ b/cmd/lotus-fountain/main.go
@@ -87,7 +87,7 @@ var runCmd = &cli.Command{
return err
}
- log.Info("Remote version: %s", v.Version)
+ log.Infof("Remote version: %s", v.Version)
from, err := address.NewFromString(cctx.String("from"))
if err != nil {
diff --git a/cmd/lotus-gateway/api.go b/cmd/lotus-gateway/api.go
index 875eaac7d..ee19eb948 100644
--- a/cmd/lotus-gateway/api.go
+++ b/cmd/lotus-gateway/api.go
@@ -67,6 +67,7 @@ type gatewayDepsAPI interface {
StateMinerAvailableBalance(context.Context, address.Address, types.TipSetKey) (types.BigInt, error)
StateMinerProvingDeadline(context.Context, address.Address, types.TipSetKey) (*dline.Info, error)
StateCirculatingSupply(context.Context, types.TipSetKey) (abi.TokenAmount, error)
+ StateSectorGetInfo(ctx context.Context, maddr address.Address, n abi.SectorNumber, tsk types.TipSetKey) (*miner.SectorOnChainInfo, error)
StateVerifiedClientStatus(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*abi.StoragePower, error)
StateVMCirculatingSupplyInternal(context.Context, types.TipSetKey) (api.CirculatingSupply, error)
}
@@ -362,7 +363,13 @@ func (a *GatewayAPI) StateCirculatingSupply(ctx context.Context, tsk types.TipSe
return types.BigInt{}, err
}
return a.api.StateCirculatingSupply(ctx, tsk)
+}
+func (a *GatewayAPI) StateSectorGetInfo(ctx context.Context, maddr address.Address, n abi.SectorNumber, tsk types.TipSetKey) (*miner.SectorOnChainInfo, error) {
+ if err := a.checkTipsetKey(ctx, tsk); err != nil {
+ return nil, err
+ }
+ return a.api.StateSectorGetInfo(ctx, maddr, n, tsk)
}
func (a *GatewayAPI) StateVerifiedClientStatus(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*abi.StoragePower, error) {
diff --git a/cmd/lotus-gateway/endtoend_test.go b/cmd/lotus-gateway/endtoend_test.go
index f0b950f5e..4d5e88c82 100644
--- a/cmd/lotus-gateway/endtoend_test.go
+++ b/cmd/lotus-gateway/endtoend_test.go
@@ -171,7 +171,11 @@ func TestDealFlow(t *testing.T) {
nodes := startNodesWithFunds(ctx, t, blocktime, maxLookbackCap, maxStateWaitLookbackLimit)
defer nodes.closer()
- test.MakeDeal(t, ctx, 6, nodes.lite, nodes.miner, false, false)
+ // For these tests where the block time is artificially short, just use
+ // a deal start epoch that is guaranteed to be far enough in the future
+ // so that the deal starts sealing in time
+ dealStartEpoch := abi.ChainEpoch(2 << 12)
+ test.MakeDeal(t, ctx, 6, nodes.lite, nodes.miner, false, false, dealStartEpoch)
}
func TestCLIDealFlow(t *testing.T) {
diff --git a/cmd/lotus-gateway/main.go b/cmd/lotus-gateway/main.go
index 3fed88468..bc8785923 100644
--- a/cmd/lotus-gateway/main.go
+++ b/cmd/lotus-gateway/main.go
@@ -61,6 +61,10 @@ var runCmd = &cli.Command{
Usage: "host address and port the api server will listen on",
Value: "0.0.0.0:2346",
},
+ &cli.IntFlag{
+ Name: "api-max-req-size",
+ Usage: "maximum API request size accepted by the JSON RPC server",
+ },
},
Action: func(cctx *cli.Context) error {
log.Info("Starting lotus gateway")
@@ -87,7 +91,11 @@ var runCmd = &cli.Command{
log.Info("Setting up API endpoint at " + address)
- rpcServer := jsonrpc.NewServer()
+ serverOptions := make([]jsonrpc.ServerOption, 0)
+ if maxRequestSize := cctx.Int("api-max-req-size"); maxRequestSize != 0 {
+ serverOptions = append(serverOptions, jsonrpc.WithMaxRequestSize(int64(maxRequestSize)))
+ }
+ rpcServer := jsonrpc.NewServer(serverOptions...)
rpcServer.Register("Filecoin", metrics.MetricedGatewayAPI(NewGatewayAPI(api)))
mux.Handle("/rpc/v0", rpcServer)
diff --git a/cmd/lotus-seal-worker/info.go b/cmd/lotus-seal-worker/info.go
index 65f26dc86..6d5c2d64e 100644
--- a/cmd/lotus-seal-worker/info.go
+++ b/cmd/lotus-seal-worker/info.go
@@ -2,12 +2,14 @@ package main
import (
"fmt"
+ "sort"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
"github.com/filecoin-project/lotus/chain/types"
lcli "github.com/filecoin-project/lotus/cli"
+ "github.com/filecoin-project/lotus/extern/sector-storage/sealtasks"
)
var infoCmd = &cli.Command{
@@ -49,10 +51,22 @@ var infoCmd = &cli.Command{
return xerrors.Errorf("getting info: %w", err)
}
+ tt, err := api.TaskTypes(ctx)
+ if err != nil {
+ return xerrors.Errorf("getting task types: %w", err)
+ }
+
fmt.Printf("Hostname: %s\n", info.Hostname)
fmt.Printf("CPUs: %d; GPUs: %v\n", info.Resources.CPUs, info.Resources.GPUs)
fmt.Printf("RAM: %s; Swap: %s\n", types.SizeStr(types.NewInt(info.Resources.MemPhysical)), types.SizeStr(types.NewInt(info.Resources.MemSwap)))
fmt.Printf("Reserved memory: %s\n", types.SizeStr(types.NewInt(info.Resources.MemReserved)))
+
+ fmt.Printf("Task types: ")
+ for _, t := range ttList(tt) {
+ fmt.Printf("%s ", t.Short())
+ }
+ fmt.Println()
+
fmt.Println()
paths, err := api.Paths(ctx)
@@ -80,3 +94,14 @@ var infoCmd = &cli.Command{
return nil
},
}
+
+func ttList(tt map[sealtasks.TaskType]struct{}) []sealtasks.TaskType {
+ tasks := make([]sealtasks.TaskType, 0, len(tt))
+ for taskType := range tt {
+ tasks = append(tasks, taskType)
+ }
+ sort.Slice(tasks, func(i, j int) bool {
+ return tasks[i].Less(tasks[j])
+ })
+ return tasks
+}
diff --git a/cmd/lotus-seal-worker/main.go b/cmd/lotus-seal-worker/main.go
index 1f20bffbd..8726a6e0d 100644
--- a/cmd/lotus-seal-worker/main.go
+++ b/cmd/lotus-seal-worker/main.go
@@ -59,6 +59,7 @@ func main() {
storageCmd,
setCmd,
waitQuietCmd,
+ tasksCmd,
}
app := &cli.App{
@@ -362,6 +363,17 @@ var runCmd = &cli.Command{
remote := stores.NewRemote(localStore, nodeApi, sminfo.AuthHeader(), cctx.Int("parallel-fetch-limit"))
+ fh := &stores.FetchHandler{Local: localStore}
+ remoteHandler := func(w http.ResponseWriter, r *http.Request) {
+ if !auth.HasPerm(r.Context(), nil, apistruct.PermAdmin) {
+ w.WriteHeader(401)
+ _ = json.NewEncoder(w).Encode(struct{ Error string }{"unauthorized: missing admin permission"})
+ return
+ }
+
+ fh.ServeHTTP(w, r)
+ }
+
// Create / expose the worker
wsts := statestore.New(namespace.Wrap(ds, modules.WorkerCallsPrefix))
@@ -385,7 +397,7 @@ var runCmd = &cli.Command{
mux.Handle("/rpc/v0", rpcServer)
mux.Handle("/rpc/streams/v0/push/{uuid}", readerHandler)
- mux.PathPrefix("/remote").HandlerFunc((&stores.FetchHandler{Local: localStore}).ServeHTTP)
+ mux.PathPrefix("/remote").HandlerFunc(remoteHandler)
mux.PathPrefix("/").Handler(http.DefaultServeMux) // pprof
ah := &auth.Handler{
diff --git a/cmd/lotus-seal-worker/tasks.go b/cmd/lotus-seal-worker/tasks.go
new file mode 100644
index 000000000..bafd98951
--- /dev/null
+++ b/cmd/lotus-seal-worker/tasks.go
@@ -0,0 +1,82 @@
+package main
+
+import (
+ "context"
+ "strings"
+
+ "github.com/urfave/cli/v2"
+ "golang.org/x/xerrors"
+
+ "github.com/filecoin-project/lotus/api"
+ lcli "github.com/filecoin-project/lotus/cli"
+ "github.com/filecoin-project/lotus/extern/sector-storage/sealtasks"
+)
+
+var tasksCmd = &cli.Command{
+ Name: "tasks",
+ Usage: "Manage task processing",
+ Subcommands: []*cli.Command{
+ tasksEnableCmd,
+ tasksDisableCmd,
+ },
+}
+
+var allowSetting = map[sealtasks.TaskType]struct{}{
+ sealtasks.TTAddPiece: {},
+ sealtasks.TTPreCommit1: {},
+ sealtasks.TTPreCommit2: {},
+ sealtasks.TTCommit2: {},
+ sealtasks.TTUnseal: {},
+}
+
+var settableStr = func() string {
+ var s []string
+ for _, tt := range ttList(allowSetting) {
+ s = append(s, tt.Short())
+ }
+ return strings.Join(s, "|")
+}()
+
+var tasksEnableCmd = &cli.Command{
+ Name: "enable",
+ Usage: "Enable a task type",
+ ArgsUsage: "[" + settableStr + "]",
+ Action: taskAction(api.WorkerAPI.TaskEnable),
+}
+
+var tasksDisableCmd = &cli.Command{
+ Name: "disable",
+ Usage: "Disable a task type",
+ ArgsUsage: "[" + settableStr + "]",
+ Action: taskAction(api.WorkerAPI.TaskDisable),
+}
+
+func taskAction(tf func(a api.WorkerAPI, ctx context.Context, tt sealtasks.TaskType) error) func(cctx *cli.Context) error {
+ return func(cctx *cli.Context) error {
+ if cctx.NArg() != 1 {
+ return xerrors.Errorf("expected 1 argument")
+ }
+
+ var tt sealtasks.TaskType
+ for taskType := range allowSetting {
+ if taskType.Short() == cctx.Args().First() {
+ tt = taskType
+ break
+ }
+ }
+
+ if tt == "" {
+ return xerrors.Errorf("unknown task type '%s'", cctx.Args().First())
+ }
+
+ api, closer, err := lcli.GetWorkerAPI(cctx)
+ if err != nil {
+ return err
+ }
+ defer closer()
+
+ ctx := lcli.ReqContext(cctx)
+
+ return tf(api, ctx, tt)
+ }
+}
diff --git a/cmd/lotus-seed/main.go b/cmd/lotus-seed/main.go
index 7822900e4..c4e62b419 100644
--- a/cmd/lotus-seed/main.go
+++ b/cmd/lotus-seed/main.go
@@ -7,6 +7,8 @@ import (
"io/ioutil"
"os"
+ "github.com/filecoin-project/go-state-types/network"
+
"github.com/docker/go-units"
logging "github.com/ipfs/go-log/v2"
"github.com/mitchellh/go-homedir"
@@ -127,7 +129,7 @@ var preSealCmd = &cli.Command{
}
sectorSize := abi.SectorSize(sectorSizeInt)
- spt, err := miner.SealProofTypeFromSectorSize(sectorSize, build.NewestNetworkVersion)
+ spt, err := miner.SealProofTypeFromSectorSize(sectorSize, network.Version0)
if err != nil {
return err
}
diff --git a/cmd/lotus-seed/seed/seed.go b/cmd/lotus-seed/seed/seed.go
index b52490928..48183690d 100644
--- a/cmd/lotus-seed/seed/seed.go
+++ b/cmd/lotus-seed/seed/seed.go
@@ -19,9 +19,9 @@ import (
ffi "github.com/filecoin-project/filecoin-ffi"
"github.com/filecoin-project/go-address"
+ "github.com/filecoin-project/go-commp-utils/zerocomm"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
- "github.com/filecoin-project/lotus/extern/sector-storage/zerocomm"
"github.com/filecoin-project/specs-storage/storage"
market2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/market"
diff --git a/cmd/lotus-shed/balances.go b/cmd/lotus-shed/balances.go
index da1263408..3280a63e5 100644
--- a/cmd/lotus-shed/balances.go
+++ b/cmd/lotus-shed/balances.go
@@ -2,9 +2,13 @@ package main
import (
"context"
+ "encoding/csv"
"fmt"
"io"
+ "os"
"strconv"
+ "strings"
+ "time"
"github.com/filecoin-project/lotus/chain/gen/genesis"
@@ -62,6 +66,7 @@ var auditsCmd = &cli.Command{
chainBalanceCmd,
chainBalanceStateCmd,
chainPledgeCmd,
+ fillBalancesCmd,
},
}
@@ -487,3 +492,119 @@ var chainPledgeCmd = &cli.Command{
return nil
},
}
+
+const dateFmt = "1/02/06"
+
+func parseCsv(inp string) ([]time.Time, []address.Address, error) {
+ fi, err := os.Open(inp)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ r := csv.NewReader(fi)
+ recs, err := r.ReadAll()
+ if err != nil {
+ return nil, nil, err
+ }
+
+ var addrs []address.Address
+ for _, rec := range recs[1:] {
+ a, err := address.NewFromString(rec[0])
+ if err != nil {
+ return nil, nil, err
+ }
+ addrs = append(addrs, a)
+ }
+
+ var dates []time.Time
+ for _, d := range recs[0][1:] {
+ if len(d) == 0 {
+ continue
+ }
+ p := strings.Split(d, " ")
+ t, err := time.Parse(dateFmt, p[len(p)-1])
+ if err != nil {
+ return nil, nil, err
+ }
+
+ dates = append(dates, t)
+ }
+
+ return dates, addrs, nil
+}
+
+func heightForDate(d time.Time, ts *types.TipSet) abi.ChainEpoch {
+ secs := d.Unix()
+ gents := ts.Blocks()[0].Timestamp
+ gents -= uint64(30 * ts.Height())
+ return abi.ChainEpoch((secs - int64(gents)) / 30)
+}
+
+var fillBalancesCmd = &cli.Command{
+ Name: "fill-balances",
+ Description: "fill out balances for addresses on dates in given spreadsheet",
+ Flags: []cli.Flag{},
+ Action: func(cctx *cli.Context) error {
+ api, closer, err := lcli.GetFullNodeAPI(cctx)
+ if err != nil {
+ return err
+ }
+
+ defer closer()
+ ctx := lcli.ReqContext(cctx)
+
+ dates, addrs, err := parseCsv(cctx.Args().First())
+ if err != nil {
+ return err
+ }
+
+ ts, err := api.ChainHead(ctx)
+ if err != nil {
+ return err
+ }
+
+ var tipsets []*types.TipSet
+ for _, d := range dates {
+ h := heightForDate(d, ts)
+ hts, err := api.ChainGetTipSetByHeight(ctx, h, ts.Key())
+ if err != nil {
+ return err
+ }
+ tipsets = append(tipsets, hts)
+ }
+
+ var balances [][]abi.TokenAmount
+ for _, a := range addrs {
+ var b []abi.TokenAmount
+ for _, hts := range tipsets {
+ act, err := api.StateGetActor(ctx, a, hts.Key())
+ if err != nil {
+ if !strings.Contains(err.Error(), "actor not found") {
+ return fmt.Errorf("error for %s at %s: %w", a, hts.Key(), err)
+ }
+ b = append(b, types.NewInt(0))
+ continue
+ }
+ b = append(b, act.Balance)
+ }
+ balances = append(balances, b)
+ }
+
+ var datestrs []string
+ for _, d := range dates {
+ datestrs = append(datestrs, "Balance at "+d.Format(dateFmt))
+ }
+
+ w := csv.NewWriter(os.Stdout)
+ w.Write(append([]string{"Wallet Address"}, datestrs...)) // nolint:errcheck
+ for i := 0; i < len(addrs); i++ {
+ row := []string{addrs[i].String()}
+ for _, b := range balances[i] {
+ row = append(row, types.FIL(b).String())
+ }
+ w.Write(row) // nolint:errcheck
+ }
+ w.Flush()
+ return nil
+ },
+}
diff --git a/cmd/lotus-shed/commp.go b/cmd/lotus-shed/commp.go
index 9b0cab75d..6f7923c24 100644
--- a/cmd/lotus-shed/commp.go
+++ b/cmd/lotus-shed/commp.go
@@ -1,27 +1,55 @@
package main
import (
+ "encoding/base64"
"encoding/hex"
"fmt"
commcid "github.com/filecoin-project/go-fil-commcid"
"github.com/urfave/cli/v2"
+ "golang.org/x/xerrors"
)
var commpToCidCmd = &cli.Command{
Name: "commp-to-cid",
+ Usage: "Convert commP to Cid",
Description: "Convert a raw commP to a piece-Cid",
+ ArgsUsage: "[data]",
+ Flags: []cli.Flag{
+ &cli.StringFlag{
+ Name: "encoding",
+ Value: "base64",
+ Usage: "specify input encoding to parse",
+ },
+ },
Action: func(cctx *cli.Context) error {
if !cctx.Args().Present() {
return fmt.Errorf("must specify commP to convert")
}
- dec, err := hex.DecodeString(cctx.Args().First())
- if err != nil {
- return fmt.Errorf("failed to decode input as hex string: %w", err)
+ var dec []byte
+ switch cctx.String("encoding") {
+ case "base64":
+ data, err := base64.StdEncoding.DecodeString(cctx.Args().First())
+ if err != nil {
+ return xerrors.Errorf("decoding base64 value: %w", err)
+ }
+ dec = data
+ case "hex":
+ data, err := hex.DecodeString(cctx.Args().First())
+ if err != nil {
+ return xerrors.Errorf("decoding hex value: %w", err)
+ }
+ dec = data
+ default:
+ return xerrors.Errorf("unrecognized encoding: %s", cctx.String("encoding"))
}
- fmt.Println(commcid.PieceCommitmentV1ToCID(dec))
+ cid, err := commcid.PieceCommitmentV1ToCID(dec)
+ if err != nil {
+ return err
+ }
+ fmt.Println(cid)
return nil
},
}
diff --git a/cmd/lotus-shed/dealtracker.go b/cmd/lotus-shed/dealtracker.go
deleted file mode 100644
index 8ded6bf4a..000000000
--- a/cmd/lotus-shed/dealtracker.go
+++ /dev/null
@@ -1,325 +0,0 @@
-package main
-
-import (
- "context"
- "encoding/json"
- "net"
- "net/http"
- "sync"
-
- "github.com/filecoin-project/go-address"
- "github.com/filecoin-project/go-state-types/abi"
- "github.com/filecoin-project/lotus/api"
- lcli "github.com/filecoin-project/lotus/cli"
- "github.com/ipfs/go-cid"
- "github.com/urfave/cli/v2"
-)
-
-type dealStatsServer struct {
- api api.FullNode
-}
-
-// Requested by @jbenet
-// How many epochs back to look at for dealstats
-var epochLookback = abi.ChainEpoch(10)
-
-// these lists grow continuously with the network
-// TODO: need to switch this to an LRU of sorts, to ensure refreshes
-var knownFiltered = new(sync.Map)
-var resolvedWallets = new(sync.Map)
-
-func init() {
- for _, a := range []string{
- "t0100", // client for genesis miner
- "t0101", // client for genesis miner
- "t0102", // client for genesis miner
- "t0112", // client for genesis miner
- "t0113", // client for genesis miner
- "t0114", // client for genesis miner
- "t1nslxql4pck5pq7hddlzym3orxlx35wkepzjkm3i", // SR1 dealbot wallet
- "t1stghxhdp2w53dym2nz2jtbpk6ccd4l2lxgmezlq", // SR1 dealbot wallet
- "t1mcr5xkgv4jdl3rnz77outn6xbmygb55vdejgbfi", // SR1 dealbot wallet
- "t1qiqdbbmrdalbntnuapriirduvxu5ltsc5mhy7si", // SR1 dealbot wallet
- } {
- a, err := address.NewFromString(a)
- if err != nil {
- panic(err)
- }
- knownFiltered.Store(a, true)
- }
-}
-
-type dealCountResp struct {
- Epoch int64 `json:"epoch"`
- Endpoint string `json:"endpoint"`
- Payload int64 `json:"payload"`
-}
-
-func (dss *dealStatsServer) handleStorageDealCount(w http.ResponseWriter, r *http.Request) {
-
- epoch, deals := dss.filteredDealList()
- if epoch == 0 {
- w.WriteHeader(500)
- return
- }
-
- if err := json.NewEncoder(w).Encode(&dealCountResp{
- Endpoint: "COUNT_DEALS",
- Payload: int64(len(deals)),
- Epoch: epoch,
- }); err != nil {
- log.Warnf("failed to write back deal count response: %s", err)
- return
- }
-}
-
-type dealAverageResp struct {
- Epoch int64 `json:"epoch"`
- Endpoint string `json:"endpoint"`
- Payload int64 `json:"payload"`
-}
-
-func (dss *dealStatsServer) handleStorageDealAverageSize(w http.ResponseWriter, r *http.Request) {
-
- epoch, deals := dss.filteredDealList()
- if epoch == 0 {
- w.WriteHeader(500)
- return
- }
-
- var totalBytes int64
- for _, d := range deals {
- totalBytes += int64(d.deal.Proposal.PieceSize.Unpadded())
- }
-
- if err := json.NewEncoder(w).Encode(&dealAverageResp{
- Endpoint: "AVERAGE_DEAL_SIZE",
- Payload: totalBytes / int64(len(deals)),
- Epoch: epoch,
- }); err != nil {
- log.Warnf("failed to write back deal average response: %s", err)
- return
- }
-}
-
-type dealTotalResp struct {
- Epoch int64 `json:"epoch"`
- Endpoint string `json:"endpoint"`
- Payload int64 `json:"payload"`
-}
-
-func (dss *dealStatsServer) handleStorageDealTotalReal(w http.ResponseWriter, r *http.Request) {
- epoch, deals := dss.filteredDealList()
- if epoch == 0 {
- w.WriteHeader(500)
- return
- }
-
- var totalBytes int64
- for _, d := range deals {
- totalBytes += int64(d.deal.Proposal.PieceSize.Unpadded())
- }
-
- if err := json.NewEncoder(w).Encode(&dealTotalResp{
- Endpoint: "DEAL_BYTES",
- Payload: totalBytes,
- Epoch: epoch,
- }); err != nil {
- log.Warnf("failed to write back deal average response: %s", err)
- return
- }
-
-}
-
-type clientStatsOutput struct {
- Epoch int64 `json:"epoch"`
- Endpoint string `json:"endpoint"`
- Payload []*clientStats `json:"payload"`
-}
-
-type clientStats struct {
- Client address.Address `json:"client"`
- DataSize int64 `json:"data_size"`
- NumCids int `json:"num_cids"`
- NumDeals int `json:"num_deals"`
- NumMiners int `json:"num_miners"`
-
- cids map[cid.Cid]bool
- providers map[address.Address]bool
-}
-
-func (dss *dealStatsServer) handleStorageClientStats(w http.ResponseWriter, r *http.Request) {
- epoch, deals := dss.filteredDealList()
- if epoch == 0 {
- w.WriteHeader(500)
- return
- }
-
- stats := make(map[address.Address]*clientStats)
-
- for _, d := range deals {
-
- st, ok := stats[d.deal.Proposal.Client]
- if !ok {
- st = &clientStats{
- Client: d.resolvedWallet,
- cids: make(map[cid.Cid]bool),
- providers: make(map[address.Address]bool),
- }
- stats[d.deal.Proposal.Client] = st
- }
-
- st.DataSize += int64(d.deal.Proposal.PieceSize.Unpadded())
- st.cids[d.deal.Proposal.PieceCID] = true
- st.providers[d.deal.Proposal.Provider] = true
- st.NumDeals++
- }
-
- out := clientStatsOutput{
- Epoch: epoch,
- Endpoint: "CLIENT_DEAL_STATS",
- Payload: make([]*clientStats, 0, len(stats)),
- }
- for _, cs := range stats {
- cs.NumCids = len(cs.cids)
- cs.NumMiners = len(cs.providers)
- out.Payload = append(out.Payload, cs)
- }
-
- if err := json.NewEncoder(w).Encode(out); err != nil {
- log.Warnf("failed to write back client stats response: %s", err)
- return
- }
-}
-
-type dealInfo struct {
- deal api.MarketDeal
- resolvedWallet address.Address
-}
-
-// filteredDealList returns the current epoch and a list of filtered deals
-// on error returns an epoch of 0
-func (dss *dealStatsServer) filteredDealList() (int64, map[string]dealInfo) {
- ctx := context.Background()
-
- head, err := dss.api.ChainHead(ctx)
- if err != nil {
- log.Warnf("failed to get chain head: %s", err)
- return 0, nil
- }
-
- head, err = dss.api.ChainGetTipSetByHeight(ctx, head.Height()-epochLookback, head.Key())
- if err != nil {
- log.Warnf("failed to walk back %s epochs: %s", epochLookback, err)
- return 0, nil
- }
-
- // Disabled as per @pooja's request
- //
- // // Exclude any address associated with a miner
- // miners, err := dss.api.StateListMiners(ctx, head.Key())
- // if err != nil {
- // log.Warnf("failed to get miner list: %s", err)
- // return 0, nil
- // }
- // for _, m := range miners {
- // info, err := dss.api.StateMinerInfo(ctx, m, head.Key())
- // if err != nil {
- // log.Warnf("failed to get info for known miner '%s': %s", m, err)
- // continue
- // }
-
- // knownFiltered.Store(info.Owner, true)
- // knownFiltered.Store(info.Worker, true)
- // for _, a := range info.ControlAddresses {
- // knownFiltered.Store(a, true)
- // }
- // }
-
- deals, err := dss.api.StateMarketDeals(ctx, head.Key())
- if err != nil {
- log.Warnf("failed to get market deals: %s", err)
- return 0, nil
- }
-
- ret := make(map[string]dealInfo, len(deals))
- for dealKey, d := range deals {
-
- // Counting no-longer-active deals as per Pooja's request
- // // https://github.com/filecoin-project/specs-actors/blob/v0.9.9/actors/builtin/market/deal.go#L81-L85
- // if d.State.SectorStartEpoch < 0 {
- // continue
- // }
-
- if _, isFiltered := knownFiltered.Load(d.Proposal.Client); isFiltered {
- continue
- }
-
- if _, wasSeen := resolvedWallets.Load(d.Proposal.Client); !wasSeen {
- w, err := dss.api.StateAccountKey(ctx, d.Proposal.Client, head.Key())
- if err != nil {
- log.Warnf("failed to resolve id '%s' to wallet address: %s", d.Proposal.Client, err)
- continue
- } else {
- resolvedWallets.Store(d.Proposal.Client, w)
- }
- }
-
- w, _ := resolvedWallets.Load(d.Proposal.Client)
- if _, isFiltered := knownFiltered.Load(w); isFiltered {
- continue
- }
-
- ret[dealKey] = dealInfo{
- deal: d,
- resolvedWallet: w.(address.Address),
- }
- }
-
- return int64(head.Height()), ret
-}
-
-var serveDealStatsCmd = &cli.Command{
- Name: "serve-deal-stats",
- Flags: []cli.Flag{},
- Action: func(cctx *cli.Context) error {
- api, closer, err := lcli.GetFullNodeAPI(cctx)
- if err != nil {
- return err
- }
-
- defer closer()
- ctx := lcli.ReqContext(cctx)
-
- _ = ctx
-
- dss := &dealStatsServer{api}
-
- mux := &http.ServeMux{}
- mux.HandleFunc("/api/storagedeal/count", dss.handleStorageDealCount)
- mux.HandleFunc("/api/storagedeal/averagesize", dss.handleStorageDealAverageSize)
- mux.HandleFunc("/api/storagedeal/totalreal", dss.handleStorageDealTotalReal)
- mux.HandleFunc("/api/storagedeal/clientstats", dss.handleStorageClientStats)
-
- s := &http.Server{
- Addr: ":7272",
- Handler: mux,
- }
-
- go func() {
- <-ctx.Done()
- if err := s.Shutdown(context.TODO()); err != nil {
- log.Error(err)
- }
- }()
-
- list, err := net.Listen("tcp", ":7272") // nolint
- if err != nil {
- panic(err)
- }
-
- log.Warnf("deal-stat server listening on %s\n== NOTE: QUERIES ARE EXPENSIVE - YOU MUST FRONT-CACHE THIS SERVICE\n", list.Addr().String())
-
- return s.Serve(list)
- },
-}
diff --git a/cmd/lotus-shed/ledger.go b/cmd/lotus-shed/ledger.go
index ecb13ec64..75c6871d0 100644
--- a/cmd/lotus-shed/ledger.go
+++ b/cmd/lotus-shed/ledger.go
@@ -8,6 +8,7 @@ import (
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/big"
+ "github.com/filecoin-project/go-state-types/crypto"
"github.com/urfave/cli/v2"
ledgerfil "github.com/whyrusleeping/ledger-filecoin-go"
@@ -242,13 +243,16 @@ var ledgerSignTestCmd = &cli.Command{
if err != nil {
return err
}
+ fmt.Printf("Message: %x\n", b.RawData())
sig, err := fl.SignSECP256K1(p, b.RawData())
if err != nil {
return err
}
- fmt.Println(sig.SignatureBytes())
+ sigBytes := append([]byte{byte(crypto.SigTypeSecp256k1)}, sig.SignatureBytes()...)
+
+ fmt.Printf("Signature: %x\n", sigBytes)
return nil
},
diff --git a/cmd/lotus-shed/main.go b/cmd/lotus-shed/main.go
index 5da5c0188..19067f8c9 100644
--- a/cmd/lotus-shed/main.go
+++ b/cmd/lotus-shed/main.go
@@ -30,6 +30,7 @@ func main() {
importObjectCmd,
commpToCidCmd,
fetchParamCmd,
+ postFindCmd,
proofsCmd,
verifRegCmd,
miscCmd,
@@ -39,7 +40,7 @@ func main() {
mpoolStatsCmd,
exportChainCmd,
consensusCmd,
- serveDealStatsCmd,
+ rollupDealStatsCmd,
syncCmd,
stateTreePruneCmd,
datastoreCmd,
diff --git a/cmd/lotus-shed/postfind.go b/cmd/lotus-shed/postfind.go
new file mode 100644
index 000000000..83006fd09
--- /dev/null
+++ b/cmd/lotus-shed/postfind.go
@@ -0,0 +1,129 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/filecoin-project/go-address"
+ "github.com/filecoin-project/go-state-types/abi"
+ "github.com/filecoin-project/go-state-types/big"
+ lapi "github.com/filecoin-project/lotus/api"
+ "github.com/filecoin-project/lotus/chain/types"
+ lcli "github.com/filecoin-project/lotus/cli"
+ "github.com/filecoin-project/specs-actors/v2/actors/builtin"
+ "github.com/urfave/cli/v2"
+)
+
+var postFindCmd = &cli.Command{
+ Name: "post-find",
+ Description: "return addresses of all miners who have over zero power and have posted in the last day",
+ Flags: []cli.Flag{
+ &cli.StringFlag{
+ Name: "tipset",
+ Usage: "specify tipset state to search on",
+ },
+ &cli.BoolFlag{
+ Name: "verbose",
+ Usage: "get more frequent print updates",
+ },
+ &cli.BoolFlag{
+ Name: "withpower",
+ Usage: "only print addrs of miners with more than zero power",
+ },
+ &cli.IntFlag{
+ Name: "lookback",
+ Usage: "number of past epochs to search for post",
+ Value: 2880, //default 1 day
+ },
+ },
+ Action: func(c *cli.Context) error {
+ api, acloser, err := lcli.GetFullNodeAPI(c)
+ if err != nil {
+ return err
+ }
+ defer acloser()
+ ctx := lcli.ReqContext(c)
+ verbose := c.Bool("verbose")
+ withpower := c.Bool("withpower")
+
+ startTs, err := lcli.LoadTipSet(ctx, c, api)
+ if err != nil {
+ return err
+ }
+ if startTs == nil {
+ startTs, err = api.ChainHead(ctx)
+ if err != nil {
+ return err
+ }
+ }
+ stopEpoch := startTs.Height() - abi.ChainEpoch(c.Int("lookback"))
+ if verbose {
+ fmt.Printf("Collecting messages between %d and %d\n", startTs.Height(), stopEpoch)
+ }
+ // Get all messages over the last day
+ ts := startTs
+ msgs := make([]*types.Message, 0)
+ for ts.Height() > stopEpoch {
+ // Get messages on ts parent
+ next, err := api.ChainGetParentMessages(ctx, ts.Cids()[0])
+ if err != nil {
+ return err
+ }
+ msgs = append(msgs, messagesFromAPIMessages(next)...)
+
+ // Next ts
+ ts, err = api.ChainGetTipSet(ctx, ts.Parents())
+ if err != nil {
+ return err
+ }
+ if verbose && int64(ts.Height())%100 == 0 {
+ fmt.Printf("Collected messages back to height %d\n", ts.Height())
+ }
+ }
+ fmt.Printf("Loaded messages to height %d\n", ts.Height())
+
+ mAddrs, err := api.StateListMiners(ctx, startTs.Key())
+ if err != nil {
+ return err
+ }
+
+ minersToCheck := make(map[address.Address]struct{})
+ for _, mAddr := range mAddrs {
+ // if they have no power ignore. This filters out 14k inactive miners
+ // so we can do 100x fewer expensive message queries
+ if withpower {
+ power, err := api.StateMinerPower(ctx, mAddr, startTs.Key())
+ if err != nil {
+ return err
+ }
+ if power.MinerPower.RawBytePower.GreaterThan(big.Zero()) {
+ minersToCheck[mAddr] = struct{}{}
+ }
+ } else {
+ minersToCheck[mAddr] = struct{}{}
+ }
+ }
+ fmt.Printf("Loaded %d miners to check\n", len(minersToCheck))
+
+ postedMiners := make(map[address.Address]struct{})
+ for _, msg := range msgs {
+ _, shouldCheck := minersToCheck[msg.To]
+ _, seenBefore := postedMiners[msg.To]
+
+ if shouldCheck && !seenBefore {
+ if msg.Method == builtin.MethodsMiner.SubmitWindowedPoSt {
+ fmt.Printf("%s\n", msg.To)
+ postedMiners[msg.To] = struct{}{}
+ }
+ }
+ }
+ return nil
+ },
+}
+
+func messagesFromAPIMessages(apiMessages []lapi.Message) []*types.Message {
+ messages := make([]*types.Message, len(apiMessages))
+ for i, apiMessage := range apiMessages {
+ messages[i] = apiMessage.Message
+ }
+ return messages
+}
diff --git a/cmd/lotus-shed/rpc.go b/cmd/lotus-shed/rpc.go
index 924bd197c..cbee95c6b 100644
--- a/cmd/lotus-shed/rpc.go
+++ b/cmd/lotus-shed/rpc.go
@@ -9,6 +9,7 @@ import (
"io/ioutil"
"net/http"
"net/url"
+ "os"
"strings"
"text/scanner"
@@ -55,6 +56,60 @@ var rpcCmd = &cli.Command{
cs.Close() // nolint:errcheck
}()
+ send := func(method, params string) error {
+ jreq, err := json.Marshal(struct {
+ Jsonrpc string `json:"jsonrpc"`
+ ID int `json:"id"`
+ Method string `json:"method"`
+ Params json.RawMessage `json:"params"`
+ }{
+ Jsonrpc: "2.0",
+ Method: "Filecoin." + method,
+ Params: json.RawMessage(params),
+ ID: 0,
+ })
+ if err != nil {
+ return err
+ }
+
+ req, err := http.NewRequest("POST", addr, bytes.NewReader(jreq))
+ if err != nil {
+ return err
+ }
+ req.Header = headers
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ return err
+ }
+
+ rb, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return err
+ }
+
+ fmt.Println(string(rb))
+
+ if err := resp.Body.Close(); err != nil {
+ return err
+ }
+
+ return nil
+ }
+
+ if cctx.Args().Present() {
+ if cctx.Args().Len() > 2 {
+ return xerrors.Errorf("expected 1 or 2 arguments: method [params]")
+ }
+
+ params := cctx.Args().Get(1)
+ if params == "" {
+ // TODO: try to be smart and use zero-values for method
+ params = "[]"
+ }
+
+ return send(cctx.Args().Get(0), params)
+ }
+
cctx.App.Metadata["repoType"] = repo.FullNode
if err := lcli.VersionCmd.Action(cctx); err != nil {
return err
@@ -94,40 +149,8 @@ var rpcCmd = &cli.Command{
s.Scan()
params := line[s.Position.Offset:]
- jreq, err := json.Marshal(struct {
- Jsonrpc string `json:"jsonrpc"`
- ID int `json:"id"`
- Method string `json:"method"`
- Params json.RawMessage `json:"params"`
- }{
- Jsonrpc: "2.0",
- Method: "Filecoin." + method,
- Params: json.RawMessage(params),
- ID: 0,
- })
- if err != nil {
- return err
- }
-
- req, err := http.NewRequest("POST", addr, bytes.NewReader(jreq))
- if err != nil {
- return err
- }
- req.Header = headers
- resp, err := http.DefaultClient.Do(req)
- if err != nil {
- return err
- }
-
- rb, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return err
- }
-
- fmt.Println(string(rb))
-
- if err := resp.Body.Close(); err != nil {
- return err
+ if err := send(method, params); err != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "%v", err)
}
}
diff --git a/cmd/lotus-shed/sr2-dealstats-rollup.go b/cmd/lotus-shed/sr2-dealstats-rollup.go
new file mode 100644
index 000000000..e0673013e
--- /dev/null
+++ b/cmd/lotus-shed/sr2-dealstats-rollup.go
@@ -0,0 +1,432 @@
+package main
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "net/http"
+ "os"
+ "sort"
+ "strings"
+
+ "github.com/Jeffail/gabs"
+ "github.com/filecoin-project/go-address"
+ "github.com/filecoin-project/go-state-types/abi"
+ lcli "github.com/filecoin-project/lotus/cli"
+ "github.com/ipfs/go-cid"
+ "github.com/urfave/cli/v2"
+ "golang.org/x/xerrors"
+)
+
+// Requested by @jbenet
+// How many epochs back to look at for dealstats
+var epochLookback = abi.ChainEpoch(10)
+
+var resolvedWallets = map[address.Address]address.Address{}
+var knownAddrMap = map[address.Address]string{}
+
+//
+// contents of basic_stats.json
+type competitionTotalOutput struct {
+ Epoch int64 `json:"epoch"`
+ Endpoint string `json:"endpoint"`
+ Payload competitionTotal `json:"payload"`
+}
+type competitionTotal struct {
+ UniqueCids int `json:"total_unique_cids"`
+ UniqueProviders int `json:"total_unique_providers"`
+ UniqueProjects int `json:"total_unique_projects"`
+ UniqueClients int `json:"total_unique_clients"`
+ TotalDeals int `json:"total_num_deals"`
+ TotalBytes int64 `json:"total_stored_data_size"`
+
+ seenProject map[string]bool
+ seenClient map[address.Address]bool
+ seenProvider map[address.Address]bool
+ seenPieceCid map[cid.Cid]bool
+}
+
+//
+// contents of client_stats.json
+type projectAggregateStatsOutput struct {
+ Epoch int64 `json:"epoch"`
+ Endpoint string `json:"endpoint"`
+ Payload map[string]*projectAggregateStats `json:"payload"`
+}
+type projectAggregateStats struct {
+ ProjectID string `json:"project_id"`
+ DataSizeMaxProvider int64 `json:"max_data_size_stored_with_single_provider"`
+ HighestCidDealCount int `json:"max_same_cid_deals"`
+ DataSize int64 `json:"total_data_size"`
+ NumCids int `json:"total_num_cids"`
+ NumDeals int `json:"total_num_deals"`
+ NumProviders int `json:"total_num_providers"`
+ ClientStats map[string]*clientAggregateStats `json:"clients"`
+
+ dataPerProvider map[address.Address]int64
+ cidDeals map[cid.Cid]int
+}
+type clientAggregateStats struct {
+ Client string `json:"client"`
+ DataSize int64 `json:"total_data_size"`
+ NumCids int `json:"total_num_cids"`
+ NumDeals int `json:"total_num_deals"`
+ NumProviders int `json:"total_num_providers"`
+
+ providers map[address.Address]bool
+ cids map[cid.Cid]bool
+}
+
+//
+// contents of deals_list_{{projid}}.json
+type dealListOutput struct {
+ Epoch int64 `json:"epoch"`
+ Endpoint string `json:"endpoint"`
+ Payload []*individualDeal `json:"payload"`
+}
+type individualDeal struct {
+ ProjectID string `json:"project_id"`
+ Client string `json:"client"`
+ DealID string `json:"deal_id"`
+ DealStartEpoch int64 `json:"deal_start_epoch"`
+ MinerID string `json:"miner_id"`
+ PayloadCID string `json:"payload_cid"`
+ PaddedSize int64 `json:"data_size"`
+}
+
+var rollupDealStatsCmd = &cli.Command{
+ Name: "rollup-deal-stats",
+ Flags: []cli.Flag{},
+ Action: func(cctx *cli.Context) error {
+
+ if cctx.Args().Len() != 2 || cctx.Args().Get(0) == "" || cctx.Args().Get(1) == "" {
+ return errors.New("must supply 2 arguments: a nonexistent target directory to write results to and a source of currently active projects")
+ }
+
+ outDirName := cctx.Args().Get(0)
+ if _, err := os.Stat(outDirName); err == nil {
+ return fmt.Errorf("unable to proceed: supplied stat target '%s' already exists", outDirName)
+ }
+
+ if err := os.MkdirAll(outDirName, 0755); err != nil {
+ return fmt.Errorf("creation of destination '%s' failed: %s", outDirName, err)
+ }
+
+ ctx := lcli.ReqContext(cctx)
+
+ projListName := cctx.Args().Get(1)
+ var projListFh *os.File
+
+ {
+ // Parses JSON input in the form:
+ // {
+ // "payload": [
+ // {
+ // "project": "5fb5f5b3ad3275e236287ce3",
+ // "address": "f3w3r2c6iukyh3u6f6kx62s5g6n2gf54aqp33ukqrqhje2y6xhf7k55przg4xqgahpcdal6laljz6zonma5pka"
+ // },
+ // {
+ // "project": "5fb608c4ad3275e236287ced",
+ // "address": "f3rs2khurnubol6ent27lpggidxxujqo2lg5aap5d5bmtam6yjb5wfla5cxxdgj45tqoaawgpzt5lofc3vpzfq"
+ // },
+ // ...
+ // ]
+ // }
+ if strings.HasPrefix(projListName, "http://") || strings.HasPrefix(projListName, "https://") {
+ req, err := http.NewRequestWithContext(ctx, "GET", projListName, nil)
+ if err != nil {
+ return err
+ }
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close() //nolint:errcheck
+
+ if resp.StatusCode != http.StatusOK {
+ return xerrors.Errorf("non-200 response: %d", resp.StatusCode)
+ }
+
+ projListFh, err = os.Create(outDirName + "/client_list.json")
+ if err != nil {
+ return err
+ }
+
+ _, err = io.Copy(projListFh, resp.Body)
+ if err != nil {
+ return err
+ }
+ } else {
+ return errors.New("file inputs not yet supported")
+ }
+
+ if _, err := projListFh.Seek(0, 0); err != nil {
+ return err
+ }
+ defer projListFh.Close() //nolint:errcheck
+
+ projList, err := gabs.ParseJSONBuffer(projListFh)
+ if err != nil {
+ return err
+ }
+ proj, err := projList.Search("payload").Children()
+ if err != nil {
+ return err
+ }
+ for _, p := range proj {
+ a, err := address.NewFromString(p.S("address").Data().(string))
+ if err != nil {
+ return err
+ }
+
+ knownAddrMap[a] = p.S("project").Data().(string)
+ }
+
+ if len(knownAddrMap) == 0 {
+ return fmt.Errorf("no active projects/clients found in '%s': unable to continue", projListName)
+ }
+ }
+
+ outClientStatsFd, err := os.Create(outDirName + "/client_stats.json")
+ if err != nil {
+ return err
+ }
+ defer outClientStatsFd.Close() //nolint:errcheck
+
+ outBasicStatsFd, err := os.Create(outDirName + "/basic_stats.json")
+ if err != nil {
+ return err
+ }
+ defer outBasicStatsFd.Close() //nolint:errcheck
+
+ outUnfilteredStatsFd, err := os.Create(outDirName + "/unfiltered_basic_stats.json")
+ if err != nil {
+ return err
+ }
+ defer outUnfilteredStatsFd.Close() //nolint:errcheck
+
+ api, apiCloser, err := lcli.GetFullNodeAPI(cctx)
+ if err != nil {
+ return err
+ }
+ defer apiCloser()
+
+ head, err := api.ChainHead(ctx)
+ if err != nil {
+ return err
+ }
+
+ head, err = api.ChainGetTipSetByHeight(ctx, head.Height()-epochLookback, head.Key())
+ if err != nil {
+ return err
+ }
+
+ grandTotals := competitionTotal{
+ seenProject: make(map[string]bool),
+ seenClient: make(map[address.Address]bool),
+ seenProvider: make(map[address.Address]bool),
+ seenPieceCid: make(map[cid.Cid]bool),
+ }
+
+ unfilteredGrandTotals := competitionTotal{
+ seenClient: make(map[address.Address]bool),
+ seenProvider: make(map[address.Address]bool),
+ seenPieceCid: make(map[cid.Cid]bool),
+ }
+
+ projStats := make(map[string]*projectAggregateStats)
+ projDealLists := make(map[string][]*individualDeal)
+
+ deals, err := api.StateMarketDeals(ctx, head.Key())
+ if err != nil {
+ return err
+ }
+
+ for dealID, dealInfo := range deals {
+
+ // Counting no-longer-active deals as per Pooja's request
+ // // https://github.com/filecoin-project/specs-actors/blob/v0.9.9/actors/builtin/market/deal.go#L81-L85
+ // if d.State.SectorStartEpoch < 0 {
+ // continue
+ // }
+
+ clientAddr, found := resolvedWallets[dealInfo.Proposal.Client]
+ if !found {
+ var err error
+ clientAddr, err = api.StateAccountKey(ctx, dealInfo.Proposal.Client, head.Key())
+ if err != nil {
+ log.Warnf("failed to resolve id '%s' to wallet address: %s", dealInfo.Proposal.Client, err)
+ continue
+ }
+
+ resolvedWallets[dealInfo.Proposal.Client] = clientAddr
+ }
+
+ unfilteredGrandTotals.seenClient[clientAddr] = true
+ unfilteredGrandTotals.TotalBytes += int64(dealInfo.Proposal.PieceSize)
+ unfilteredGrandTotals.seenProvider[dealInfo.Proposal.Provider] = true
+ unfilteredGrandTotals.seenPieceCid[dealInfo.Proposal.PieceCID] = true
+ unfilteredGrandTotals.TotalDeals++
+
+ projID, projKnown := knownAddrMap[clientAddr]
+ if !projKnown {
+ continue
+ }
+
+ grandTotals.seenProject[projID] = true
+ grandTotals.seenClient[clientAddr] = true
+
+ projStatEntry, ok := projStats[projID]
+ if !ok {
+ projStatEntry = &projectAggregateStats{
+ ProjectID: projID,
+ ClientStats: make(map[string]*clientAggregateStats),
+ cidDeals: make(map[cid.Cid]int),
+ dataPerProvider: make(map[address.Address]int64),
+ }
+ projStats[projID] = projStatEntry
+ }
+
+ clientStatEntry, ok := projStatEntry.ClientStats[clientAddr.String()]
+ if !ok {
+ clientStatEntry = &clientAggregateStats{
+ Client: clientAddr.String(),
+ cids: make(map[cid.Cid]bool),
+ providers: make(map[address.Address]bool),
+ }
+ projStatEntry.ClientStats[clientAddr.String()] = clientStatEntry
+ }
+
+ grandTotals.TotalBytes += int64(dealInfo.Proposal.PieceSize)
+ projStatEntry.DataSize += int64(dealInfo.Proposal.PieceSize)
+ clientStatEntry.DataSize += int64(dealInfo.Proposal.PieceSize)
+
+ grandTotals.seenProvider[dealInfo.Proposal.Provider] = true
+ projStatEntry.dataPerProvider[dealInfo.Proposal.Provider] += int64(dealInfo.Proposal.PieceSize)
+ clientStatEntry.providers[dealInfo.Proposal.Provider] = true
+
+ grandTotals.seenPieceCid[dealInfo.Proposal.PieceCID] = true
+ projStatEntry.cidDeals[dealInfo.Proposal.PieceCID]++
+ clientStatEntry.cids[dealInfo.Proposal.PieceCID] = true
+
+ grandTotals.TotalDeals++
+ projStatEntry.NumDeals++
+ clientStatEntry.NumDeals++
+
+ payloadCid := "unknown"
+ if c, err := cid.Parse(dealInfo.Proposal.Label); err == nil {
+ payloadCid = c.String()
+ }
+
+ projDealLists[projID] = append(projDealLists[projID], &individualDeal{
+ DealID: dealID,
+ ProjectID: projID,
+ Client: clientAddr.String(),
+ MinerID: dealInfo.Proposal.Provider.String(),
+ PayloadCID: payloadCid,
+ PaddedSize: int64(dealInfo.Proposal.PieceSize),
+ DealStartEpoch: int64(dealInfo.State.SectorStartEpoch),
+ })
+ }
+
+ //
+ // Write out per-project deal lists
+ for proj, dl := range projDealLists {
+ err := func() error {
+ outListFd, err := os.Create(fmt.Sprintf(outDirName+"/deals_list_%s.json", proj))
+ if err != nil {
+ return err
+ }
+
+ defer outListFd.Close() //nolint:errcheck
+
+ ridiculousLintMandatedRebind := dl
+ sort.Slice(dl, func(i, j int) bool {
+ return ridiculousLintMandatedRebind[j].PaddedSize < ridiculousLintMandatedRebind[i].PaddedSize
+ })
+
+ if err := json.NewEncoder(outListFd).Encode(
+ dealListOutput{
+ Epoch: int64(head.Height()),
+ Endpoint: "DEAL_LIST",
+ Payload: dl,
+ },
+ ); err != nil {
+ return err
+ }
+
+ return nil
+ }()
+
+ if err != nil {
+ return err
+ }
+ }
+
+ //
+ // write out basic_stats.json and unfiltered_basic_stats.json
+ for _, st := range []*competitionTotal{&grandTotals, &unfilteredGrandTotals} {
+ st.UniqueCids = len(st.seenPieceCid)
+ st.UniqueClients = len(st.seenClient)
+ st.UniqueProviders = len(st.seenProvider)
+ if st.seenProject != nil {
+ st.UniqueProjects = len(st.seenProject)
+ }
+ }
+
+ if err := json.NewEncoder(outBasicStatsFd).Encode(
+ competitionTotalOutput{
+ Epoch: int64(head.Height()),
+ Endpoint: "COMPETITION_TOTALS",
+ Payload: grandTotals,
+ },
+ ); err != nil {
+ return err
+ }
+
+ if err := json.NewEncoder(outUnfilteredStatsFd).Encode(
+ competitionTotalOutput{
+ Epoch: int64(head.Height()),
+ Endpoint: "NETWORK_WIDE_TOTALS",
+ Payload: unfilteredGrandTotals,
+ },
+ ); err != nil {
+ return err
+ }
+
+ //
+ // write out client_stats.json
+ for _, ps := range projStats {
+ ps.NumCids = len(ps.cidDeals)
+ ps.NumProviders = len(ps.dataPerProvider)
+ for _, dealsForCid := range ps.cidDeals {
+ if ps.HighestCidDealCount < dealsForCid {
+ ps.HighestCidDealCount = dealsForCid
+ }
+ }
+ for _, dataForProvider := range ps.dataPerProvider {
+ if ps.DataSizeMaxProvider < dataForProvider {
+ ps.DataSizeMaxProvider = dataForProvider
+ }
+ }
+
+ for _, cs := range ps.ClientStats {
+ cs.NumCids = len(cs.cids)
+ cs.NumProviders = len(cs.providers)
+ }
+ }
+
+ if err := json.NewEncoder(outClientStatsFd).Encode(
+ projectAggregateStatsOutput{
+ Epoch: int64(head.Height()),
+ Endpoint: "PROJECT_DEAL_STATS",
+ Payload: projStats,
+ },
+ ); err != nil {
+ return err
+ }
+
+ return nil
+ },
+}
diff --git a/cmd/lotus-storage-miner/actor.go b/cmd/lotus-storage-miner/actor.go
index 69486eaf5..dcf3d35c0 100644
--- a/cmd/lotus-storage-miner/actor.go
+++ b/cmd/lotus-storage-miner/actor.go
@@ -414,7 +414,7 @@ var actorControlList = &cli.Command{
tablewriter.Col("balance"),
)
- postAddr, err := storage.AddressFor(ctx, api, mi, storage.PoStAddr, types.FromFil(1))
+ postAddr, _, err := storage.AddressFor(ctx, api, mi, storage.PoStAddr, types.FromFil(1), types.FromFil(1))
if err != nil {
return xerrors.Errorf("getting address for post: %w", err)
}
diff --git a/cmd/lotus-storage-miner/allinfo_test.go b/cmd/lotus-storage-miner/allinfo_test.go
index a458c024b..51aba14a9 100644
--- a/cmd/lotus-storage-miner/allinfo_test.go
+++ b/cmd/lotus-storage-miner/allinfo_test.go
@@ -70,7 +70,7 @@ func TestMinerAllInfo(t *testing.T) {
return n, sn
}
- test.TestDealFlow(t, bp, time.Second, false, false)
+ test.TestDealFlow(t, bp, time.Second, false, false, 0)
t.Run("post-info-all", run)
}
diff --git a/cmd/lotus-storage-miner/info.go b/cmd/lotus-storage-miner/info.go
index b16b4dde8..7bedd2b94 100644
--- a/cmd/lotus-storage-miner/info.go
+++ b/cmd/lotus-storage-miner/info.go
@@ -14,6 +14,7 @@ import (
"github.com/filecoin-project/go-fil-markets/storagemarket"
"github.com/filecoin-project/go-state-types/abi"
+ "github.com/filecoin-project/go-state-types/big"
sealing "github.com/filecoin-project/lotus/extern/storage-sealing"
"github.com/filecoin-project/lotus/api"
@@ -59,7 +60,7 @@ func infoCmdAct(cctx *cli.Context) error {
ctx := lcli.ReqContext(cctx)
- fmt.Print("Full node: ")
+ fmt.Print("Chain: ")
head, err := api.ChainHead(ctx)
if err != nil {
@@ -75,6 +76,20 @@ func infoCmdAct(cctx *cli.Context) error {
fmt.Printf("[%s]", color.RedString("sync behind! (%s behind)", time.Now().Sub(time.Unix(int64(head.MinTimestamp()), 0)).Truncate(time.Second)))
}
+ basefee := head.MinTicketBlock().ParentBaseFee
+ gasCol := []color.Attribute{color.FgBlue}
+ switch {
+ case basefee.GreaterThan(big.NewInt(7000_000_000)): // 7 nFIL
+ gasCol = []color.Attribute{color.BgRed, color.FgBlack}
+ case basefee.GreaterThan(big.NewInt(3000_000_000)): // 3 nFIL
+ gasCol = []color.Attribute{color.FgRed}
+ case basefee.GreaterThan(big.NewInt(750_000_000)): // 750 uFIL
+ gasCol = []color.Attribute{color.FgYellow}
+ case basefee.GreaterThan(big.NewInt(100_000_000)): // 100 uFIL
+ gasCol = []color.Attribute{color.FgGreen}
+ }
+ fmt.Printf(" [basefee %s]", color.New(gasCol...).Sprint(types.FIL(basefee).Short()))
+
fmt.Println()
maddr, err := getActorAddress(ctx, nodeApi, cctx.String("actor"))
@@ -93,15 +108,14 @@ func infoCmdAct(cctx *cli.Context) error {
return err
}
- fmt.Printf("Miner: %s\n", color.BlueString("%s", maddr))
-
// Sector size
mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK)
if err != nil {
return err
}
- fmt.Printf("Sector Size: %s\n", types.SizeStr(types.NewInt(uint64(mi.SectorSize))))
+ ssize := types.SizeStr(types.NewInt(uint64(mi.SectorSize)))
+ fmt.Printf("Miner: %s (%s sectors)\n", color.BlueString("%s", maddr), ssize)
pow, err := api.StateMinerPower(ctx, maddr, types.EmptyTSK)
if err != nil {
@@ -111,16 +125,16 @@ func infoCmdAct(cctx *cli.Context) error {
rpercI := types.BigDiv(types.BigMul(pow.MinerPower.RawBytePower, types.NewInt(1000000)), pow.TotalPower.RawBytePower)
qpercI := types.BigDiv(types.BigMul(pow.MinerPower.QualityAdjPower, types.NewInt(1000000)), pow.TotalPower.QualityAdjPower)
- fmt.Printf("Byte Power: %s / %s (%0.4f%%)\n",
- color.BlueString(types.SizeStr(pow.MinerPower.RawBytePower)),
- types.SizeStr(pow.TotalPower.RawBytePower),
- float64(rpercI.Int64())/10000)
-
- fmt.Printf("Actual Power: %s / %s (%0.4f%%)\n",
+ fmt.Printf("Power: %s / %s (%0.4f%%)\n",
color.GreenString(types.DeciStr(pow.MinerPower.QualityAdjPower)),
types.DeciStr(pow.TotalPower.QualityAdjPower),
float64(qpercI.Int64())/10000)
+ fmt.Printf("\tRaw: %s / %s (%0.4f%%)\n",
+ color.BlueString(types.SizeStr(pow.MinerPower.RawBytePower)),
+ types.SizeStr(pow.TotalPower.RawBytePower),
+ float64(rpercI.Int64())/10000)
+
secCounts, err := api.StateMinerSectorCount(ctx, maddr, types.EmptyTSK)
if err != nil {
return err
@@ -168,6 +182,10 @@ func infoCmdAct(cctx *cli.Context) error {
var nactiveDeals, nVerifDeals, ndeals uint64
var activeDealBytes, activeVerifDealBytes, dealBytes abi.PaddedPieceSize
for _, deal := range deals {
+ if deal.State == storagemarket.StorageDealError {
+ continue
+ }
+
ndeals++
dealBytes += deal.Proposal.PieceSize
@@ -186,6 +204,8 @@ func infoCmdAct(cctx *cli.Context) error {
fmt.Printf("\tActive: %d, %s (Verified: %d, %s)\n", nactiveDeals, types.SizeStr(types.NewInt(uint64(activeDealBytes))), nVerifDeals, types.SizeStr(types.NewInt(uint64(activeVerifDealBytes))))
fmt.Println()
+ spendable := big.Zero()
+
// NOTE: there's no need to unlock anything here. Funds only
// vest on deadline boundaries, and they're unlocked by cron.
lockedFunds, err := mas.LockedFunds()
@@ -196,33 +216,47 @@ func infoCmdAct(cctx *cli.Context) error {
if err != nil {
return xerrors.Errorf("getting available balance: %w", err)
}
- fmt.Printf("Miner Balance: %s\n", color.YellowString("%s", types.FIL(mact.Balance)))
- fmt.Printf("\tPreCommit: %s\n", types.FIL(lockedFunds.PreCommitDeposits))
- fmt.Printf("\tPledge: %s\n", types.FIL(lockedFunds.InitialPledgeRequirement))
- fmt.Printf("\tVesting: %s\n", types.FIL(lockedFunds.VestingFunds))
- color.Green("\tAvailable: %s", types.FIL(availBalance))
- wb, err := api.WalletBalance(ctx, mi.Worker)
- if err != nil {
- return xerrors.Errorf("getting worker balance: %w", err)
- }
- color.Cyan("Worker Balance: %s", types.FIL(wb))
+ spendable = big.Add(spendable, availBalance)
+
+ fmt.Printf("Miner Balance: %s\n", color.YellowString("%s", types.FIL(mact.Balance).Short()))
+ fmt.Printf(" PreCommit: %s\n", types.FIL(lockedFunds.PreCommitDeposits).Short())
+ fmt.Printf(" Pledge: %s\n", types.FIL(lockedFunds.InitialPledgeRequirement).Short())
+ fmt.Printf(" Vesting: %s\n", types.FIL(lockedFunds.VestingFunds).Short())
+ color.Green(" Available: %s", types.FIL(availBalance).Short())
mb, err := api.StateMarketBalance(ctx, maddr, types.EmptyTSK)
if err != nil {
return xerrors.Errorf("getting market balance: %w", err)
}
- fmt.Printf("Market (Escrow): %s\n", types.FIL(mb.Escrow))
- fmt.Printf("Market (Locked): %s\n", types.FIL(mb.Locked))
+ spendable = big.Add(spendable, big.Sub(mb.Escrow, mb.Locked))
+
+ fmt.Printf("Market Balance: %s\n", types.FIL(mb.Escrow).Short())
+ fmt.Printf(" Locked: %s\n", types.FIL(mb.Locked).Short())
+ color.Green(" Available: %s\n", types.FIL(big.Sub(mb.Escrow, mb.Locked)).Short())
+
+ wb, err := api.WalletBalance(ctx, mi.Worker)
+ if err != nil {
+ return xerrors.Errorf("getting worker balance: %w", err)
+ }
+ spendable = big.Add(spendable, wb)
+ color.Cyan("Worker Balance: %s", types.FIL(wb).Short())
+ if len(mi.ControlAddresses) > 0 {
+ cbsum := big.Zero()
+ for _, ca := range mi.ControlAddresses {
+ b, err := api.WalletBalance(ctx, ca)
+ if err != nil {
+ return xerrors.Errorf("getting control address balance: %w", err)
+ }
+ cbsum = big.Add(cbsum, b)
+ }
+ spendable = big.Add(spendable, cbsum)
+
+ fmt.Printf(" Control: %s\n", types.FIL(cbsum).Short())
+ }
+ fmt.Printf("Total Spendable: %s\n", color.YellowString(types.FIL(spendable).Short()))
fmt.Println()
- sealdur, err := nodeApi.SectorGetExpectedSealDuration(ctx)
- if err != nil {
- return err
- }
-
- fmt.Printf("Expected Seal Duration: %s\n\n", sealdur)
-
if !cctx.Bool("hide-sectors-info") {
fmt.Println("Sectors:")
err = sectorsInfo(ctx, nodeApi)
@@ -253,6 +287,7 @@ var stateList = []stateMeta{
{col: color.FgRed, state: sealing.UndefinedSectorState},
{col: color.FgYellow, state: sealing.Packing},
+ {col: color.FgYellow, state: sealing.GetTicket},
{col: color.FgYellow, state: sealing.PreCommit1},
{col: color.FgYellow, state: sealing.PreCommit2},
{col: color.FgYellow, state: sealing.PreCommitting},
diff --git a/cmd/lotus-storage-miner/market.go b/cmd/lotus-storage-miner/market.go
index be4a529e9..15a3c8b64 100644
--- a/cmd/lotus-storage-miner/market.go
+++ b/cmd/lotus-storage-miner/market.go
@@ -2,6 +2,7 @@ package main
import (
"bufio"
+ "context"
"errors"
"fmt"
"io"
@@ -650,6 +651,11 @@ var marketCancelTransfer = &cli.Command{
Usage: "specify only transfers where peer is/is not initiator",
Value: false,
},
+ &cli.DurationFlag{
+ Name: "cancel-timeout",
+ Usage: "time to wait for cancel to be sent to client",
+ Value: 5 * time.Second,
+ },
},
Action: func(cctx *cli.Context) error {
if !cctx.Args().Present() {
@@ -693,7 +699,9 @@ var marketCancelTransfer = &cli.Command{
}
}
- return nodeApi.MarketCancelDataTransfer(ctx, transferID, other, initiator)
+ timeoutCtx, cancel := context.WithTimeout(ctx, cctx.Duration("cancel-timeout"))
+ defer cancel()
+ return nodeApi.MarketCancelDataTransfer(timeoutCtx, transferID, other, initiator)
},
}
diff --git a/cmd/lotus-storage-miner/proving.go b/cmd/lotus-storage-miner/proving.go
index 377b81d32..5d176a754 100644
--- a/cmd/lotus-storage-miner/proving.go
+++ b/cmd/lotus-storage-miner/proving.go
@@ -10,11 +10,14 @@ import (
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
+ "github.com/filecoin-project/go-address"
+ "github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/api/apibstore"
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
lcli "github.com/filecoin-project/lotus/cli"
+ "github.com/filecoin-project/specs-storage/storage"
)
var provingCmd = &cli.Command{
@@ -25,6 +28,7 @@ var provingCmd = &cli.Command{
provingDeadlinesCmd,
provingDeadlineInfoCmd,
provingFaultsCmd,
+ provingCheckProvableCmd,
},
}
@@ -371,3 +375,108 @@ var provingDeadlineInfoCmd = &cli.Command{
return nil
},
}
+
+var provingCheckProvableCmd = &cli.Command{
+ Name: "check",
+ Usage: "Check sectors provable",
+ ArgsUsage: "",
+ Flags: []cli.Flag{
+ &cli.BoolFlag{
+ Name: "only-bad",
+ Usage: "print only bad sectors",
+ Value: false,
+ },
+ &cli.BoolFlag{
+ Name: "slow",
+ Usage: "run slower checks",
+ },
+ },
+ Action: func(cctx *cli.Context) error {
+ if cctx.Args().Len() != 1 {
+ return xerrors.Errorf("must pass deadline index")
+ }
+
+ dlIdx, err := strconv.ParseUint(cctx.Args().Get(0), 10, 64)
+ if err != nil {
+ return xerrors.Errorf("could not parse deadline index: %w", err)
+ }
+
+ api, closer, err := lcli.GetFullNodeAPI(cctx)
+ if err != nil {
+ return err
+ }
+ defer closer()
+
+ sapi, scloser, err := lcli.GetStorageMinerAPI(cctx)
+ if err != nil {
+ return err
+ }
+ defer scloser()
+
+ ctx := lcli.ReqContext(cctx)
+
+ addr, err := sapi.ActorAddress(ctx)
+ if err != nil {
+ return err
+ }
+
+ mid, err := address.IDFromAddress(addr)
+ if err != nil {
+ return err
+ }
+
+ info, err := api.StateMinerInfo(ctx, addr, types.EmptyTSK)
+ if err != nil {
+ return err
+ }
+
+ pf, err := info.SealProofType.RegisteredWindowPoStProof()
+ if err != nil {
+ return err
+ }
+
+ partitions, err := api.StateMinerPartitions(ctx, addr, dlIdx, types.EmptyTSK)
+ if err != nil {
+ return err
+ }
+
+ tw := tabwriter.NewWriter(os.Stdout, 2, 4, 2, ' ', 0)
+ _, _ = fmt.Fprintln(tw, "deadline\tpartition\tsector\tstatus")
+
+ for parIdx, par := range partitions {
+ sectors := make(map[abi.SectorNumber]struct{})
+
+ sectorInfos, err := api.StateMinerSectors(ctx, addr, &par.AllSectors, types.EmptyTSK)
+ if err != nil {
+ return err
+ }
+
+ var tocheck []storage.SectorRef
+ for _, info := range sectorInfos {
+ sectors[info.SectorNumber] = struct{}{}
+ tocheck = append(tocheck, storage.SectorRef{
+ ProofType: info.SealProof,
+ ID: abi.SectorID{
+ Miner: abi.ActorID(mid),
+ Number: info.SectorNumber,
+ },
+ })
+ }
+
+ bad, err := sapi.CheckProvable(ctx, pf, tocheck, cctx.Bool("slow"))
+ if err != nil {
+ return err
+ }
+
+ for s := range sectors {
+ if err, exist := bad[s]; exist {
+ _, _ = fmt.Fprintf(tw, "%d\t%d\t%d\t%s\n", dlIdx, parIdx, s, color.RedString("bad")+fmt.Sprintf(" (%s)", err))
+ } else if !cctx.Bool("only-bad") {
+ _, _ = fmt.Fprintf(tw, "%d\t%d\t%d\t%s\n", dlIdx, parIdx, s, color.GreenString("good"))
+ }
+ }
+ }
+
+ return tw.Flush()
+ },
+}
diff --git a/cmd/lotus/daemon.go b/cmd/lotus/daemon.go
index 1d13b4082..581238d4b 100644
--- a/cmd/lotus/daemon.go
+++ b/cmd/lotus/daemon.go
@@ -136,6 +136,14 @@ var DaemonCmd = &cli.Command{
Name: "config",
Usage: "specify path of config file to use",
},
+ // FIXME: This is not the correct place to put this configuration
+ // option. Ideally it would be part of `config.toml` but at the
+ // moment that only applies to the node configuration and not outside
+ // components like the RPC server.
+ &cli.IntFlag{
+ Name: "api-max-req-size",
+ Usage: "maximum API request size accepted by the JSON RPC server",
+ },
},
Action: func(cctx *cli.Context) error {
isLite := cctx.Bool("lite")
@@ -321,7 +329,7 @@ var DaemonCmd = &cli.Command{
}
// TODO: properly parse api endpoint (or make it a URL)
- return serveRPC(api, stop, endpoint, shutdownChan)
+ return serveRPC(api, stop, endpoint, shutdownChan, int64(cctx.Int("api-max-req-size")))
},
Subcommands: []*cli.Command{
daemonStopCmd,
@@ -358,7 +366,7 @@ func importKey(ctx context.Context, api api.FullNode, f string) error {
return err
}
- log.Info("successfully imported key for %s", addr)
+ log.Infof("successfully imported key for %s", addr)
return nil
}
diff --git a/cmd/lotus/pprof.go b/cmd/lotus/pprof.go
new file mode 100644
index 000000000..ea6823e48
--- /dev/null
+++ b/cmd/lotus/pprof.go
@@ -0,0 +1,33 @@
+package main
+
+import (
+ "net/http"
+ "strconv"
+)
+
+func handleFractionOpt(name string, setter func(int)) http.HandlerFunc {
+ return func(rw http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodPost {
+ http.Error(rw, "only POST allowed", http.StatusMethodNotAllowed)
+ return
+ }
+ if err := r.ParseForm(); err != nil {
+ http.Error(rw, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ asfr := r.Form.Get("x")
+ if len(asfr) == 0 {
+ http.Error(rw, "parameter 'x' must be set", http.StatusBadRequest)
+ return
+ }
+
+ fr, err := strconv.Atoi(asfr)
+ if err != nil {
+ http.Error(rw, err.Error(), http.StatusBadRequest)
+ return
+ }
+ log.Infof("setting %s to %d", name, fr)
+ setter(fr)
+ }
+}
diff --git a/cmd/lotus/rpc.go b/cmd/lotus/rpc.go
index f2c59b615..82a1fb480 100644
--- a/cmd/lotus/rpc.go
+++ b/cmd/lotus/rpc.go
@@ -8,6 +8,7 @@ import (
_ "net/http/pprof"
"os"
"os/signal"
+ "runtime"
"syscall"
"github.com/ipfs/go-cid"
@@ -32,8 +33,12 @@ import (
var log = logging.Logger("main")
-func serveRPC(a api.FullNode, stop node.StopFunc, addr multiaddr.Multiaddr, shutdownCh <-chan struct{}) error {
- rpcServer := jsonrpc.NewServer()
+func serveRPC(a api.FullNode, stop node.StopFunc, addr multiaddr.Multiaddr, shutdownCh <-chan struct{}, maxRequestSize int64) error {
+ serverOptions := make([]jsonrpc.ServerOption, 0)
+ if maxRequestSize != 0 { // config set
+ serverOptions = append(serverOptions, jsonrpc.WithMaxRequestSize(maxRequestSize))
+ }
+ rpcServer := jsonrpc.NewServer(serverOptions...)
rpcServer.Register("Filecoin", apistruct.PermissionedFullAPI(metrics.MetricedFullAPI(a)))
ah := &auth.Handler{
@@ -67,6 +72,10 @@ func serveRPC(a api.FullNode, stop node.StopFunc, addr multiaddr.Multiaddr, shut
}
http.Handle("/debug/metrics", exporter)
+ http.Handle("/debug/pprof-set/block", handleFractionOpt("BlockProfileRate", runtime.SetBlockProfileRate))
+ http.Handle("/debug/pprof-set/mutex", handleFractionOpt("MutexProfileFraction",
+ func(x int) { runtime.SetMutexProfileFraction(x) },
+ ))
lst, err := manet.Listen(addr)
if err != nil {
diff --git a/cmd/tvx/extract.go b/cmd/tvx/extract.go
index 3dfec37d8..894fa0fbc 100644
--- a/cmd/tvx/extract.go
+++ b/cmd/tvx/extract.go
@@ -12,6 +12,7 @@ import (
"path/filepath"
"github.com/fatih/color"
+ "github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/actors/builtin"
@@ -70,6 +71,11 @@ var extractCmd = &cli.Command{
Usage: "optionally, the block CID the message was included in, to avoid expensive chain scanning",
Destination: &extractFlags.block,
},
+ &cli.StringFlag{
+ Name: "exec-block",
+ Usage: "optionally, the block CID of a block where this message was executed, to avoid expensive chain scanning",
+ Destination: &extractFlags.block,
+ },
&cli.StringFlag{
Name: "cid",
Usage: "message CID to generate test vector from",
@@ -143,7 +149,7 @@ func doExtract(opts extractOpts) error {
return fmt.Errorf("failed to fetch messages in canonical order from inclusion tipset: %w", err)
}
- related, found, err := findMsgAndPrecursors(opts.precursor, msg, msgs)
+ related, found, err := findMsgAndPrecursors(opts.precursor, mcid, msg.From, msgs)
if err != nil {
return fmt.Errorf("failed while finding message and precursors: %w", err)
}
@@ -496,19 +502,19 @@ func fetchThisAndPrevTipset(ctx context.Context, api api.FullNode, target types.
// findMsgAndPrecursors ranges through the canonical messages slice, locating
// the target message and returning precursors in accordance to the supplied
// mode.
-func findMsgAndPrecursors(mode string, target *types.Message, msgs []api.Message) (related []*types.Message, found bool, err error) {
+func findMsgAndPrecursors(mode string, msgCid cid.Cid, sender address.Address, msgs []api.Message) (related []*types.Message, found bool, err error) {
// Range through canonicalised messages, selecting only the precursors based
// on selection mode.
for _, other := range msgs {
switch {
case mode == PrecursorSelectAll:
fallthrough
- case mode == PrecursorSelectSender && other.Message.From == target.From:
+ case mode == PrecursorSelectSender && other.Message.From == sender:
related = append(related, other.Message)
}
// this message is the target; we're done.
- if other.Cid == target.Cid() {
+ if other.Cid == msgCid {
return related, true, nil
}
}
diff --git a/documentation/en/api-methods-miner.md b/documentation/en/api-methods-miner.md
index 09c340c3a..d3f9880aa 100644
--- a/documentation/en/api-methods-miner.md
+++ b/documentation/en/api-methods-miner.md
@@ -10,6 +10,8 @@
* [Auth](#Auth)
* [AuthNew](#AuthNew)
* [AuthVerify](#AuthVerify)
+* [Check](#Check)
+ * [CheckProvable](#CheckProvable)
* [Create](#Create)
* [CreateBackup](#CreateBackup)
* [Deals](#Deals)
@@ -53,6 +55,9 @@
* [NetBandwidthStats](#NetBandwidthStats)
* [NetBandwidthStatsByPeer](#NetBandwidthStatsByPeer)
* [NetBandwidthStatsByProtocol](#NetBandwidthStatsByProtocol)
+ * [NetBlockAdd](#NetBlockAdd)
+ * [NetBlockList](#NetBlockList)
+ * [NetBlockRemove](#NetBlockRemove)
* [NetConnect](#NetConnect)
* [NetConnectedness](#NetConnectedness)
* [NetDisconnect](#NetDisconnect)
@@ -215,6 +220,30 @@ Inputs:
Response: `null`
+## Check
+
+
+### CheckProvable
+There are not yet any comments for this method.
+
+Perms: admin
+
+Inputs:
+```json
+[
+ 8,
+ null,
+ true
+]
+```
+
+Response:
+```json
+{
+ "123": "can't acquire read lock"
+}
+```
+
## Create
@@ -549,7 +578,8 @@ Response:
"Initiator": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf",
"Responder": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf",
"ID": 3
- }
+ },
+ "SectorNumber": 9
}
```
@@ -798,6 +828,58 @@ Response:
}
```
+### NetBlockAdd
+
+
+Perms: admin
+
+Inputs:
+```json
+[
+ {
+ "Peers": null,
+ "IPAddrs": null,
+ "IPSubnets": null
+ }
+]
+```
+
+Response: `{}`
+
+### NetBlockList
+
+
+Perms: read
+
+Inputs: `null`
+
+Response:
+```json
+{
+ "Peers": null,
+ "IPAddrs": null,
+ "IPSubnets": null
+}
+```
+
+### NetBlockRemove
+
+
+Perms: admin
+
+Inputs:
+```json
+[
+ {
+ "Peers": null,
+ "IPAddrs": null,
+ "IPSubnets": null
+ }
+]
+```
+
+Response: `{}`
+
### NetConnect
diff --git a/documentation/en/api-methods-worker.md b/documentation/en/api-methods-worker.md
index 7e4fe5e9c..2224a73f9 100644
--- a/documentation/en/api-methods-worker.md
+++ b/documentation/en/api-methods-worker.md
@@ -29,6 +29,8 @@
* [Storage](#Storage)
* [StorageAddLocal](#StorageAddLocal)
* [Task](#Task)
+ * [TaskDisable](#TaskDisable)
+ * [TaskEnable](#TaskEnable)
* [TaskTypes](#TaskTypes)
* [Unseal](#Unseal)
* [UnsealPiece](#UnsealPiece)
@@ -502,6 +504,34 @@ Response: `{}`
## Task
+### TaskDisable
+There are not yet any comments for this method.
+
+Perms: admin
+
+Inputs:
+```json
+[
+ "seal/v0/commit/2"
+]
+```
+
+Response: `{}`
+
+### TaskEnable
+There are not yet any comments for this method.
+
+Perms: admin
+
+Inputs:
+```json
+[
+ "seal/v0/commit/2"
+]
+```
+
+Response: `{}`
+
### TaskTypes
TaskType -> Weight
diff --git a/documentation/en/api-methods.md b/documentation/en/api-methods.md
index 8db940007..04998b27e 100644
--- a/documentation/en/api-methods.md
+++ b/documentation/en/api-methods.md
@@ -110,6 +110,9 @@
* [NetBandwidthStats](#NetBandwidthStats)
* [NetBandwidthStatsByPeer](#NetBandwidthStatsByPeer)
* [NetBandwidthStatsByProtocol](#NetBandwidthStatsByProtocol)
+ * [NetBlockAdd](#NetBlockAdd)
+ * [NetBlockList](#NetBlockList)
+ * [NetBlockRemove](#NetBlockRemove)
* [NetConnect](#NetConnect)
* [NetConnectedness](#NetConnectedness)
* [NetDisconnect](#NetDisconnect)
@@ -2639,6 +2642,58 @@ Response:
}
```
+### NetBlockAdd
+
+
+Perms: admin
+
+Inputs:
+```json
+[
+ {
+ "Peers": null,
+ "IPAddrs": null,
+ "IPSubnets": null
+ }
+]
+```
+
+Response: `{}`
+
+### NetBlockList
+
+
+Perms: read
+
+Inputs: `null`
+
+Response:
+```json
+{
+ "Peers": null,
+ "IPAddrs": null,
+ "IPSubnets": null
+}
+```
+
+### NetBlockRemove
+
+
+Perms: admin
+
+Inputs:
+```json
+[
+ {
+ "Peers": null,
+ "IPAddrs": null,
+ "IPSubnets": null
+ }
+]
+```
+
+Response: `{}`
+
### NetConnect
diff --git a/extern/oni b/extern/oni
deleted file mode 160000
index 10ed9ef57..000000000
--- a/extern/oni
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 10ed9ef576836186de3b8513c03cdc3fb18c44ed
diff --git a/extern/sector-storage/faults.go b/extern/sector-storage/faults.go
index 6f0dcfa13..fdd5f6b7d 100644
--- a/extern/sector-storage/faults.go
+++ b/extern/sector-storage/faults.go
@@ -2,13 +2,16 @@ package sectorstorage
import (
"context"
+ "crypto/rand"
"fmt"
"os"
"path/filepath"
"golang.org/x/xerrors"
+ ffi "github.com/filecoin-project/filecoin-ffi"
"github.com/filecoin-project/go-state-types/abi"
+ "github.com/filecoin-project/specs-actors/actors/runtime/proof"
"github.com/filecoin-project/specs-storage/storage"
"github.com/filecoin-project/lotus/extern/sector-storage/storiface"
@@ -16,12 +19,12 @@ import (
// FaultTracker TODO: Track things more actively
type FaultTracker interface {
- CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof, sectors []storage.SectorRef) ([]abi.SectorID, error)
+ CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof, sectors []storage.SectorRef, rg storiface.RGetter) (map[abi.SectorID]string, error)
}
// CheckProvable returns unprovable sectors
-func (m *Manager) CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof, sectors []storage.SectorRef) ([]abi.SectorID, error) {
- var bad []abi.SectorID
+func (m *Manager) CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof, sectors []storage.SectorRef, rg storiface.RGetter) (map[abi.SectorID]string, error) {
+ var bad = make(map[abi.SectorID]string)
ssize, err := pp.SectorSize()
if err != nil {
@@ -40,21 +43,21 @@ func (m *Manager) CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof,
}
if !locked {
- log.Warnw("CheckProvable Sector FAULT: can't acquire read lock", "sector", sector, "sealed")
- bad = append(bad, sector.ID)
+ log.Warnw("CheckProvable Sector FAULT: can't acquire read lock", "sector", sector)
+ bad[sector.ID] = fmt.Sprint("can't acquire read lock")
return nil
}
lp, _, err := m.localStore.AcquireSector(ctx, sector, storiface.FTSealed|storiface.FTCache, storiface.FTNone, storiface.PathStorage, storiface.AcquireMove)
if err != nil {
log.Warnw("CheckProvable Sector FAULT: acquire sector in checkProvable", "sector", sector, "error", err)
- bad = append(bad, sector.ID)
+ bad[sector.ID] = fmt.Sprintf("acquire sector failed: %s", err)
return nil
}
if lp.Sealed == "" || lp.Cache == "" {
- log.Warnw("CheckProvable Sector FAULT: cache an/or sealed paths not found", "sector", sector, "sealed", lp.Sealed, "cache", lp.Cache)
- bad = append(bad, sector.ID)
+ log.Warnw("CheckProvable Sector FAULT: cache and/or sealed paths not found", "sector", sector, "sealed", lp.Sealed, "cache", lp.Cache)
+ bad[sector.ID] = fmt.Sprintf("cache and/or sealed paths not found, cache %q, sealed %q", lp.Cache, lp.Sealed)
return nil
}
@@ -70,19 +73,62 @@ func (m *Manager) CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof,
st, err := os.Stat(p)
if err != nil {
log.Warnw("CheckProvable Sector FAULT: sector file stat error", "sector", sector, "sealed", lp.Sealed, "cache", lp.Cache, "file", p, "err", err)
- bad = append(bad, sector.ID)
+ bad[sector.ID] = fmt.Sprintf("%s", err)
return nil
}
if sz != 0 {
if st.Size() != int64(ssize)*sz {
log.Warnw("CheckProvable Sector FAULT: sector file is wrong size", "sector", sector, "sealed", lp.Sealed, "cache", lp.Cache, "file", p, "size", st.Size(), "expectSize", int64(ssize)*sz)
- bad = append(bad, sector.ID)
+ bad[sector.ID] = fmt.Sprintf("%s is wrong size (got %d, expect %d)", p, st.Size(), int64(ssize)*sz)
return nil
}
}
}
+ if rg != nil {
+ wpp, err := sector.ProofType.RegisteredWindowPoStProof()
+ if err != nil {
+ return err
+ }
+
+ var pr abi.PoStRandomness = make([]byte, abi.RandomnessLength)
+ _, _ = rand.Read(pr)
+ pr[31] &= 0x3f
+
+ ch, err := ffi.GeneratePoStFallbackSectorChallenges(wpp, sector.ID.Miner, pr, []abi.SectorNumber{
+ sector.ID.Number,
+ })
+ if err != nil {
+ log.Warnw("CheckProvable Sector FAULT: generating challenges", "sector", sector, "sealed", lp.Sealed, "cache", lp.Cache, "err", err)
+ bad[sector.ID] = fmt.Sprintf("generating fallback challenges: %s", err)
+ return nil
+ }
+
+ commr, err := rg(ctx, sector.ID)
+ if err != nil {
+ log.Warnw("CheckProvable Sector FAULT: getting commR", "sector", sector, "sealed", lp.Sealed, "cache", lp.Cache, "err", err)
+ bad[sector.ID] = fmt.Sprintf("getting commR: %s", err)
+ return nil
+ }
+
+ _, err = ffi.GenerateSingleVanillaProof(ffi.PrivateSectorInfo{
+ SectorInfo: proof.SectorInfo{
+ SealProof: sector.ProofType,
+ SectorNumber: sector.ID.Number,
+ SealedCID: commr,
+ },
+ CacheDirPath: lp.Cache,
+ PoStProofType: wpp,
+ SealedSectorPath: lp.Sealed,
+ }, ch.Challenges[sector.ID.Number])
+ if err != nil {
+ log.Warnw("CheckProvable Sector FAULT: generating vanilla proof", "sector", sector, "sealed", lp.Sealed, "cache", lp.Cache, "err", err)
+ bad[sector.ID] = fmt.Sprintf("generating vanilla proof: %s", err)
+ return nil
+ }
+ }
+
return nil
}()
if err != nil {
diff --git a/extern/sector-storage/ffiwrapper/files.go b/extern/sector-storage/ffiwrapper/files.go
deleted file mode 100644
index a13776d2d..000000000
--- a/extern/sector-storage/ffiwrapper/files.go
+++ /dev/null
@@ -1,53 +0,0 @@
-package ffiwrapper
-
-import (
- "io"
- "os"
- "sync"
-
- "golang.org/x/xerrors"
-)
-
-func ToReadableFile(r io.Reader, n int64) (*os.File, func() error, error) {
- f, ok := r.(*os.File)
- if ok {
- return f, func() error { return nil }, nil
- }
-
- var w *os.File
-
- f, w, err := os.Pipe()
- if err != nil {
- return nil, nil, err
- }
-
- var wait sync.Mutex
- var werr error
-
- wait.Lock()
- go func() {
- defer wait.Unlock()
-
- var copied int64
- copied, werr = io.CopyN(w, r, n)
- if werr != nil {
- log.Warnf("toReadableFile: copy error: %+v", werr)
- }
-
- err := w.Close()
- if werr == nil && err != nil {
- werr = err
- log.Warnf("toReadableFile: close error: %+v", err)
- return
- }
- if copied != n {
- log.Warnf("copied different amount than expected: %d != %d", copied, n)
- werr = xerrors.Errorf("copied different amount than expected: %d != %d", copied, n)
- }
- }()
-
- return f, func() error {
- wait.Lock()
- return werr
- }, nil
-}
diff --git a/extern/sector-storage/ffiwrapper/sealer_cgo.go b/extern/sector-storage/ffiwrapper/sealer_cgo.go
index 0887bc329..1c8c7ee84 100644
--- a/extern/sector-storage/ffiwrapper/sealer_cgo.go
+++ b/extern/sector-storage/ffiwrapper/sealer_cgo.go
@@ -20,9 +20,10 @@ import (
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/specs-storage/storage"
+ commpffi "github.com/filecoin-project/go-commp-utils/ffiwrapper"
+ "github.com/filecoin-project/go-commp-utils/zerocomm"
"github.com/filecoin-project/lotus/extern/sector-storage/fr32"
"github.com/filecoin-project/lotus/extern/sector-storage/storiface"
- "github.com/filecoin-project/lotus/extern/sector-storage/zerocomm"
)
var _ Storage = &Sealer{}
@@ -175,7 +176,7 @@ func (sb *Sealer) AddPiece(ctx context.Context, sector storage.SectorRef, existi
}
func (sb *Sealer) pieceCid(spt abi.RegisteredSealProof, in []byte) (cid.Cid, error) {
- prf, werr, err := ToReadableFile(bytes.NewReader(in), int64(len(in)))
+ prf, werr, err := commpffi.ToReadableFile(bytes.NewReader(in), int64(len(in)))
if err != nil {
return cid.Undef, xerrors.Errorf("getting tee reader pipe: %w", err)
}
@@ -610,20 +611,6 @@ func (sb *Sealer) Remove(ctx context.Context, sector storage.SectorRef) error {
return xerrors.Errorf("not supported at this layer") // happens in localworker
}
-func GeneratePieceCIDFromFile(proofType abi.RegisteredSealProof, piece io.Reader, pieceSize abi.UnpaddedPieceSize) (cid.Cid, error) {
- f, werr, err := ToReadableFile(piece, int64(pieceSize))
- if err != nil {
- return cid.Undef, err
- }
-
- pieceCID, err := ffi.GeneratePieceCIDFromFile(proofType, f, pieceSize)
- if err != nil {
- return cid.Undef, err
- }
-
- return pieceCID, werr()
-}
-
func GetRequiredPadding(oldLength abi.PaddedPieceSize, newPieceLength abi.PaddedPieceSize) ([]abi.PaddedPieceSize, abi.PaddedPieceSize) {
padPieces := make([]abi.PaddedPieceSize, 0)
diff --git a/extern/sector-storage/ffiwrapper/sealer_test.go b/extern/sector-storage/ffiwrapper/sealer_test.go
index 686ef847b..1292a9513 100644
--- a/extern/sector-storage/ffiwrapper/sealer_test.go
+++ b/extern/sector-storage/ffiwrapper/sealer_test.go
@@ -15,6 +15,8 @@ import (
"testing"
"time"
+ commpffi "github.com/filecoin-project/go-commp-utils/ffiwrapper"
+
proof2 "github.com/filecoin-project/specs-actors/v2/actors/runtime/proof"
"github.com/ipfs/go-cid"
@@ -465,7 +467,7 @@ func BenchmarkWriteWithAlignment(b *testing.B) {
for i := 0; i < b.N; i++ {
b.StopTimer()
- rf, w, _ := ToReadableFile(bytes.NewReader(bytes.Repeat([]byte{0xff, 0}, int(bt/2))), int64(bt))
+ rf, w, _ := commpffi.ToReadableFile(bytes.NewReader(bytes.Repeat([]byte{0xff, 0}, int(bt/2))), int64(bt))
tf, _ := ioutil.TempFile("/tmp/", "scrb-")
b.StartTimer()
@@ -524,7 +526,7 @@ func TestGenerateUnsealedCID(t *testing.T) {
ups := int(abi.PaddedPieceSize(2048).Unpadded())
commP := func(b []byte) cid.Cid {
- pf, werr, err := ToReadableFile(bytes.NewReader(b), int64(len(b)))
+ pf, werr, err := commpffi.ToReadableFile(bytes.NewReader(b), int64(len(b)))
require.NoError(t, err)
c, err := ffi.GeneratePieceCIDFromFile(pt, pf, abi.UnpaddedPieceSize(len(b)))
diff --git a/extern/sector-storage/fr32/fr32_ffi_cmp_test.go b/extern/sector-storage/fr32/fr32_ffi_cmp_test.go
index 3d5679095..49eb11548 100644
--- a/extern/sector-storage/fr32/fr32_ffi_cmp_test.go
+++ b/extern/sector-storage/fr32/fr32_ffi_cmp_test.go
@@ -7,11 +7,12 @@ import (
"os"
"testing"
- "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper"
"github.com/filecoin-project/lotus/extern/sector-storage/fr32"
ffi "github.com/filecoin-project/filecoin-ffi"
+ commpffi "github.com/filecoin-project/go-commp-utils/ffiwrapper"
+
"github.com/filecoin-project/go-state-types/abi"
"github.com/stretchr/testify/require"
@@ -29,7 +30,7 @@ func TestWriteTwoPcs(t *testing.T) {
buf := bytes.Repeat([]byte{0xab * byte(i)}, int(paddedSize.Unpadded()))
rawBytes = append(rawBytes, buf...)
- rf, w, _ := ffiwrapper.ToReadableFile(bytes.NewReader(buf), int64(len(buf)))
+ rf, w, _ := commpffi.ToReadableFile(bytes.NewReader(buf), int64(len(buf)))
_, _, _, err := ffi.WriteWithAlignment(abi.RegisteredSealProof_StackedDrg32GiBV1, rf, abi.UnpaddedPieceSize(len(buf)), tf, nil)
if err != nil {
diff --git a/extern/sector-storage/fr32/fr32_test.go b/extern/sector-storage/fr32/fr32_test.go
index 415134272..0626f72a9 100644
--- a/extern/sector-storage/fr32/fr32_test.go
+++ b/extern/sector-storage/fr32/fr32_test.go
@@ -9,15 +9,15 @@ import (
"testing"
ffi "github.com/filecoin-project/filecoin-ffi"
+ commpffi "github.com/filecoin-project/go-commp-utils/ffiwrapper"
"github.com/filecoin-project/go-state-types/abi"
"github.com/stretchr/testify/require"
- "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper"
"github.com/filecoin-project/lotus/extern/sector-storage/fr32"
)
func padFFI(buf []byte) []byte {
- rf, w, _ := ffiwrapper.ToReadableFile(bytes.NewReader(buf), int64(len(buf)))
+ rf, w, _ := commpffi.ToReadableFile(bytes.NewReader(buf), int64(len(buf)))
tf, _ := ioutil.TempFile("/tmp/", "scrb-")
_, _, _, err := ffi.WriteWithAlignment(abi.RegisteredSealProof_StackedDrg32GiBV1, rf, abi.UnpaddedPieceSize(len(buf)), tf, nil)
diff --git a/extern/sector-storage/manager.go b/extern/sector-storage/manager.go
index 52e079d75..c56bbdf50 100644
--- a/extern/sector-storage/manager.go
+++ b/extern/sector-storage/manager.go
@@ -58,6 +58,10 @@ type SectorManager interface {
type WorkerID uuid.UUID // worker session UUID
var ClosedWorkerID = uuid.UUID{}
+func (w WorkerID) String() string {
+ return uuid.UUID(w).String()
+}
+
type Manager struct {
ls stores.LocalStorage
storage *stores.Remote
diff --git a/extern/sector-storage/manager_calltracker.go b/extern/sector-storage/manager_calltracker.go
index e2f801303..c3b2e3190 100644
--- a/extern/sector-storage/manager_calltracker.go
+++ b/extern/sector-storage/manager_calltracker.go
@@ -146,7 +146,7 @@ func (m *Manager) getWork(ctx context.Context, method sealtasks.TaskType, params
switch ws.Status {
case wsStarted:
- log.Warn("canceling started (not running) work %s", wid)
+ log.Warnf("canceling started (not running) work %s", wid)
if err := m.work.Get(wid).End(); err != nil {
log.Errorf("cancel: failed to cancel started work %s: %+v", wid, err)
@@ -154,9 +154,9 @@ func (m *Manager) getWork(ctx context.Context, method sealtasks.TaskType, params
}
case wsDone:
// TODO: still remove?
- log.Warn("cancel called on work %s in 'done' state", wid)
+ log.Warnf("cancel called on work %s in 'done' state", wid)
case wsRunning:
- log.Warn("cancel called on work %s in 'running' state (manager shutting down?)", wid)
+ log.Warnf("cancel called on work %s in 'running' state (manager shutting down?)", wid)
}
}, nil
diff --git a/extern/sector-storage/mock/mock.go b/extern/sector-storage/mock/mock.go
index 747fcdf8b..47fb2b974 100644
--- a/extern/sector-storage/mock/mock.go
+++ b/extern/sector-storage/mock/mock.go
@@ -11,6 +11,7 @@ import (
proof2 "github.com/filecoin-project/specs-actors/v2/actors/runtime/proof"
+ ffiwrapper2 "github.com/filecoin-project/go-commp-utils/ffiwrapper"
commcid "github.com/filecoin-project/go-fil-commcid"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/specs-storage/storage"
@@ -76,7 +77,7 @@ func (mgr *SectorMgr) AddPiece(ctx context.Context, sectorID storage.SectorRef,
var b bytes.Buffer
tr := io.TeeReader(r, &b)
- c, err := ffiwrapper.GeneratePieceCIDFromFile(sectorID.ProofType, tr, size)
+ c, err := ffiwrapper2.GeneratePieceCIDFromFile(sectorID.ProofType, tr, size)
if err != nil {
return abi.PieceInfo{}, xerrors.Errorf("failed to generate piece cid: %w", err)
}
@@ -404,14 +405,14 @@ func (mgr *SectorMgr) Remove(ctx context.Context, sector storage.SectorRef) erro
return nil
}
-func (mgr *SectorMgr) CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof, ids []storage.SectorRef) ([]abi.SectorID, error) {
- var bad []abi.SectorID
+func (mgr *SectorMgr) CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof, ids []storage.SectorRef, rg storiface.RGetter) (map[abi.SectorID]string, error) {
+ bad := map[abi.SectorID]string{}
for _, sid := range ids {
_, found := mgr.sectors[sid.ID]
if !found || mgr.sectors[sid.ID].failed {
- bad = append(bad, sid.ID)
+ bad[sid.ID] = "mock fail"
}
}
diff --git a/extern/sector-storage/sched.go b/extern/sector-storage/sched.go
index 79761a65e..61411081a 100644
--- a/extern/sector-storage/sched.go
+++ b/extern/sector-storage/sched.go
@@ -348,24 +348,25 @@ func (sh *scheduler) trySched() {
sh.workersLk.RLock()
defer sh.workersLk.RUnlock()
- windows := make([]schedWindow, len(sh.openWindows))
- acceptableWindows := make([][]int, sh.schedQueue.Len())
+ windowsLen := len(sh.openWindows)
+ queuneLen := sh.schedQueue.Len()
- log.Debugf("SCHED %d queued; %d open windows", sh.schedQueue.Len(), len(windows))
+ log.Debugf("SCHED %d queued; %d open windows", queuneLen, windowsLen)
- if len(sh.openWindows) == 0 {
+ if windowsLen == 0 || queuneLen == 0 {
// nothing to schedule on
return
}
+ windows := make([]schedWindow, windowsLen)
+ acceptableWindows := make([][]int, queuneLen)
+
// Step 1
- concurrency := len(sh.openWindows)
- throttle := make(chan struct{}, concurrency)
+ throttle := make(chan struct{}, windowsLen)
var wg sync.WaitGroup
- wg.Add(sh.schedQueue.Len())
-
- for i := 0; i < sh.schedQueue.Len(); i++ {
+ wg.Add(queuneLen)
+ for i := 0; i < queuneLen; i++ {
throttle <- struct{}{}
go func(sqi int) {
@@ -436,7 +437,7 @@ func (sh *scheduler) trySched() {
r, err := task.sel.Cmp(rpcCtx, task.taskType, wi, wj)
if err != nil {
- log.Error("selecting best worker: %s", err)
+ log.Errorf("selecting best worker: %s", err)
}
return r
})
@@ -450,8 +451,9 @@ func (sh *scheduler) trySched() {
// Step 2
scheduled := 0
+ rmQueue := make([]int, 0, queuneLen)
- for sqi := 0; sqi < sh.schedQueue.Len(); sqi++ {
+ for sqi := 0; sqi < queuneLen; sqi++ {
task := (*sh.schedQueue)[sqi]
needRes := ResourceTable[task.taskType][task.sector.ProofType]
@@ -486,11 +488,16 @@ func (sh *scheduler) trySched() {
windows[selectedWindow].todo = append(windows[selectedWindow].todo, task)
- sh.schedQueue.Remove(sqi)
- sqi--
+ rmQueue = append(rmQueue, sqi)
scheduled++
}
+ if len(rmQueue) > 0 {
+ for i := len(rmQueue) - 1; i >= 0; i-- {
+ sh.schedQueue.Remove(rmQueue[i])
+ }
+ }
+
// Step 3
if scheduled == 0 {
@@ -515,7 +522,7 @@ func (sh *scheduler) trySched() {
}
// Rewrite sh.openWindows array, removing scheduled windows
- newOpenWindows := make([]*schedWindowRequest, 0, len(sh.openWindows)-len(scheduledWindows))
+ newOpenWindows := make([]*schedWindowRequest, 0, windowsLen-len(scheduledWindows))
for wnd, window := range sh.openWindows {
if _, scheduled := scheduledWindows[wnd]; scheduled {
// keep unscheduled windows open
diff --git a/extern/sector-storage/sched_resources.go b/extern/sector-storage/sched_resources.go
index 10fe29aae..3e359c121 100644
--- a/extern/sector-storage/sched_resources.go
+++ b/extern/sector-storage/sched_resources.go
@@ -49,25 +49,25 @@ func (a *activeResources) canHandleRequest(needRes Resources, wid WorkerID, call
// TODO: dedupe needRes.BaseMinMemory per task type (don't add if that task is already running)
minNeedMem := res.MemReserved + a.memUsedMin + needRes.MinMemory + needRes.BaseMinMemory
if minNeedMem > res.MemPhysical {
- log.Debugf("sched: not scheduling on worker %d for %s; not enough physical memory - need: %dM, have %dM", wid, caller, minNeedMem/mib, res.MemPhysical/mib)
+ log.Debugf("sched: not scheduling on worker %s for %s; not enough physical memory - need: %dM, have %dM", wid, caller, minNeedMem/mib, res.MemPhysical/mib)
return false
}
maxNeedMem := res.MemReserved + a.memUsedMax + needRes.MaxMemory + needRes.BaseMinMemory
if maxNeedMem > res.MemSwap+res.MemPhysical {
- log.Debugf("sched: not scheduling on worker %d for %s; not enough virtual memory - need: %dM, have %dM", wid, caller, maxNeedMem/mib, (res.MemSwap+res.MemPhysical)/mib)
+ log.Debugf("sched: not scheduling on worker %s for %s; not enough virtual memory - need: %dM, have %dM", wid, caller, maxNeedMem/mib, (res.MemSwap+res.MemPhysical)/mib)
return false
}
if a.cpuUse+needRes.Threads(res.CPUs) > res.CPUs {
- log.Debugf("sched: not scheduling on worker %d for %s; not enough threads, need %d, %d in use, target %d", wid, caller, needRes.Threads(res.CPUs), a.cpuUse, res.CPUs)
+ log.Debugf("sched: not scheduling on worker %s for %s; not enough threads, need %d, %d in use, target %d", wid, caller, needRes.Threads(res.CPUs), a.cpuUse, res.CPUs)
return false
}
if len(res.GPUs) > 0 && needRes.CanGPU {
if a.gpuUsed {
- log.Debugf("sched: not scheduling on worker %d for %s; GPU in use", wid, caller)
+ log.Debugf("sched: not scheduling on worker %s for %s; GPU in use", wid, caller)
return false
}
}
diff --git a/extern/sector-storage/sched_worker.go b/extern/sector-storage/sched_worker.go
index 67bddca3a..573aa623b 100644
--- a/extern/sector-storage/sched_worker.go
+++ b/extern/sector-storage/sched_worker.go
@@ -368,7 +368,7 @@ assignLoop:
err := sw.startProcessingTask(sw.taskDone, todo)
if err != nil {
- log.Error("startProcessingTask error: %+v", err)
+ log.Errorf("startProcessingTask error: %+v", err)
go todo.respond(xerrors.Errorf("startProcessingTask error: %w", err))
}
@@ -486,6 +486,6 @@ func (sh *scheduler) workerCleanup(wid WorkerID, w *workerHandle) {
}
sh.openWindows = newWindows
- log.Debugf("worker %d dropped", wid)
+ log.Debugf("worker %s dropped", wid)
}
}
diff --git a/extern/sector-storage/storiface/ffi.go b/extern/sector-storage/storiface/ffi.go
index 95d400e52..f6b2cbdd3 100644
--- a/extern/sector-storage/storiface/ffi.go
+++ b/extern/sector-storage/storiface/ffi.go
@@ -1,8 +1,11 @@
package storiface
import (
+ "context"
"errors"
+ "github.com/ipfs/go-cid"
+
"github.com/filecoin-project/go-state-types/abi"
)
@@ -15,3 +18,5 @@ func (i UnpaddedByteIndex) Padded() PaddedByteIndex {
}
type PaddedByteIndex uint64
+
+type RGetter func(ctx context.Context, id abi.SectorID) (cid.Cid, error)
diff --git a/extern/sector-storage/worker_local.go b/extern/sector-storage/worker_local.go
index c069d7bf7..abbad4d9c 100644
--- a/extern/sector-storage/worker_local.go
+++ b/extern/sector-storage/worker_local.go
@@ -49,6 +49,7 @@ type LocalWorker struct {
ct *workerCallTracker
acceptTasks map[sealtasks.TaskType]struct{}
running sync.WaitGroup
+ taskLk sync.Mutex
session uuid.UUID
testDisable int64
@@ -457,9 +458,28 @@ func (l *LocalWorker) ReadPiece(ctx context.Context, writer io.Writer, sector st
}
func (l *LocalWorker) TaskTypes(context.Context) (map[sealtasks.TaskType]struct{}, error) {
+ l.taskLk.Lock()
+ defer l.taskLk.Unlock()
+
return l.acceptTasks, nil
}
+func (l *LocalWorker) TaskDisable(ctx context.Context, tt sealtasks.TaskType) error {
+ l.taskLk.Lock()
+ defer l.taskLk.Unlock()
+
+ delete(l.acceptTasks, tt)
+ return nil
+}
+
+func (l *LocalWorker) TaskEnable(ctx context.Context, tt sealtasks.TaskType) error {
+ l.taskLk.Lock()
+ defer l.taskLk.Unlock()
+
+ l.acceptTasks[tt] = struct{}{}
+ return nil
+}
+
func (l *LocalWorker) Paths(ctx context.Context) ([]stores.StoragePath, error) {
return l.localStore.Local(ctx)
}
diff --git a/extern/sector-storage/zerocomm/zerocomm.go b/extern/sector-storage/zerocomm/zerocomm.go
deleted file mode 100644
index 9855a5821..000000000
--- a/extern/sector-storage/zerocomm/zerocomm.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package zerocomm
-
-import (
- "math/bits"
-
- commcid "github.com/filecoin-project/go-fil-commcid"
- "github.com/filecoin-project/go-state-types/abi"
- "github.com/ipfs/go-cid"
-)
-
-const Levels = 37
-const Skip = 2 // can't generate for 32, 64b
-
-var PieceComms = [Levels - Skip][32]byte{
- {0x37, 0x31, 0xbb, 0x99, 0xac, 0x68, 0x9f, 0x66, 0xee, 0xf5, 0x97, 0x3e, 0x4a, 0x94, 0xda, 0x18, 0x8f, 0x4d, 0xdc, 0xae, 0x58, 0x7, 0x24, 0xfc, 0x6f, 0x3f, 0xd6, 0xd, 0xfd, 0x48, 0x83, 0x33},
- {0x64, 0x2a, 0x60, 0x7e, 0xf8, 0x86, 0xb0, 0x4, 0xbf, 0x2c, 0x19, 0x78, 0x46, 0x3a, 0xe1, 0xd4, 0x69, 0x3a, 0xc0, 0xf4, 0x10, 0xeb, 0x2d, 0x1b, 0x7a, 0x47, 0xfe, 0x20, 0x5e, 0x5e, 0x75, 0xf},
- {0x57, 0xa2, 0x38, 0x1a, 0x28, 0x65, 0x2b, 0xf4, 0x7f, 0x6b, 0xef, 0x7a, 0xca, 0x67, 0x9b, 0xe4, 0xae, 0xde, 0x58, 0x71, 0xab, 0x5c, 0xf3, 0xeb, 0x2c, 0x8, 0x11, 0x44, 0x88, 0xcb, 0x85, 0x26},
- {0x1f, 0x7a, 0xc9, 0x59, 0x55, 0x10, 0xe0, 0x9e, 0xa4, 0x1c, 0x46, 0xb, 0x17, 0x64, 0x30, 0xbb, 0x32, 0x2c, 0xd6, 0xfb, 0x41, 0x2e, 0xc5, 0x7c, 0xb1, 0x7d, 0x98, 0x9a, 0x43, 0x10, 0x37, 0x2f},
- {0xfc, 0x7e, 0x92, 0x82, 0x96, 0xe5, 0x16, 0xfa, 0xad, 0xe9, 0x86, 0xb2, 0x8f, 0x92, 0xd4, 0x4a, 0x4f, 0x24, 0xb9, 0x35, 0x48, 0x52, 0x23, 0x37, 0x6a, 0x79, 0x90, 0x27, 0xbc, 0x18, 0xf8, 0x33},
- {0x8, 0xc4, 0x7b, 0x38, 0xee, 0x13, 0xbc, 0x43, 0xf4, 0x1b, 0x91, 0x5c, 0xe, 0xed, 0x99, 0x11, 0xa2, 0x60, 0x86, 0xb3, 0xed, 0x62, 0x40, 0x1b, 0xf9, 0xd5, 0x8b, 0x8d, 0x19, 0xdf, 0xf6, 0x24},
- {0xb2, 0xe4, 0x7b, 0xfb, 0x11, 0xfa, 0xcd, 0x94, 0x1f, 0x62, 0xaf, 0x5c, 0x75, 0xf, 0x3e, 0xa5, 0xcc, 0x4d, 0xf5, 0x17, 0xd5, 0xc4, 0xf1, 0x6d, 0xb2, 0xb4, 0xd7, 0x7b, 0xae, 0xc1, 0xa3, 0x2f},
- {0xf9, 0x22, 0x61, 0x60, 0xc8, 0xf9, 0x27, 0xbf, 0xdc, 0xc4, 0x18, 0xcd, 0xf2, 0x3, 0x49, 0x31, 0x46, 0x0, 0x8e, 0xae, 0xfb, 0x7d, 0x2, 0x19, 0x4d, 0x5e, 0x54, 0x81, 0x89, 0x0, 0x51, 0x8},
- {0x2c, 0x1a, 0x96, 0x4b, 0xb9, 0xb, 0x59, 0xeb, 0xfe, 0xf, 0x6d, 0xa2, 0x9a, 0xd6, 0x5a, 0xe3, 0xe4, 0x17, 0x72, 0x4a, 0x8f, 0x7c, 0x11, 0x74, 0x5a, 0x40, 0xca, 0xc1, 0xe5, 0xe7, 0x40, 0x11},
- {0xfe, 0xe3, 0x78, 0xce, 0xf1, 0x64, 0x4, 0xb1, 0x99, 0xed, 0xe0, 0xb1, 0x3e, 0x11, 0xb6, 0x24, 0xff, 0x9d, 0x78, 0x4f, 0xbb, 0xed, 0x87, 0x8d, 0x83, 0x29, 0x7e, 0x79, 0x5e, 0x2, 0x4f, 0x2},
- {0x8e, 0x9e, 0x24, 0x3, 0xfa, 0x88, 0x4c, 0xf6, 0x23, 0x7f, 0x60, 0xdf, 0x25, 0xf8, 0x3e, 0xe4, 0xd, 0xca, 0x9e, 0xd8, 0x79, 0xeb, 0x6f, 0x63, 0x52, 0xd1, 0x50, 0x84, 0xf5, 0xad, 0xd, 0x3f},
- {0x75, 0x2d, 0x96, 0x93, 0xfa, 0x16, 0x75, 0x24, 0x39, 0x54, 0x76, 0xe3, 0x17, 0xa9, 0x85, 0x80, 0xf0, 0x9, 0x47, 0xaf, 0xb7, 0xa3, 0x5, 0x40, 0xd6, 0x25, 0xa9, 0x29, 0x1c, 0xc1, 0x2a, 0x7},
- {0x70, 0x22, 0xf6, 0xf, 0x7e, 0xf6, 0xad, 0xfa, 0x17, 0x11, 0x7a, 0x52, 0x61, 0x9e, 0x30, 0xce, 0xa8, 0x2c, 0x68, 0x7, 0x5a, 0xdf, 0x1c, 0x66, 0x77, 0x86, 0xec, 0x50, 0x6e, 0xef, 0x2d, 0x19},
- {0xd9, 0x98, 0x87, 0xb9, 0x73, 0x57, 0x3a, 0x96, 0xe1, 0x13, 0x93, 0x64, 0x52, 0x36, 0xc1, 0x7b, 0x1f, 0x4c, 0x70, 0x34, 0xd7, 0x23, 0xc7, 0xa9, 0x9f, 0x70, 0x9b, 0xb4, 0xda, 0x61, 0x16, 0x2b},
- {0xd0, 0xb5, 0x30, 0xdb, 0xb0, 0xb4, 0xf2, 0x5c, 0x5d, 0x2f, 0x2a, 0x28, 0xdf, 0xee, 0x80, 0x8b, 0x53, 0x41, 0x2a, 0x2, 0x93, 0x1f, 0x18, 0xc4, 0x99, 0xf5, 0xa2, 0x54, 0x8, 0x6b, 0x13, 0x26},
- {0x84, 0xc0, 0x42, 0x1b, 0xa0, 0x68, 0x5a, 0x1, 0xbf, 0x79, 0x5a, 0x23, 0x44, 0x6, 0x4f, 0xe4, 0x24, 0xbd, 0x52, 0xa9, 0xd2, 0x43, 0x77, 0xb3, 0x94, 0xff, 0x4c, 0x4b, 0x45, 0x68, 0xe8, 0x11},
- {0x65, 0xf2, 0x9e, 0x5d, 0x98, 0xd2, 0x46, 0xc3, 0x8b, 0x38, 0x8c, 0xfc, 0x6, 0xdb, 0x1f, 0x6b, 0x2, 0x13, 0x3, 0xc5, 0xa2, 0x89, 0x0, 0xb, 0xdc, 0xe8, 0x32, 0xa9, 0xc3, 0xec, 0x42, 0x1c},
- {0xa2, 0x24, 0x75, 0x8, 0x28, 0x58, 0x50, 0x96, 0x5b, 0x7e, 0x33, 0x4b, 0x31, 0x27, 0xb0, 0xc0, 0x42, 0xb1, 0xd0, 0x46, 0xdc, 0x54, 0x40, 0x21, 0x37, 0x62, 0x7c, 0xd8, 0x79, 0x9c, 0xe1, 0x3a},
- {0xda, 0xfd, 0xab, 0x6d, 0xa9, 0x36, 0x44, 0x53, 0xc2, 0x6d, 0x33, 0x72, 0x6b, 0x9f, 0xef, 0xe3, 0x43, 0xbe, 0x8f, 0x81, 0x64, 0x9e, 0xc0, 0x9, 0xaa, 0xd3, 0xfa, 0xff, 0x50, 0x61, 0x75, 0x8},
- {0xd9, 0x41, 0xd5, 0xe0, 0xd6, 0x31, 0x4a, 0x99, 0x5c, 0x33, 0xff, 0xbd, 0x4f, 0xbe, 0x69, 0x11, 0x8d, 0x73, 0xd4, 0xe5, 0xfd, 0x2c, 0xd3, 0x1f, 0xf, 0x7c, 0x86, 0xeb, 0xdd, 0x14, 0xe7, 0x6},
- {0x51, 0x4c, 0x43, 0x5c, 0x3d, 0x4, 0xd3, 0x49, 0xa5, 0x36, 0x5f, 0xbd, 0x59, 0xff, 0xc7, 0x13, 0x62, 0x91, 0x11, 0x78, 0x59, 0x91, 0xc1, 0xa3, 0xc5, 0x3a, 0xf2, 0x20, 0x79, 0x74, 0x1a, 0x2f},
- {0xad, 0x6, 0x85, 0x39, 0x69, 0xd3, 0x7d, 0x34, 0xff, 0x8, 0xe0, 0x9f, 0x56, 0x93, 0xa, 0x4a, 0xd1, 0x9a, 0x89, 0xde, 0xf6, 0xc, 0xbf, 0xee, 0x7e, 0x1d, 0x33, 0x81, 0xc1, 0xe7, 0x1c, 0x37},
- {0x39, 0x56, 0xe, 0x7b, 0x13, 0xa9, 0x3b, 0x7, 0xa2, 0x43, 0xfd, 0x27, 0x20, 0xff, 0xa7, 0xcb, 0x3e, 0x1d, 0x2e, 0x50, 0x5a, 0xb3, 0x62, 0x9e, 0x79, 0xf4, 0x63, 0x13, 0x51, 0x2c, 0xda, 0x6},
- {0xcc, 0xc3, 0xc0, 0x12, 0xf5, 0xb0, 0x5e, 0x81, 0x1a, 0x2b, 0xbf, 0xdd, 0xf, 0x68, 0x33, 0xb8, 0x42, 0x75, 0xb4, 0x7b, 0xf2, 0x29, 0xc0, 0x5, 0x2a, 0x82, 0x48, 0x4f, 0x3c, 0x1a, 0x5b, 0x3d},
- {0x7d, 0xf2, 0x9b, 0x69, 0x77, 0x31, 0x99, 0xe8, 0xf2, 0xb4, 0xb, 0x77, 0x91, 0x9d, 0x4, 0x85, 0x9, 0xee, 0xd7, 0x68, 0xe2, 0xc7, 0x29, 0x7b, 0x1f, 0x14, 0x37, 0x3, 0x4f, 0xc3, 0xc6, 0x2c},
- {0x66, 0xce, 0x5, 0xa3, 0x66, 0x75, 0x52, 0xcf, 0x45, 0xc0, 0x2b, 0xcc, 0x4e, 0x83, 0x92, 0x91, 0x9b, 0xde, 0xac, 0x35, 0xde, 0x2f, 0xf5, 0x62, 0x71, 0x84, 0x8e, 0x9f, 0x7b, 0x67, 0x51, 0x7},
- {0xd8, 0x61, 0x2, 0x18, 0x42, 0x5a, 0xb5, 0xe9, 0x5b, 0x1c, 0xa6, 0x23, 0x9d, 0x29, 0xa2, 0xe4, 0x20, 0xd7, 0x6, 0xa9, 0x6f, 0x37, 0x3e, 0x2f, 0x9c, 0x9a, 0x91, 0xd7, 0x59, 0xd1, 0x9b, 0x1},
- {0x6d, 0x36, 0x4b, 0x1e, 0xf8, 0x46, 0x44, 0x1a, 0x5a, 0x4a, 0x68, 0x86, 0x23, 0x14, 0xac, 0xc0, 0xa4, 0x6f, 0x1, 0x67, 0x17, 0xe5, 0x34, 0x43, 0xe8, 0x39, 0xee, 0xdf, 0x83, 0xc2, 0x85, 0x3c},
- {0x7, 0x7e, 0x5f, 0xde, 0x35, 0xc5, 0xa, 0x93, 0x3, 0xa5, 0x50, 0x9, 0xe3, 0x49, 0x8a, 0x4e, 0xbe, 0xdf, 0xf3, 0x9c, 0x42, 0xb7, 0x10, 0xb7, 0x30, 0xd8, 0xec, 0x7a, 0xc7, 0xaf, 0xa6, 0x3e},
- {0xe6, 0x40, 0x5, 0xa6, 0xbf, 0xe3, 0x77, 0x79, 0x53, 0xb8, 0xad, 0x6e, 0xf9, 0x3f, 0xf, 0xca, 0x10, 0x49, 0xb2, 0x4, 0x16, 0x54, 0xf2, 0xa4, 0x11, 0xf7, 0x70, 0x27, 0x99, 0xce, 0xce, 0x2},
- {0x25, 0x9d, 0x3d, 0x6b, 0x1f, 0x4d, 0x87, 0x6d, 0x11, 0x85, 0xe1, 0x12, 0x3a, 0xf6, 0xf5, 0x50, 0x1a, 0xf0, 0xf6, 0x7c, 0xf1, 0x5b, 0x52, 0x16, 0x25, 0x5b, 0x7b, 0x17, 0x8d, 0x12, 0x5, 0x1d},
- {0x3f, 0x9a, 0x4d, 0x41, 0x1d, 0xa4, 0xef, 0x1b, 0x36, 0xf3, 0x5f, 0xf0, 0xa1, 0x95, 0xae, 0x39, 0x2a, 0xb2, 0x3f, 0xee, 0x79, 0x67, 0xb7, 0xc4, 0x1b, 0x3, 0xd1, 0x61, 0x3f, 0xc2, 0x92, 0x39},
- {0xfe, 0x4e, 0xf3, 0x28, 0xc6, 0x1a, 0xa3, 0x9c, 0xfd, 0xb2, 0x48, 0x4e, 0xaa, 0x32, 0xa1, 0x51, 0xb1, 0xfe, 0x3d, 0xfd, 0x1f, 0x96, 0xdd, 0x8c, 0x97, 0x11, 0xfd, 0x86, 0xd6, 0xc5, 0x81, 0x13},
- {0xf5, 0x5d, 0x68, 0x90, 0xe, 0x2d, 0x83, 0x81, 0xec, 0xcb, 0x81, 0x64, 0xcb, 0x99, 0x76, 0xf2, 0x4b, 0x2d, 0xe0, 0xdd, 0x61, 0xa3, 0x1b, 0x97, 0xce, 0x6e, 0xb2, 0x38, 0x50, 0xd5, 0xe8, 0x19},
- {0xaa, 0xaa, 0x8c, 0x4c, 0xb4, 0xa, 0xac, 0xee, 0x1e, 0x2, 0xdc, 0x65, 0x42, 0x4b, 0x2a, 0x6c, 0x8e, 0x99, 0xf8, 0x3, 0xb7, 0x2f, 0x79, 0x29, 0xc4, 0x10, 0x1d, 0x7f, 0xae, 0x6b, 0xff, 0x32},
-}
-
-func ZeroPieceCommitment(sz abi.UnpaddedPieceSize) cid.Cid {
- level := bits.TrailingZeros64(uint64(sz.Padded())) - Skip - 5 // 2^5 = 32
- commP, _ := commcid.PieceCommitmentV1ToCID(PieceComms[level][:])
- return commP
-}
diff --git a/extern/sector-storage/zerocomm/zerocomm_test.go b/extern/sector-storage/zerocomm/zerocomm_test.go
deleted file mode 100644
index 393f61d64..000000000
--- a/extern/sector-storage/zerocomm/zerocomm_test.go
+++ /dev/null
@@ -1,115 +0,0 @@
-package zerocomm_test
-
-import (
- "bytes"
- "fmt"
- "io"
- "testing"
-
- commcid "github.com/filecoin-project/go-fil-commcid"
- abi "github.com/filecoin-project/go-state-types/abi"
- "github.com/ipfs/go-cid"
-
- "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper"
- "github.com/filecoin-project/lotus/extern/sector-storage/zerocomm"
-)
-
-func TestComms(t *testing.T) {
- t.Skip("don't have enough ram") // no, but seriously, currently this needs like 3tb of /tmp
-
- var expPieceComms [zerocomm.Levels - zerocomm.Skip]cid.Cid
-
- {
- l2, err := ffiwrapper.GeneratePieceCIDFromFile(abi.RegisteredSealProof_StackedDrg2KiBV1, bytes.NewReader(make([]byte, 127)), 127)
- if err != nil {
- t.Fatal(err)
- }
- expPieceComms[0] = l2
- }
-
- for i := 1; i < zerocomm.Levels-2; i++ {
- var err error
- sz := abi.UnpaddedPieceSize(127 << uint(i))
- fmt.Println(i, sz)
- r := io.LimitReader(&NullReader{}, int64(sz))
-
- expPieceComms[i], err = ffiwrapper.GeneratePieceCIDFromFile(abi.RegisteredSealProof_StackedDrg2KiBV1, r, sz)
- if err != nil {
- t.Fatal(err)
- }
- }
-
- for i, comm := range expPieceComms {
- c, err := commcid.CIDToPieceCommitmentV1(comm)
- if err != nil {
- t.Fatal(err)
- }
- if string(c) != string(zerocomm.PieceComms[i][:]) {
- t.Errorf("zero commitment %d didn't match", i)
- }
- }
-
- for _, comm := range expPieceComms { // Could do codegen, but this is good enough
- fmt.Printf("%#v,\n", comm)
- }
-}
-
-func TestCommsSmall(t *testing.T) {
- var expPieceComms [8]cid.Cid
- lvls := len(expPieceComms) + zerocomm.Skip
-
- {
- l2, err := ffiwrapper.GeneratePieceCIDFromFile(abi.RegisteredSealProof_StackedDrg2KiBV1, bytes.NewReader(make([]byte, 127)), 127)
- if err != nil {
- t.Fatal(err)
- }
- expPieceComms[0] = l2
- }
-
- for i := 1; i < lvls-2; i++ {
- var err error
- sz := abi.UnpaddedPieceSize(127 << uint(i))
- fmt.Println(i, sz)
- r := io.LimitReader(&NullReader{}, int64(sz))
-
- expPieceComms[i], err = ffiwrapper.GeneratePieceCIDFromFile(abi.RegisteredSealProof_StackedDrg2KiBV1, r, sz)
- if err != nil {
- t.Fatal(err)
- }
- }
-
- for i, comm := range expPieceComms {
- c, err := commcid.CIDToPieceCommitmentV1(comm)
- if err != nil {
- t.Fatal(err)
- }
- if string(c) != string(zerocomm.PieceComms[i][:]) {
- t.Errorf("zero commitment %d didn't match", i)
- }
- }
-
- for _, comm := range expPieceComms { // Could do codegen, but this is good enough
- fmt.Printf("%#v,\n", comm)
- }
-}
-
-func TestForSise(t *testing.T) {
- exp, err := ffiwrapper.GeneratePieceCIDFromFile(abi.RegisteredSealProof_StackedDrg2KiBV1, bytes.NewReader(make([]byte, 1016)), 1016)
- if err != nil {
- return
- }
-
- actual := zerocomm.ZeroPieceCommitment(1016)
- if !exp.Equals(actual) {
- t.Errorf("zero commitment didn't match")
- }
-}
-
-type NullReader struct{}
-
-func (NullReader) Read(out []byte) (int, error) {
- for i := range out {
- out[i] = 0
- }
- return len(out), nil
-}
diff --git a/extern/storage-sealing/checks.go b/extern/storage-sealing/checks.go
index 56a55bb61..249163d66 100644
--- a/extern/storage-sealing/checks.go
+++ b/extern/storage-sealing/checks.go
@@ -12,9 +12,9 @@ import (
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
+ "github.com/filecoin-project/go-commp-utils/zerocomm"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/crypto"
- "github.com/filecoin-project/lotus/extern/sector-storage/zerocomm"
)
// TODO: For now we handle this by halting state execution, when we get jsonrpc reconnecting
diff --git a/extern/storage-sealing/fsm.go b/extern/storage-sealing/fsm.go
index cf0be4cd4..740e4243a 100644
--- a/extern/storage-sealing/fsm.go
+++ b/extern/storage-sealing/fsm.go
@@ -159,8 +159,12 @@ var fsmPlanners = map[SectorState]func(events []statemachine.Event, state *Secto
on(SectorFaultReported{}, FaultReported),
),
+ FaultReported: final, // not really supported right now
+
FaultedFinal: final,
Removed: final,
+
+ FailedUnrecoverable: final,
}
func (m *Sealing) plan(events []statemachine.Event, state *SectorInfo) (func(statemachine.Context, SectorInfo) error, uint64, error) {
diff --git a/extern/storage-sealing/fsm_test.go b/extern/storage-sealing/fsm_test.go
index 5b4541f75..ac71588a5 100644
--- a/extern/storage-sealing/fsm_test.go
+++ b/extern/storage-sealing/fsm_test.go
@@ -160,3 +160,18 @@ func TestPlanCommittingHandlesSectorCommitFailed(t *testing.T) {
require.Equal(t, CommitFailed, m.state.State)
}
+
+func TestPlannerList(t *testing.T) {
+ for state := range ExistSectorStateList {
+ _, ok := fsmPlanners[state]
+ require.True(t, ok, "state %s", state)
+ }
+
+ for state := range fsmPlanners {
+ if state == UndefinedSectorState {
+ continue
+ }
+ _, ok := ExistSectorStateList[state]
+ require.True(t, ok, "state %s", state)
+ }
+}
diff --git a/extern/storage-sealing/sealing.go b/extern/storage-sealing/sealing.go
index 5211f8bbe..25397ecfb 100644
--- a/extern/storage-sealing/sealing.go
+++ b/extern/storage-sealing/sealing.go
@@ -283,29 +283,52 @@ func (m *Sealing) StartPacking(sectorID abi.SectorNumber) error {
// Caller should hold m.unsealedInfoMap.lk
func (m *Sealing) getSectorAndPadding(ctx context.Context, size abi.UnpaddedPieceSize) (abi.SectorNumber, []abi.PaddedPieceSize, error) {
- for k, v := range m.unsealedInfoMap.infos {
- pads, padLength := ffiwrapper.GetRequiredPadding(v.stored, size.Padded())
+ for tries := 0; tries < 100; tries++ {
+ for k, v := range m.unsealedInfoMap.infos {
+ pads, padLength := ffiwrapper.GetRequiredPadding(v.stored, size.Padded())
- if v.stored+size.Padded()+padLength <= abi.PaddedPieceSize(v.ssize) {
- return k, pads, nil
+ if v.stored+size.Padded()+padLength <= abi.PaddedPieceSize(v.ssize) {
+ return k, pads, nil
+ }
}
+
+ if len(m.unsealedInfoMap.infos) > 0 {
+ log.Infow("tried to put a piece into an open sector, found none with enough space", "open", len(m.unsealedInfoMap.infos), "size", size, "tries", tries)
+ }
+
+ ns, ssize, err := m.newDealSector(ctx)
+ switch err {
+ case nil:
+ m.unsealedInfoMap.infos[ns] = UnsealedSectorInfo{
+ numDeals: 0,
+ stored: 0,
+ pieceSizes: nil,
+ ssize: ssize,
+ }
+ case errTooManySealing:
+ m.unsealedInfoMap.lk.Unlock()
+
+ select {
+ case <-time.After(2 * time.Second):
+ case <-ctx.Done():
+ m.unsealedInfoMap.lk.Lock()
+ return 0, nil, xerrors.Errorf("getting sector for piece: %w", ctx.Err())
+ }
+
+ m.unsealedInfoMap.lk.Lock()
+ continue
+ default:
+ return 0, nil, xerrors.Errorf("creating new sector: %w", err)
+ }
+
+ return ns, nil, nil
}
- ns, ssize, err := m.newDealSector(ctx)
- if err != nil {
- return 0, nil, err
- }
-
- m.unsealedInfoMap.infos[ns] = UnsealedSectorInfo{
- numDeals: 0,
- stored: 0,
- pieceSizes: nil,
- ssize: ssize,
- }
-
- return ns, nil, nil
+ return 0, nil, xerrors.Errorf("failed to allocate piece to a sector")
}
+var errTooManySealing = errors.New("too many sectors sealing")
+
// newDealSector creates a new sector for deal storage
func (m *Sealing) newDealSector(ctx context.Context) (abi.SectorNumber, abi.SectorSize, error) {
// First make sure we don't have too many 'open' sectors
@@ -321,47 +344,34 @@ func (m *Sealing) newDealSector(ctx context.Context) (abi.SectorNumber, abi.Sect
}
}
- if cfg.MaxWaitDealsSectors > 0 {
- // run in a loop because we have to drop the map lock here for a bit
- tries := 0
+ if cfg.MaxWaitDealsSectors > 0 && uint64(len(m.unsealedInfoMap.infos)) >= cfg.MaxWaitDealsSectors {
+ // Too many sectors are sealing in parallel. Start sealing one, and retry
+ // allocating the piece to a sector (we're dropping the lock here, so in
+ // case other goroutines are also trying to create a sector, we retry in
+ // getSectorAndPadding instead of here - otherwise if we have lots of
+ // parallel deals in progress, we can start creating a ton of sectors
+ // with just a single deal in them)
+ var mostStored abi.PaddedPieceSize = math.MaxUint64
+ var best abi.SectorNumber = math.MaxUint64
- // we have to run in a loop as we're dropping unsealedInfoMap.lk
- // to actually call StartPacking. When we do that, another entry can
- // get added to unsealedInfoMap.
- for uint64(len(m.unsealedInfoMap.infos)) >= cfg.MaxWaitDealsSectors {
- if tries > 10 {
- // whatever...
- break
+ for sn, info := range m.unsealedInfoMap.infos {
+ if info.stored+1 > mostStored+1 { // 18446744073709551615 + 1 = 0
+ best = sn
}
-
- if tries > 0 {
- m.unsealedInfoMap.lk.Unlock()
- time.Sleep(time.Second)
- m.unsealedInfoMap.lk.Lock()
- }
-
- tries++
- var mostStored abi.PaddedPieceSize = math.MaxUint64
- var best abi.SectorNumber = math.MaxUint64
-
- for sn, info := range m.unsealedInfoMap.infos {
- if info.stored+1 > mostStored+1 { // 18446744073709551615 + 1 = 0
- best = sn
- }
- }
-
- if best == math.MaxUint64 {
- // probably not possible, but who knows
- break
- }
-
- m.unsealedInfoMap.lk.Unlock()
- if err := m.StartPacking(best); err != nil {
- log.Error("newDealSector StartPacking error: %+v", err)
- continue // let's pretend this is fine
- }
- m.unsealedInfoMap.lk.Lock()
}
+
+ if best != math.MaxUint64 {
+ m.unsealedInfoMap.lk.Unlock()
+ err := m.StartPacking(best)
+ m.unsealedInfoMap.lk.Lock()
+
+ if err != nil {
+ log.Errorf("newDealSector StartPacking error: %+v", err)
+ // let's pretend this is fine
+ }
+ }
+
+ return 0, 0, errTooManySealing // will wait a bit and retry
}
spt, err := m.currentSealProof(ctx)
diff --git a/extern/storage-sealing/sector_state.go b/extern/storage-sealing/sector_state.go
index 8b0bff24a..ed32a110b 100644
--- a/extern/storage-sealing/sector_state.go
+++ b/extern/storage-sealing/sector_state.go
@@ -6,6 +6,7 @@ var ExistSectorStateList = map[SectorState]struct{}{
Empty: {},
WaitDeals: {},
Packing: {},
+ GetTicket: {},
PreCommit1: {},
PreCommit2: {},
PreCommitting: {},
@@ -75,7 +76,7 @@ const (
func toStatState(st SectorState) statSectorState {
switch st {
- case Empty, WaitDeals, Packing, PreCommit1, PreCommit2, PreCommitting, PreCommitWait, WaitSeed, Committing, CommitWait, FinalizeSector:
+ case Empty, WaitDeals, Packing, GetTicket, PreCommit1, PreCommit2, PreCommitting, PreCommitWait, WaitSeed, Committing, SubmitCommit, CommitWait, FinalizeSector:
return sstSealing
case Proving, Removed, Removing:
return sstProving
diff --git a/extern/storage-sealing/states_failed.go b/extern/storage-sealing/states_failed.go
index 760afc0ba..e425606de 100644
--- a/extern/storage-sealing/states_failed.go
+++ b/extern/storage-sealing/states_failed.go
@@ -13,7 +13,7 @@ import (
"github.com/filecoin-project/go-state-types/exitcode"
"github.com/filecoin-project/go-statemachine"
- "github.com/filecoin-project/lotus/extern/sector-storage/zerocomm"
+ "github.com/filecoin-project/go-commp-utils/zerocomm"
)
const minRetryTime = 1 * time.Minute
@@ -137,12 +137,12 @@ func (m *Sealing) handlePreCommitFailed(ctx statemachine.Context, sector SectorI
if pci, is := m.checkPreCommitted(ctx, sector); is && pci != nil {
if sector.PreCommitMessage == nil {
- log.Warn("sector %d is precommitted on chain, but we don't have precommit message", sector.SectorNumber)
+ log.Warnf("sector %d is precommitted on chain, but we don't have precommit message", sector.SectorNumber)
return ctx.Send(SectorPreCommitLanded{TipSet: tok})
}
if pci.Info.SealedCID != *sector.CommR {
- log.Warn("sector %d is precommitted on chain, with different CommR: %x != %x", sector.SectorNumber, pci.Info.SealedCID, sector.CommR)
+ log.Warnf("sector %d is precommitted on chain, with different CommR: %x != %x", sector.SectorNumber, pci.Info.SealedCID, sector.CommR)
return nil // TODO: remove when the actor allows re-precommit
}
@@ -387,7 +387,7 @@ func (m *Sealing) handleRecoverDealIDs(ctx statemachine.Context, sector SectorIn
if p.DealInfo.PublishCid == nil {
// TODO: check if we are in an early enough state try to remove this piece
- log.Error("can't fix sector deals: piece %d (of %d) of sector %d has nil DealInfo.PublishCid (refers to deal %d)", i, len(sector.Pieces), sector.SectorNumber, p.DealInfo.DealID)
+ log.Errorf("can't fix sector deals: piece %d (of %d) of sector %d has nil DealInfo.PublishCid (refers to deal %d)", i, len(sector.Pieces), sector.SectorNumber, p.DealInfo.DealID)
// Not much to do here (and this can only happen for old spacerace sectors)
return ctx.Send(SectorRemove{})
}
diff --git a/extern/storage-sealing/states_sealing.go b/extern/storage-sealing/states_sealing.go
index fca4a8699..212ec8a40 100644
--- a/extern/storage-sealing/states_sealing.go
+++ b/extern/storage-sealing/states_sealing.go
@@ -59,6 +59,10 @@ func (m *Sealing) handlePacking(ctx statemachine.Context, sector SectorInfo) err
return ctx.Send(SectorPacked{FillerPieces: fillerPieces})
}
+func checkTicketExpired(sector SectorInfo, epoch abi.ChainEpoch) bool {
+ return epoch-sector.TicketEpoch > MaxTicketAge // TODO: allow configuring expected seal durations
+}
+
func (m *Sealing) getTicket(ctx statemachine.Context, sector SectorInfo) (abi.SealRandomness, abi.ChainEpoch, error) {
tok, epoch, err := m.api.ChainHead(ctx.Context())
if err != nil {
@@ -79,6 +83,10 @@ func (m *Sealing) getTicket(ctx statemachine.Context, sector SectorInfo) (abi.Se
if pci != nil {
ticketEpoch = pci.Info.SealRandEpoch
+
+ if checkTicketExpired(sector, ticketEpoch) {
+ return nil, 0, xerrors.Errorf("ticket expired for precommitted sector")
+ }
}
rand, err := m.api.ChainGetRandomnessFromTickets(ctx.Context(), tok, crypto.DomainSeparationTag_SealRandomness, ticketEpoch, buf.Bytes())
@@ -93,8 +101,8 @@ func (m *Sealing) handleGetTicket(ctx statemachine.Context, sector SectorInfo) e
ticketValue, ticketEpoch, err := m.getTicket(ctx, sector)
if err != nil {
allocated, aerr := m.api.StateMinerSectorAllocated(ctx.Context(), m.maddr, sector.SectorNumber, nil)
- if aerr == nil {
- log.Errorf("error checking if sector is allocated: %+v", err)
+ if aerr != nil {
+ log.Errorf("error checking if sector is allocated: %+v", aerr)
}
if allocated {
@@ -132,25 +140,14 @@ func (m *Sealing) handlePreCommit1(ctx statemachine.Context, sector SectorInfo)
}
}
- tok, height, err := m.api.ChainHead(ctx.Context())
+ _, height, err := m.api.ChainHead(ctx.Context())
if err != nil {
log.Errorf("handlePreCommit1: api error, not proceeding: %+v", err)
return nil
}
- if height-sector.TicketEpoch > MaxTicketAge {
- pci, err := m.api.StateSectorPreCommitInfo(ctx.Context(), m.maddr, sector.SectorNumber, tok)
- if err != nil {
- log.Errorf("getting precommit info: %+v", err)
- }
-
- if pci == nil {
- return ctx.Send(SectorOldTicket{}) // go get new ticket
- }
-
- // TODO: allow configuring expected seal durations, if we're here, it's
- // pretty unlikely that we'll precommit on time (unless the miner
- // process has just restarted and the worker had the result ready)
+ if checkTicketExpired(sector, height) {
+ return ctx.Send(SectorOldTicket{}) // go get new ticket
}
pc1o, err := m.sealer.SealPreCommit1(sector.sealingCtx(ctx.Context()), m.minerSector(sector.SectorType, sector.SectorNumber), sector.TicketValue, sector.pieceInfos())
@@ -297,8 +294,10 @@ func (m *Sealing) handlePreCommitWait(ctx statemachine.Context, sector SectorInf
switch mw.Receipt.ExitCode {
case exitcode.Ok:
// this is what we expect
+ case exitcode.SysErrInsufficientFunds:
+ fallthrough
case exitcode.SysErrOutOfGas:
- // gas estimator guessed a wrong number
+ // gas estimator guessed a wrong number / out of funds:
return ctx.Send(SectorRetryPreCommit{})
default:
log.Error("sector precommit failed: ", mw.Receipt.ExitCode)
@@ -476,8 +475,10 @@ func (m *Sealing) handleCommitWait(ctx statemachine.Context, sector SectorInfo)
switch mw.Receipt.ExitCode {
case exitcode.Ok:
// this is what we expect
+ case exitcode.SysErrInsufficientFunds:
+ fallthrough
case exitcode.SysErrOutOfGas:
- // gas estimator guessed a wrong number
+ // gas estimator guessed a wrong number / out of funds
return ctx.Send(SectorRetrySubmitCommit{})
default:
return ctx.Send(SectorCommitFailed{xerrors.Errorf("submitting sector proof failed (exit=%d, msg=%s) (t:%x; s:%x(%d); p:%x)", mw.Receipt.ExitCode, sector.CommitMessage, sector.TicketValue, sector.SeedValue, sector.SeedEpoch, sector.Proof)})
diff --git a/go.mod b/go.mod
index 60c9c7586..6b62ec67d 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module github.com/filecoin-project/lotus
-go 1.14
+go 1.15
require (
contrib.go.opencensus.io/exporter/jaeger v0.1.0
@@ -8,6 +8,7 @@ require (
github.com/BurntSushi/toml v0.3.1
github.com/GeertJohan/go.rice v1.0.0
github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee
+ github.com/Jeffail/gabs v1.4.0
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129
@@ -27,11 +28,12 @@ require (
github.com/filecoin-project/go-amt-ipld/v2 v2.1.1-0.20201006184820-924ee87a1349 // indirect
github.com/filecoin-project/go-bitfield v0.2.3-0.20201110211213-fe2c1862e816
github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2
+ github.com/filecoin-project/go-commp-utils v0.0.0-20201119054358-b88f7a96a434
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03
- github.com/filecoin-project/go-data-transfer v1.2.0
- github.com/filecoin-project/go-fil-commcid v0.0.0-20200716160307-8f644712406f
- github.com/filecoin-project/go-fil-markets v1.0.5-0.20201113164554-c5eba40d5335
- github.com/filecoin-project/go-jsonrpc v0.1.2-0.20201008195726-68c6a2704e49
+ github.com/filecoin-project/go-data-transfer v1.2.2
+ github.com/filecoin-project/go-fil-commcid v0.0.0-20201016201715-d41df56b4f6a
+ github.com/filecoin-project/go-fil-markets v1.0.9
+ github.com/filecoin-project/go-jsonrpc v0.1.2
github.com/filecoin-project/go-multistore v0.0.3
github.com/filecoin-project/go-padreader v0.0.0-20200903213702-ed5fae088b20
github.com/filecoin-project/go-paramfetch v0.0.2-0.20200701152213-3e0f0afdc261
@@ -67,7 +69,7 @@ require (
github.com/ipfs/go-ds-pebble v0.0.2-0.20200921225637-ce220f8ac459
github.com/ipfs/go-filestore v1.0.0
github.com/ipfs/go-fs-lock v0.0.6
- github.com/ipfs/go-graphsync v0.5.0
+ github.com/ipfs/go-graphsync v0.5.1
github.com/ipfs/go-ipfs-blockstore v1.0.3
github.com/ipfs/go-ipfs-chunker v0.0.5
github.com/ipfs/go-ipfs-ds-help v1.0.0
@@ -87,7 +89,7 @@ require (
github.com/ipfs/go-path v0.0.7
github.com/ipfs/go-unixfs v0.2.4
github.com/ipfs/interface-go-ipfs-core v0.2.3
- github.com/ipld/go-car v0.1.1-0.20200923150018-8cdef32e2da4
+ github.com/ipld/go-car v0.1.1-0.20201119040415-11b6074b6d4d
github.com/ipld/go-ipld-prime v0.5.1-0.20201021195245-109253e8a018
github.com/kelseyhightower/envconfig v1.4.0
github.com/lib/pq v1.7.0
@@ -131,10 +133,10 @@ require (
github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7
github.com/whyrusleeping/pubsub v0.0.0-20131020042734-02de8aa2db3d
github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542
- go.opencensus.io v0.22.4
+ go.opencensus.io v0.22.5
go.uber.org/dig v1.10.0 // indirect
go.uber.org/fx v1.9.0
- go.uber.org/multierr v1.5.0
+ go.uber.org/multierr v1.6.0
go.uber.org/zap v1.16.0
golang.org/x/net v0.0.0-20201021035429-f5854403a974
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
diff --git a/go.sum b/go.sum
index 542f45b45..623aa7b6f 100644
--- a/go.sum
+++ b/go.sum
@@ -40,6 +40,8 @@ github.com/GeertJohan/go.rice v1.0.0 h1:KkI6O9uMaQU3VEKaj01ulavtF7o1fWT7+pk/4voi
github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee h1:8doiS7ib3zi6/K172oDhSKU0dJ/miJramo9NITOMyZQ=
github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee/go.mod h1:W0GbEAA4uFNYOGG2cJpmFJ04E6SD1NLELPYZB57/7AY=
+github.com/Jeffail/gabs v1.4.0 h1://5fYRRTq1edjfIrQGvdkcd22pkYUrHZ5YC/H2GJVAo=
+github.com/Jeffail/gabs v1.4.0/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
@@ -245,26 +247,29 @@ github.com/filecoin-project/go-bitfield v0.2.3-0.20201110211213-fe2c1862e816 h1:
github.com/filecoin-project/go-bitfield v0.2.3-0.20201110211213-fe2c1862e816/go.mod h1:CNl9WG8hgR5mttCnUErjcQjGvuiZjRqK9rHVBsQF4oM=
github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2 h1:av5fw6wmm58FYMgJeoB/lK9XXrgdugYiTqkdxjTy9k8=
github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2/go.mod h1:pqTiPHobNkOVM5thSRsHYjyQfq7O5QSCMhvuu9JoDlg=
+github.com/filecoin-project/go-commp-utils v0.0.0-20201119054358-b88f7a96a434 h1:0kHszkYP3hgApcjl5x4rpwONhN9+j7XDobf6at5XfHs=
+github.com/filecoin-project/go-commp-utils v0.0.0-20201119054358-b88f7a96a434/go.mod h1:6s95K91mCyHY51RPWECZieD3SGWTqIFLf1mPOes9l5U=
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 h1:2pMXdBnCiXjfCYx/hLqFxccPoqsSveQFxVLvNxy9bus=
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03/go.mod h1:+viYnvGtUTgJRdy6oaeF4MTFKAfatX071MPDPBL11EQ=
github.com/filecoin-project/go-data-transfer v1.0.1 h1:5sYKDbstyDsdJpVP4UGUW6+BgCNfgnH8hQgf0E3ZAno=
github.com/filecoin-project/go-data-transfer v1.0.1/go.mod h1:UxvfUAY9v3ub0a21BSK9u3pB2aq30Y0KMsG+w9/ysyo=
-github.com/filecoin-project/go-data-transfer v1.2.0 h1:LM+K+J+y9t8e3gYskJHWDlyHJsF6aaxoHOP+HIiVE1U=
-github.com/filecoin-project/go-data-transfer v1.2.0/go.mod h1:ZAH51JZFR8NZC4FPiDPG+swjgui0q6zTMJbztc6pHhY=
+github.com/filecoin-project/go-data-transfer v1.2.2 h1:zBeUNqSXgYbHqyl3mnwQU5GdOM1h0ecbqc6yvqmHsCQ=
+github.com/filecoin-project/go-data-transfer v1.2.2/go.mod h1:ZAH51JZFR8NZC4FPiDPG+swjgui0q6zTMJbztc6pHhY=
github.com/filecoin-project/go-ds-versioning v0.1.0 h1:y/X6UksYTsK8TLCI7rttCKEvl8btmWxyFMEeeWGUxIQ=
github.com/filecoin-project/go-ds-versioning v0.1.0/go.mod h1:mp16rb4i2QPmxBnmanUx8i/XANp+PFCCJWiAb+VW4/s=
github.com/filecoin-project/go-fil-commcid v0.0.0-20200716160307-8f644712406f h1:GxJzR3oRIMTPtpZ0b7QF8FKPK6/iPAc7trhlL5k/g+s=
github.com/filecoin-project/go-fil-commcid v0.0.0-20200716160307-8f644712406f/go.mod h1:Eaox7Hvus1JgPrL5+M3+h7aSPHc0cVqpSxA+TxIEpZQ=
-github.com/filecoin-project/go-fil-markets v1.0.5-0.20201113164554-c5eba40d5335 h1:DF8eu0WdEBnSVdu71+jfT4YMk6fO7AIJk2ZiWd3l15c=
+github.com/filecoin-project/go-fil-commcid v0.0.0-20201016201715-d41df56b4f6a h1:hyJ+pUm/4U4RdEZBlg6k8Ma4rDiuvqyGpoICXAxwsTg=
+github.com/filecoin-project/go-fil-commcid v0.0.0-20201016201715-d41df56b4f6a/go.mod h1:Eaox7Hvus1JgPrL5+M3+h7aSPHc0cVqpSxA+TxIEpZQ=
github.com/filecoin-project/go-fil-markets v1.0.5-0.20201113164554-c5eba40d5335/go.mod h1:AJySOJC00JRWEZzRG2KsfUnqEf5ITXxeX09BE9N4f9c=
+github.com/filecoin-project/go-fil-markets v1.0.9 h1:bGWo6xoXV9zMPYgbplQDtUREogDuKPiSY1CYwxV5cOY=
+github.com/filecoin-project/go-fil-markets v1.0.9/go.mod h1:uOikzYK7aNbSWMczCp6Ru257ML4PplLRBfDk/NAOgaY=
github.com/filecoin-project/go-hamt-ipld v0.1.5 h1:uoXrKbCQZ49OHpsTCkrThPNelC4W3LPEk0OrS/ytIBM=
-github.com/filecoin-project/go-hamt-ipld v0.1.5 h1:uoXrKbCQZ49OHpsTCkrThPNelC4W3LPEk0OrS/ytIBM=
-github.com/filecoin-project/go-hamt-ipld v0.1.5/go.mod h1:6Is+ONR5Cd5R6XZoCse1CWaXZc0Hdb/JeX+EQCQzX24=
github.com/filecoin-project/go-hamt-ipld v0.1.5/go.mod h1:6Is+ONR5Cd5R6XZoCse1CWaXZc0Hdb/JeX+EQCQzX24=
github.com/filecoin-project/go-hamt-ipld/v2 v2.0.0 h1:b3UDemBYN2HNfk3KOXNuxgTTxlWi3xVvbQP0IT38fvM=
github.com/filecoin-project/go-hamt-ipld/v2 v2.0.0/go.mod h1:7aWZdaQ1b16BVoQUYR+eEvrDCGJoPLxFpDynFjYfBjI=
-github.com/filecoin-project/go-jsonrpc v0.1.2-0.20201008195726-68c6a2704e49 h1:FSY245KeXFCUgyfFEu+bhrZNk8BGGJyfpSmQl2aiPU8=
-github.com/filecoin-project/go-jsonrpc v0.1.2-0.20201008195726-68c6a2704e49/go.mod h1:XBBpuKIMaXIIzeqzO1iucq4GvbF8CxmXRFoezRh+Cx4=
+github.com/filecoin-project/go-jsonrpc v0.1.2 h1:MTebUawBHLxxY9gDi1WXuGc89TWIDmsgoDqeZSk9KRw=
+github.com/filecoin-project/go-jsonrpc v0.1.2/go.mod h1:XBBpuKIMaXIIzeqzO1iucq4GvbF8CxmXRFoezRh+Cx4=
github.com/filecoin-project/go-multistore v0.0.3 h1:vaRBY4YiA2UZFPK57RNuewypB8u0DzzQwqsL0XarpnI=
github.com/filecoin-project/go-multistore v0.0.3/go.mod h1:kaNqCC4IhU4B1uyr7YWFHd23TL4KM32aChS0jNkyUvQ=
github.com/filecoin-project/go-padreader v0.0.0-20200903213702-ed5fae088b20 h1:+/4aUeUoKr6AKfPE3mBhXA5spIV6UcKdTYDPNU2Tdmg=
@@ -273,7 +278,6 @@ github.com/filecoin-project/go-paramfetch v0.0.2-0.20200701152213-3e0f0afdc261 h
github.com/filecoin-project/go-paramfetch v0.0.2-0.20200701152213-3e0f0afdc261/go.mod h1:fZzmf4tftbwf9S37XRifoJlz7nCjRdIrMGLR07dKLCc=
github.com/filecoin-project/go-state-types v0.0.0-20200903145444-247639ffa6ad/go.mod h1:IQ0MBPnonv35CJHtWSN3YY1Hz2gkPru1Q9qoaYLxx9I=
github.com/filecoin-project/go-state-types v0.0.0-20200904021452-1883f36ca2f4/go.mod h1:IQ0MBPnonv35CJHtWSN3YY1Hz2gkPru1Q9qoaYLxx9I=
-github.com/filecoin-project/go-state-types v0.0.0-20200928172055-2df22083d8ab h1:cEDC5Ei8UuT99hPWhCjA72SM9AuRtnpvdSTIYbnzN8I=
github.com/filecoin-project/go-state-types v0.0.0-20200928172055-2df22083d8ab/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g=
github.com/filecoin-project/go-state-types v0.0.0-20201102161440-c8033295a1fc h1:+hbMY4Pcx2oizrfH08VWXwrj5mU8aJT6g0UNxGHFCGU=
github.com/filecoin-project/go-state-types v0.0.0-20201102161440-c8033295a1fc/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g=
@@ -284,7 +288,6 @@ 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/specs-actors v0.9.4/go.mod h1:BStZQzx5x7TmCkLv0Bpa07U6cPKol6fd3w9KjMPZ6Z4=
-github.com/filecoin-project/specs-actors v0.9.12 h1:iIvk58tuMtmloFNHhAOQHG+4Gci6Lui0n7DYQGi3cJk=
github.com/filecoin-project/specs-actors v0.9.12/go.mod h1:TS1AW/7LbG+615j4NsjMK1qlpAwaFsG9w0V2tg2gSao=
github.com/filecoin-project/specs-actors v0.9.13 h1:rUEOQouefi9fuVY/2HOroROJlZbOzWYXXeIh41KF2M4=
github.com/filecoin-project/specs-actors v0.9.13/go.mod h1:TS1AW/7LbG+615j4NsjMK1qlpAwaFsG9w0V2tg2gSao=
@@ -376,7 +379,6 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.2-0.20190904063534-ff6b7dc882cf h1:gFVkHXmVAhEbxZVDln5V9GKrLaluNoFHDbrZwAWZgws=
github.com/golang/snappy v0.0.2-0.20190904063534-ff6b7dc882cf/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@@ -385,7 +387,6 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@@ -401,7 +402,6 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OI
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -501,7 +501,6 @@ github.com/ipfs/go-blockservice v0.0.3/go.mod h1:/NNihwTi6V2Yr6g8wBI+BSwPuURpBRM
github.com/ipfs/go-blockservice v0.0.7/go.mod h1:EOfb9k/Y878ZTRY/CH0x5+ATtaipfbRhbvNSdgc/7So=
github.com/ipfs/go-blockservice v0.1.0/go.mod h1:hzmMScl1kXHg3M2BjTymbVPjv627N7sYcvYaKbop39M=
github.com/ipfs/go-blockservice v0.1.3/go.mod h1:OTZhFpkgY48kNzbgyvcexW9cHrpjBYIjSR0KoDOFOLU=
-github.com/ipfs/go-blockservice v0.1.4-0.20200624145336-a978cec6e834 h1:hFJoI1D2a3MqiNkSb4nKwrdkhCngUxUTFNwVwovZX2s=
github.com/ipfs/go-blockservice v0.1.4-0.20200624145336-a978cec6e834/go.mod h1:OTZhFpkgY48kNzbgyvcexW9cHrpjBYIjSR0KoDOFOLU=
github.com/ipfs/go-blockservice v0.1.4 h1:Vq+MlsH8000KbbUciRyYMEw/NNP8UAGmcqKi4uWmFGA=
github.com/ipfs/go-blockservice v0.1.4/go.mod h1:OTZhFpkgY48kNzbgyvcexW9cHrpjBYIjSR0KoDOFOLU=
@@ -526,7 +525,6 @@ github.com/ipfs/go-datastore v0.3.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRV
github.com/ipfs/go-datastore v0.4.0/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA=
github.com/ipfs/go-datastore v0.4.1/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA=
github.com/ipfs/go-datastore v0.4.2/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA=
-github.com/ipfs/go-datastore v0.4.4 h1:rjvQ9+muFaJ+QZ7dN5B1MSDNQ0JVZKkkES/rMZmA8X8=
github.com/ipfs/go-datastore v0.4.4/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA=
github.com/ipfs/go-datastore v0.4.5 h1:cwOUcGMLdLPWgu3SlrCckCMznaGADbPqE0r8h768/Dg=
github.com/ipfs/go-datastore v0.4.5/go.mod h1:eXTcaaiN6uOlVCLS9GjJUJtlvJfM3xk23w3fyfrmmJs=
@@ -555,19 +553,18 @@ github.com/ipfs/go-filestore v1.0.0/go.mod h1:/XOCuNtIe2f1YPbiXdYvD0BKLA0JR1MgPi
github.com/ipfs/go-fs-lock v0.0.6 h1:sn3TWwNVQqSeNjlWy6zQ1uUGAZrV3hPOyEA6y1/N2a0=
github.com/ipfs/go-fs-lock v0.0.6/go.mod h1:OTR+Rj9sHiRubJh3dRhD15Juhd/+w6VPOY28L7zESmM=
github.com/ipfs/go-graphsync v0.1.0/go.mod h1:jMXfqIEDFukLPZHqDPp8tJMbHO9Rmeb9CEGevngQbmE=
-github.com/ipfs/go-graphsync v0.4.2 h1:Y/jt5r619yj0LI7OLtGKh4jYm8goYUcuJ09y7TZ3zMo=
github.com/ipfs/go-graphsync v0.4.2/go.mod h1:/VmbZTUdUMTbNkgzAiCEucIIAU3BkLE2cZrDCVUhyi0=
-github.com/ipfs/go-graphsync v0.4.3 h1:2t+oCpufufs1oqChoWiIK7V5uC1XCtf06PK9nqMV6pM=
github.com/ipfs/go-graphsync v0.4.3/go.mod h1:mPOwDYv128gf8gxPFgXnz4fNrSYPsWyqisJ7ych+XDY=
github.com/ipfs/go-graphsync v0.5.0 h1:iaByvxq88Ys1KcaQzTS1wmRhNsNEo3SaUiSGqTSbGmM=
github.com/ipfs/go-graphsync v0.5.0/go.mod h1:e2ZxnClqBBYAtd901g9vXMJzS47labjAtOzsWtOzKNk=
+github.com/ipfs/go-graphsync v0.5.1 h1:4fXBRvRKicTgTmCFMmEua/H5jvmAOLgU9Z7PCPWt2ec=
+github.com/ipfs/go-graphsync v0.5.1/go.mod h1:e2ZxnClqBBYAtd901g9vXMJzS47labjAtOzsWtOzKNk=
github.com/ipfs/go-hamt-ipld v0.1.1 h1:0IQdvwnAAUKmDE+PMJa5y1QiwOPHpI9+eAbQEEEYthk=
github.com/ipfs/go-hamt-ipld v0.1.1/go.mod h1:1EZCr2v0jlCnhpa+aZ0JZYp8Tt2w16+JJOAVz17YcDk=
github.com/ipfs/go-ipfs-blockstore v0.0.1/go.mod h1:d3WClOmRQKFnJ0Jz/jj/zmksX0ma1gROTlovZKBmN08=
github.com/ipfs/go-ipfs-blockstore v0.1.0/go.mod h1:5aD0AvHPi7mZc6Ci1WCAhiBQu2IsfTduLl+422H6Rqw=
github.com/ipfs/go-ipfs-blockstore v0.1.4/go.mod h1:Jxm3XMVjh6R17WvxFEiyKBLUGr86HgIYJW/D/MwqeYQ=
github.com/ipfs/go-ipfs-blockstore v1.0.0/go.mod h1:knLVdhVU9L7CC4T+T4nvGdeUIPAXlnd9zmXfp+9MIjU=
-github.com/ipfs/go-ipfs-blockstore v1.0.1 h1:fnuVj4XdZp4yExhd0CnUwAiMNJHiPnfInhiuwz4lW1w=
github.com/ipfs/go-ipfs-blockstore v1.0.1/go.mod h1:MGNZlHNEnR4KGgPHM3/k8lBySIOK2Ve+0KjZubKlaOE=
github.com/ipfs/go-ipfs-blockstore v1.0.3 h1:RDhK6fdg5YsonkpMuMpdvk/pRtOQlrIRIybuQfkvB2M=
github.com/ipfs/go-ipfs-blockstore v1.0.3/go.mod h1:MGNZlHNEnR4KGgPHM3/k8lBySIOK2Ve+0KjZubKlaOE=
@@ -669,15 +666,14 @@ github.com/ipfs/iptb v1.4.0 h1:YFYTrCkLMRwk/35IMyC6+yjoQSHTEcNcefBStLJzgvo=
github.com/ipfs/iptb v1.4.0/go.mod h1:1rzHpCYtNp87/+hTxG5TfCVn/yMY3dKnLn8tBiMfdmg=
github.com/ipfs/iptb-plugins v0.2.1 h1:au4HWn9/pRPbkxA08pDx2oRAs4cnbgQWgV0teYXuuGA=
github.com/ipfs/iptb-plugins v0.2.1/go.mod h1:QXMbtIWZ+jRsW8a4h13qAKU7jcM7qaittO8wOsTP0Rs=
-github.com/ipld/go-car v0.1.1-0.20200923150018-8cdef32e2da4 h1:6phjU3kXvCEWOZpu+Ob0w6DzgPFZmDLgLPxJhD8RxEY=
github.com/ipld/go-car v0.1.1-0.20200923150018-8cdef32e2da4/go.mod h1:xrMEcuSq+D1vEwl+YAXsg/JfA98XGpXDwnkIL4Aimqw=
+github.com/ipld/go-car v0.1.1-0.20201119040415-11b6074b6d4d h1:iphSzTuPqyDgH7WUVZsdqUnQNzYgIblsVr1zhVNA33U=
+github.com/ipld/go-car v0.1.1-0.20201119040415-11b6074b6d4d/go.mod h1:2Gys8L8MJ6zkh1gktTSXreY63t4UbyvNp5JaudTyxHQ=
github.com/ipld/go-ipld-prime v0.0.2-0.20200428162820-8b59dc292b8e/go.mod h1:uVIwe/u0H4VdKv3kaN1ck7uCb6yD9cFLS9/ELyXbsw8=
-github.com/ipld/go-ipld-prime v0.5.1-0.20200828233916-988837377a7f h1:XpOuNQ5GbXxUcSukbQcW9jkE7REpaFGJU2/T00fo9kA=
github.com/ipld/go-ipld-prime v0.5.1-0.20200828233916-988837377a7f/go.mod h1:0xEgdD6MKbZ1vF0GC+YcR/C4SQCAlRuOjIJ2i0HxqzM=
github.com/ipld/go-ipld-prime v0.5.1-0.20201021195245-109253e8a018 h1:RbRHv8epkmvBYA5cGfz68GUSbOgx5j/7ObLIl4Rsif0=
github.com/ipld/go-ipld-prime v0.5.1-0.20201021195245-109253e8a018/go.mod h1:0xEgdD6MKbZ1vF0GC+YcR/C4SQCAlRuOjIJ2i0HxqzM=
github.com/ipld/go-ipld-prime-proto v0.0.0-20200428191222-c1ffdadc01e1/go.mod h1:OAV6xBmuTLsPZ+epzKkPB1e25FHk/vCtyatkdHcArLs=
-github.com/ipld/go-ipld-prime-proto v0.0.0-20200922192210-9a2bfd4440a6 h1:6Mq+tZGSEMEoJJ1NbJRhddeelkXZcU8yfH/ZRYUo/Es=
github.com/ipld/go-ipld-prime-proto v0.0.0-20200922192210-9a2bfd4440a6/go.mod h1:3pHYooM9Ea65jewRwrb2u5uHZCNkNTe9ABsVB+SrkH0=
github.com/ipld/go-ipld-prime-proto v0.1.0 h1:j7gjqrfwbT4+gXpHwEx5iMssma3mnctC7YaCimsFP70=
github.com/ipld/go-ipld-prime-proto v0.1.0/go.mod h1:11zp8f3sHVgIqtb/c9Kr5ZGqpnCLF1IVTNOez9TopzE=
@@ -732,7 +728,6 @@ github.com/kabukky/httpscerts v0.0.0-20150320125433-617593d7dcb3/go.mod h1:BYpt4
github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
-github.com/kilic/bls12-381 v0.0.0-20200607163746-32e1441c8a9f h1:qET3Wx0v8tMtoTOQnsJXVvqvCopSf48qobR6tcJuDHo=
github.com/kilic/bls12-381 v0.0.0-20200607163746-32e1441c8a9f/go.mod h1:XXfR6YFCRSrkEXbNlIyDsgXVNJWVUV30m/ebkVy9n6s=
github.com/kilic/bls12-381 v0.0.0-20200731194930-64c428e1bff5/go.mod h1:XXfR6YFCRSrkEXbNlIyDsgXVNJWVUV30m/ebkVy9n6s=
github.com/kilic/bls12-381 v0.0.0-20200820230200-6b2c19996391 h1:51kHw7l/dUDdOdW06AlUGT5jnpj6nqQSILebcsikSjA=
@@ -852,7 +847,6 @@ github.com/libp2p/go-libp2p-core v0.5.5/go.mod h1:vj3awlOr9+GMZJFH9s4mpt9RHHgGqe
github.com/libp2p/go-libp2p-core v0.5.6/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo=
github.com/libp2p/go-libp2p-core v0.5.7/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo=
github.com/libp2p/go-libp2p-core v0.6.0/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo=
-github.com/libp2p/go-libp2p-core v0.6.1 h1:XS+Goh+QegCDojUZp00CaPMfiEADCrLjNZskWE7pvqs=
github.com/libp2p/go-libp2p-core v0.6.1/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8=
github.com/libp2p/go-libp2p-core v0.7.0 h1:4a0TMjrWNTZlNvcqxZmrMRDi/NQWrhwO2pkTuLSQ/IQ=
github.com/libp2p/go-libp2p-core v0.7.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8=
@@ -902,7 +896,6 @@ github.com/libp2p/go-libp2p-net v0.0.2/go.mod h1:Yt3zgmlsHOgUWSXmt5V/Jpz9upuJBE8
github.com/libp2p/go-libp2p-netutil v0.0.1/go.mod h1:GdusFvujWZI9Vt0X5BKqwWWmZFxecf9Gt03cKxm2f/Q=
github.com/libp2p/go-libp2p-netutil v0.1.0 h1:zscYDNVEcGxyUpMd0JReUZTrpMfia8PmLKcKF72EAMQ=
github.com/libp2p/go-libp2p-netutil v0.1.0/go.mod h1:3Qv/aDqtMLTUyQeundkKsA+YCThNdbQD54k3TqjpbFU=
-github.com/libp2p/go-libp2p-noise v0.1.1 h1:vqYQWvnIcHpIoWJKC7Al4D6Hgj0H012TuXRhPwSMGpQ=
github.com/libp2p/go-libp2p-noise v0.1.1/go.mod h1:QDFLdKX7nluB7DEnlVPbz7xlLHdwHFA9HiohJRr3vwM=
github.com/libp2p/go-libp2p-noise v0.1.2 h1:IH9GRihQJTx56obm+GnpdPX4KeVIlvpXrP6xnJ0wxWk=
github.com/libp2p/go-libp2p-noise v0.1.2/go.mod h1:9B10b7ueo7TIxZHHcjcDCo5Hd6kfKT2m77by82SFRfE=
@@ -958,7 +951,6 @@ github.com/libp2p/go-libp2p-swarm v0.2.2/go.mod h1:fvmtQ0T1nErXym1/aa1uJEyN7JzaT
github.com/libp2p/go-libp2p-swarm v0.2.3/go.mod h1:P2VO/EpxRyDxtChXz/VPVXyTnszHvokHKRhfkEgFKNM=
github.com/libp2p/go-libp2p-swarm v0.2.4/go.mod h1:/xIpHFPPh3wmSthtxdGbkHZ0OET1h/GGZes8Wku/M5Y=
github.com/libp2p/go-libp2p-swarm v0.2.7/go.mod h1:ZSJ0Q+oq/B1JgfPHJAT2HTall+xYRNYp1xs4S2FBWKA=
-github.com/libp2p/go-libp2p-swarm v0.2.8 h1:cIUUvytBzNQmGSjnXFlI6UpoBGsaud82mJPIJVfkDlg=
github.com/libp2p/go-libp2p-swarm v0.2.8/go.mod h1:JQKMGSth4SMqonruY0a8yjlPVIkb0mdNSwckW7OYziM=
github.com/libp2p/go-libp2p-swarm v0.3.0/go.mod h1:hdv95GWCTmzkgeJpP+GK/9D9puJegb7H57B5hWQR5Kk=
github.com/libp2p/go-libp2p-swarm v0.3.1 h1:UTobu+oQHGdXTOGpZ4RefuVqYoJXcT0EBtSR74m2LkI=
@@ -969,7 +961,6 @@ github.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MB
github.com/libp2p/go-libp2p-testing v0.0.4/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E=
github.com/libp2p/go-libp2p-testing v0.1.0/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0=
github.com/libp2p/go-libp2p-testing v0.1.1/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0=
-github.com/libp2p/go-libp2p-testing v0.1.2-0.20200422005655-8775583591d8 h1:v4dvk7YEW8buwCdIVWnhpv0Hp/AAJKRWIxBhmLRZrsk=
github.com/libp2p/go-libp2p-testing v0.1.2-0.20200422005655-8775583591d8/go.mod h1:Qy8sAncLKpwXtS2dSnDOP8ktexIAHKu+J+pnZOFZLTc=
github.com/libp2p/go-libp2p-testing v0.3.0 h1:ZiBYstPamsi7y6NJZebRudUzsYmVkt998hltyLqf8+g=
github.com/libp2p/go-libp2p-testing v0.3.0/go.mod h1:efZkql4UZ7OVsEfaxNHZPzIehtsBXMrXnCfJIgDti5g=
@@ -991,9 +982,7 @@ github.com/libp2p/go-libp2p-yamux v0.2.1/go.mod h1:1FBXiHDk1VyRM1C0aez2bCfHQ4vMZ
github.com/libp2p/go-libp2p-yamux v0.2.2/go.mod h1:lIohaR0pT6mOt0AZ0L2dFze9hds9Req3OfS+B+dv4qw=
github.com/libp2p/go-libp2p-yamux v0.2.5/go.mod h1:Zpgj6arbyQrmZ3wxSZxfBmbdnWtbZ48OpsfmQVTErwA=
github.com/libp2p/go-libp2p-yamux v0.2.7/go.mod h1:X28ENrBMU/nm4I3Nx4sZ4dgjZ6VhLEn0XhIoZ5viCwU=
-github.com/libp2p/go-libp2p-yamux v0.2.8 h1:0s3ELSLu2O7hWKfX1YjzudBKCP0kZ+m9e2+0veXzkn4=
github.com/libp2p/go-libp2p-yamux v0.2.8/go.mod h1:/t6tDqeuZf0INZMTgd0WxIRbtK2EzI2h7HbFm9eAKI4=
-github.com/libp2p/go-libp2p-yamux v0.4.0 h1:qunEZzWwwmfSBYTtSyd81PlD1TjB5uuWcGYHWVXLbUg=
github.com/libp2p/go-libp2p-yamux v0.4.0/go.mod h1:+DWDjtFMzoAwYLVkNZftoucn7PelNoy5nm3tZ3/Zw30=
github.com/libp2p/go-libp2p-yamux v0.4.1 h1:TJxRVPY9SjH7TNrNC80l1OJMBiWhs1qpKmeB+1Ug3xU=
github.com/libp2p/go-libp2p-yamux v0.4.1/go.mod h1:FA/NjRYRVNjqOzpGuGqcruH7jAU2mYIjtKBicVOL3dc=
@@ -1007,7 +996,6 @@ github.com/libp2p/go-mplex v0.0.3/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTW
github.com/libp2p/go-mplex v0.0.4/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTWe7l4Yd0=
github.com/libp2p/go-mplex v0.1.0/go.mod h1:SXgmdki2kwCUlCCbfGLEgHjC4pFqhTp0ZoV6aiKgxDU=
github.com/libp2p/go-mplex v0.1.1/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk=
-github.com/libp2p/go-mplex v0.1.2 h1:qOg1s+WdGLlpkrczDqmhYzyk3vCfsQ8+RxRTQjOZWwI=
github.com/libp2p/go-mplex v0.1.2/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk=
github.com/libp2p/go-mplex v0.2.0 h1:Ov/D+8oBlbRkjBs1R1Iua8hJ8cUfbdiW8EOdZuxcgaI=
github.com/libp2p/go-mplex v0.2.0/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ=
@@ -1071,9 +1059,7 @@ github.com/libp2p/go-yamux v1.3.0/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZ
github.com/libp2p/go-yamux v1.3.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
github.com/libp2p/go-yamux v1.3.5/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
github.com/libp2p/go-yamux v1.3.6/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
-github.com/libp2p/go-yamux v1.3.7 h1:v40A1eSPJDIZwz2AvrV3cxpTZEGDP11QJbukmEhYyQI=
github.com/libp2p/go-yamux v1.3.7/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE=
-github.com/libp2p/go-yamux v1.4.0 h1:7nqe0T95T2CWh40IdJ/tp8RMor4ubc9/wYZpB2a/Hx0=
github.com/libp2p/go-yamux v1.4.0/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE=
github.com/libp2p/go-yamux v1.4.1 h1:P1Fe9vF4th5JOxxgQvfbOHkrGqIZniTLf+ddhZp8YTI=
github.com/libp2p/go-yamux v1.4.1/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE=
@@ -1100,14 +1086,12 @@ github.com/marten-seemann/qtls-go1-15 v0.1.0/go.mod h1:GyFwywLKkRt+6mfU99csTEY1j
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
-github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
-github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
@@ -1534,12 +1518,16 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0=
+go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
+go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/dig v1.10.0 h1:yLmDDj9/zuDjv3gz8GQGviXMs9TfysIUMUilCpgzUJY=
go.uber.org/dig v1.10.0/go.mod h1:X34SnWGr8Fyla9zQNO2GSO2D+TIuqB14OS8JhYocIyw=
go.uber.org/fx v1.9.0 h1:7OAz8ucp35AU8eydejpYG7QrbE8rLKzGhHbZlJi5LYY=
@@ -1551,12 +1539,13 @@ go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+
go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
+go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
+go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
-go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
@@ -1592,7 +1581,6 @@ golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
@@ -1670,7 +1658,6 @@ golang.org/x/net v0.0.0-20200519113804-d87ec0cfa476/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
@@ -1689,7 +1676,6 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1754,17 +1740,14 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 h1:OjiUf46hAmXblsZdnoSXsEUSKU8r1UEzcL5RVZ4gO9Y=
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200812155832-6a926be9bd1d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200926100807-9d91bd62050c h1:38q6VNPWR010vN82/SB121GujZNIfAUb4YttE2rhGuc=
golang.org/x/sys v0.0.0-20200926100807-9d91bd62050c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -1811,7 +1794,6 @@ golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200711155855-7342f9734a7d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/tools v0.0.0-20200827010519-17fd2f27a9e3 h1:r3P/5xOq/dK1991B65Oy6E1fRF/2d/fSYZJ/fXGVfJc=
golang.org/x/tools v0.0.0-20200827010519-17fd2f27a9e3/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20201112185108-eeaa07dd7696 h1:Bfazo+enXJET5SbHeh95NtxabJF6fJ9r/jpfRJgd3j4=
golang.org/x/tools v0.0.0-20201112185108-eeaa07dd7696/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
@@ -1881,7 +1863,6 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
-google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.31.1 h1:SfXqXS5hkufcdZ/mHtYCh53P2b+92WQq/DZcKLgsFRs=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
@@ -1893,11 +1874,9 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
-gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -1944,7 +1923,6 @@ launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80
modernc.org/cc v1.0.0 h1:nPibNuDEx6tvYrUAtvDTTw98rx5juGsa5zuDnKwEEQQ=
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8=
-modernc.org/golex v1.0.0 h1:wWpDlbK8ejRfSyi0frMyhilD3JBvtcx2AdGDnU+JtsE=
modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
modernc.org/golex v1.0.1 h1:EYKY1a3wStt0RzHaH8mdSRNg78Ub0OHxYfCRWw35YtM=
modernc.org/golex v1.0.1/go.mod h1:QCA53QtsT1NdGkaZZkF5ezFwk4IXh4BGNafAARTC254=
diff --git a/lib/commp/writer.go b/lib/commp/writer.go
deleted file mode 100644
index 4c5e3350c..000000000
--- a/lib/commp/writer.go
+++ /dev/null
@@ -1,113 +0,0 @@
-package commp
-
-import (
- "bytes"
- "math/bits"
-
- "github.com/ipfs/go-cid"
- "golang.org/x/xerrors"
-
- ffi "github.com/filecoin-project/filecoin-ffi"
- "github.com/filecoin-project/go-padreader"
- "github.com/filecoin-project/go-state-types/abi"
-
- "github.com/filecoin-project/lotus/api"
- "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper"
- "github.com/filecoin-project/lotus/extern/sector-storage/zerocomm"
-)
-
-const commPBufPad = abi.PaddedPieceSize(8 << 20)
-const CommPBuf = abi.UnpaddedPieceSize(commPBufPad - (commPBufPad / 128)) // can't use .Unpadded() for const
-
-type Writer struct {
- len int64
- buf [CommPBuf]byte
- leaves []cid.Cid
-}
-
-func (w *Writer) Write(p []byte) (int, error) {
- n := len(p)
- for len(p) > 0 {
- buffered := int(w.len % int64(len(w.buf)))
- toBuffer := len(w.buf) - buffered
- if toBuffer > len(p) {
- toBuffer = len(p)
- }
-
- copied := copy(w.buf[buffered:], p[:toBuffer])
- p = p[copied:]
- w.len += int64(copied)
-
- if copied > 0 && w.len%int64(len(w.buf)) == 0 {
- leaf, err := ffiwrapper.GeneratePieceCIDFromFile(abi.RegisteredSealProof_StackedDrg32GiBV1, bytes.NewReader(w.buf[:]), CommPBuf)
- if err != nil {
- return 0, err
- }
- w.leaves = append(w.leaves, leaf)
- }
- }
- return n, nil
-}
-
-func (w *Writer) Sum() (api.DataCIDSize, error) {
- // process last non-zero leaf if exists
- lastLen := w.len % int64(len(w.buf))
- rawLen := w.len
-
- // process remaining bit of data
- if lastLen != 0 {
- if len(w.leaves) != 0 {
- copy(w.buf[lastLen:], make([]byte, int(int64(CommPBuf)-lastLen)))
- lastLen = int64(CommPBuf)
- }
-
- r, sz := padreader.New(bytes.NewReader(w.buf[:lastLen]), uint64(lastLen))
- p, err := ffiwrapper.GeneratePieceCIDFromFile(abi.RegisteredSealProof_StackedDrg32GiBV1, r, sz)
- if err != nil {
- return api.DataCIDSize{}, err
- }
-
- if sz < CommPBuf { // special case for pieces smaller than 16MiB
- return api.DataCIDSize{
- PayloadSize: w.len,
- PieceSize: sz.Padded(),
- PieceCID: p,
- }, nil
- }
-
- w.leaves = append(w.leaves, p)
- }
-
- // pad with zero pieces to power-of-two size
- fillerLeaves := (1 << (bits.Len(uint(len(w.leaves) - 1)))) - len(w.leaves)
- for i := 0; i < fillerLeaves; i++ {
- w.leaves = append(w.leaves, zerocomm.ZeroPieceCommitment(CommPBuf))
- }
-
- if len(w.leaves) == 1 {
- return api.DataCIDSize{
- PayloadSize: rawLen,
- PieceSize: abi.PaddedPieceSize(len(w.leaves)) * commPBufPad,
- PieceCID: w.leaves[0],
- }, nil
- }
-
- pieces := make([]abi.PieceInfo, len(w.leaves))
- for i, leaf := range w.leaves {
- pieces[i] = abi.PieceInfo{
- Size: commPBufPad,
- PieceCID: leaf,
- }
- }
-
- p, err := ffi.GenerateUnsealedCID(abi.RegisteredSealProof_StackedDrg32GiBV1, pieces)
- if err != nil {
- return api.DataCIDSize{}, xerrors.Errorf("generating unsealed CID: %w", err)
- }
-
- return api.DataCIDSize{
- PayloadSize: rawLen,
- PieceSize: abi.PaddedPieceSize(len(w.leaves)) * commPBufPad,
- PieceCID: p,
- }, nil
-}
diff --git a/lib/commp/writer_test.go b/lib/commp/writer_test.go
deleted file mode 100644
index 284648e4e..000000000
--- a/lib/commp/writer_test.go
+++ /dev/null
@@ -1,88 +0,0 @@
-package commp
-
-import (
- "bytes"
- "crypto/rand"
- "fmt"
- "io"
- "io/ioutil"
- "testing"
-
- "github.com/stretchr/testify/require"
-
- "github.com/filecoin-project/go-padreader"
- "github.com/filecoin-project/go-state-types/abi"
-
- "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper"
- "github.com/filecoin-project/lotus/extern/sector-storage/zerocomm"
-)
-
-func TestWriterZero(t *testing.T) {
- for i, s := range []struct {
- writes []int
- expect abi.PaddedPieceSize
- }{
- {writes: []int{200}, expect: 256},
- {writes: []int{200, 200}, expect: 512},
-
- {writes: []int{int(CommPBuf)}, expect: commPBufPad},
- {writes: []int{int(CommPBuf) * 2}, expect: 2 * commPBufPad},
- {writes: []int{int(CommPBuf), int(CommPBuf), int(CommPBuf)}, expect: 4 * commPBufPad},
- {writes: []int{int(CommPBuf), int(CommPBuf), int(CommPBuf), int(CommPBuf), int(CommPBuf), int(CommPBuf), int(CommPBuf), int(CommPBuf), int(CommPBuf)}, expect: 16 * commPBufPad},
-
- {writes: []int{200, int(CommPBuf)}, expect: 2 * commPBufPad},
- } {
- s := s
- t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
- w := &Writer{}
- var rawSum int64
- for _, write := range s.writes {
- rawSum += int64(write)
- _, err := w.Write(make([]byte, write))
- require.NoError(t, err)
- }
-
- p, err := w.Sum()
- require.NoError(t, err)
- require.Equal(t, rawSum, p.PayloadSize)
- require.Equal(t, s.expect, p.PieceSize)
- require.Equal(t, zerocomm.ZeroPieceCommitment(s.expect.Unpadded()).String(), p.PieceCID.String())
- })
- }
-}
-
-func TestWriterData(t *testing.T) {
- dataLen := float64(CommPBuf) * 6.78
- data, _ := ioutil.ReadAll(io.LimitReader(rand.Reader, int64(dataLen)))
-
- pr, sz := padreader.New(bytes.NewReader(data), uint64(dataLen))
- exp, err := ffiwrapper.GeneratePieceCIDFromFile(abi.RegisteredSealProof_StackedDrg32GiBV1, pr, sz)
- require.NoError(t, err)
-
- w := &Writer{}
- _, err = io.Copy(w, bytes.NewReader(data))
- require.NoError(t, err)
-
- res, err := w.Sum()
- require.NoError(t, err)
-
- require.Equal(t, exp.String(), res.PieceCID.String())
-}
-
-func BenchmarkWriterZero(b *testing.B) {
- buf := make([]byte, int(CommPBuf)*b.N)
- b.SetBytes(int64(CommPBuf))
- b.ResetTimer()
-
- w := &Writer{}
-
- _, err := w.Write(buf)
- require.NoError(b, err)
- o, err := w.Sum()
-
- b.StopTimer()
-
- require.NoError(b, err)
- require.Equal(b, zerocomm.ZeroPieceCommitment(o.PieceSize.Unpadded()).String(), o.PieceCID.String())
- require.Equal(b, int64(CommPBuf)*int64(b.N), o.PayloadSize)
-}
diff --git a/lib/peermgr/peermgr.go b/lib/peermgr/peermgr.go
index 2fe54caea..ee158cc04 100644
--- a/lib/peermgr/peermgr.go
+++ b/lib/peermgr/peermgr.go
@@ -53,16 +53,24 @@ type PeerMgr struct {
h host.Host
dht *dht.IpfsDHT
- notifee *net.NotifyBundle
- filPeerEmitter event.Emitter
+ notifee *net.NotifyBundle
+ emitter event.Emitter
done chan struct{}
}
-type NewFilPeer struct {
- Id peer.ID
+type FilPeerEvt struct {
+ Type FilPeerEvtType
+ ID peer.ID
}
+type FilPeerEvtType int
+
+const (
+ AddFilPeerEvt FilPeerEvtType = iota
+ RemoveFilPeerEvt
+)
+
func NewPeerMgr(lc fx.Lifecycle, h host.Host, dht *dht.IpfsDHT, bootstrap dtypes.BootstrapPeers) (*PeerMgr, error) {
pm := &PeerMgr{
h: h,
@@ -77,16 +85,16 @@ func NewPeerMgr(lc fx.Lifecycle, h host.Host, dht *dht.IpfsDHT, bootstrap dtypes
done: make(chan struct{}),
}
- emitter, err := h.EventBus().Emitter(new(NewFilPeer))
+ emitter, err := h.EventBus().Emitter(new(FilPeerEvt))
if err != nil {
- return nil, xerrors.Errorf("creating NewFilPeer emitter: %w", err)
+ return nil, xerrors.Errorf("creating FilPeerEvt emitter: %w", err)
}
- pm.filPeerEmitter = emitter
+ pm.emitter = emitter
lc.Append(fx.Hook{
OnStop: func(ctx context.Context) error {
return multierr.Combine(
- pm.filPeerEmitter.Close(),
+ pm.emitter.Close(),
pm.Stop(ctx),
)
},
@@ -104,7 +112,7 @@ func NewPeerMgr(lc fx.Lifecycle, h host.Host, dht *dht.IpfsDHT, bootstrap dtypes
}
func (pmgr *PeerMgr) AddFilecoinPeer(p peer.ID) {
- _ = pmgr.filPeerEmitter.Emit(NewFilPeer{Id: p}) //nolint:errcheck
+ _ = pmgr.emitter.Emit(FilPeerEvt{Type: AddFilPeerEvt, ID: p}) //nolint:errcheck
pmgr.peersLk.Lock()
defer pmgr.peersLk.Unlock()
pmgr.peers[p] = time.Duration(0)
@@ -127,10 +135,19 @@ func (pmgr *PeerMgr) SetPeerLatency(p peer.ID, latency time.Duration) {
}
func (pmgr *PeerMgr) Disconnect(p peer.ID) {
+ disconnected := false
+
if pmgr.h.Network().Connectedness(p) == net.NotConnected {
pmgr.peersLk.Lock()
- defer pmgr.peersLk.Unlock()
- delete(pmgr.peers, p)
+ _, disconnected = pmgr.peers[p]
+ if disconnected {
+ delete(pmgr.peers, p)
+ }
+ pmgr.peersLk.Unlock()
+ }
+
+ if disconnected {
+ _ = pmgr.emitter.Emit(FilPeerEvt{Type: RemoveFilPeerEvt, ID: p}) //nolint:errcheck
}
}
diff --git a/lib/rpcenc/reader.go b/lib/rpcenc/reader.go
index 617c6495e..8bd512705 100644
--- a/lib/rpcenc/reader.go
+++ b/lib/rpcenc/reader.go
@@ -128,7 +128,7 @@ func ReaderParamDecoder() (http.HandlerFunc, jsonrpc.ServerOption) {
case ch <- wr:
case <-tctx.Done():
close(ch)
- log.Error("context error in reader stream handler (1): %v", tctx.Err())
+ log.Errorf("context error in reader stream handler (1): %v", tctx.Err())
resp.WriteHeader(500)
return
}
@@ -136,7 +136,7 @@ func ReaderParamDecoder() (http.HandlerFunc, jsonrpc.ServerOption) {
select {
case <-wr.wait:
case <-req.Context().Done():
- log.Error("context error in reader stream handler (2): %v", req.Context().Err())
+ log.Errorf("context error in reader stream handler (2): %v", req.Context().Err())
resp.WriteHeader(500)
return
}
diff --git a/markets/loggers/loggers.go b/markets/loggers/loggers.go
index a8e1c20aa..87c8dfe65 100644
--- a/markets/loggers/loggers.go
+++ b/markets/loggers/loggers.go
@@ -1,6 +1,7 @@
package marketevents
import (
+ datatransfer "github.com/filecoin-project/go-data-transfer"
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
"github.com/filecoin-project/go-fil-markets/storagemarket"
"github.com/filecoin-project/go-state-types/abi"
@@ -29,6 +30,23 @@ func RetrievalProviderLogger(event retrievalmarket.ProviderEvent, deal retrieval
log.Infow("retrieval event", "name", retrievalmarket.ProviderEvents[event], "deal ID", deal.ID, "receiver", deal.Receiver, "state", retrievalmarket.DealStatuses[deal.Status], "message", deal.Message)
}
+// DataTransferLogger logs events from the data transfer module
+func DataTransferLogger(event datatransfer.Event, state datatransfer.ChannelState) {
+ log.Debugw("data transfer event",
+ "name", datatransfer.Events[event.Code],
+ "status", datatransfer.Statuses[state.Status()],
+ "transfer ID", state.TransferID(),
+ "channel ID", state.ChannelID(),
+ "sent", state.Sent(),
+ "received", state.Received(),
+ "queued", state.Queued(),
+ "received count", len(state.ReceivedCids()),
+ "total size", state.TotalSize(),
+ "remote peer", state.OtherPeer(),
+ "event message", event.Message,
+ "channel message", state.Message())
+}
+
// ReadyLogger returns a function to log the results of module initialization
func ReadyLogger(module string) func(error) {
return func(err error) {
diff --git a/markets/storageadapter/client.go b/markets/storageadapter/client.go
index 36fe0d771..4d00ab258 100644
--- a/markets/storageadapter/client.go
+++ b/markets/storageadapter/client.go
@@ -217,8 +217,12 @@ func (c *ClientNodeAdapter) DealProviderCollateralBounds(ctx context.Context, si
return big.Mul(bounds.Min, big.NewInt(clientOverestimation)), bounds.Max, nil
}
-func (c *ClientNodeAdapter) OnDealSectorCommitted(ctx context.Context, provider address.Address, dealID abi.DealID, proposal market2.DealProposal, publishCid *cid.Cid, cb storagemarket.DealSectorCommittedCallback) error {
- return OnDealSectorCommitted(ctx, c, c.ev, provider, dealID, marketactor.DealProposal(proposal), publishCid, cb)
+func (c *ClientNodeAdapter) OnDealSectorPreCommitted(ctx context.Context, provider address.Address, dealID abi.DealID, proposal market2.DealProposal, publishCid *cid.Cid, cb storagemarket.DealSectorPreCommittedCallback) error {
+ return OnDealSectorPreCommitted(ctx, c, c.ev, provider, dealID, marketactor.DealProposal(proposal), publishCid, cb)
+}
+
+func (c *ClientNodeAdapter) OnDealSectorCommitted(ctx context.Context, provider address.Address, dealID abi.DealID, sectorNumber abi.SectorNumber, proposal market2.DealProposal, publishCid *cid.Cid, cb storagemarket.DealSectorCommittedCallback) error {
+ return OnDealSectorCommitted(ctx, c, c.ev, provider, dealID, sectorNumber, marketactor.DealProposal(proposal), publishCid, cb)
}
func (c *ClientNodeAdapter) OnDealExpiredOrSlashed(ctx context.Context, dealID abi.DealID, onDealExpired storagemarket.DealExpiredCallback, onDealSlashed storagemarket.DealSlashedCallback) error {
diff --git a/markets/storageadapter/ondealsectorcommitted.go b/markets/storageadapter/ondealsectorcommitted.go
index 59e649147..bfa084638 100644
--- a/markets/storageadapter/ondealsectorcommitted.go
+++ b/markets/storageadapter/ondealsectorcommitted.go
@@ -3,6 +3,7 @@ package storageadapter
import (
"bytes"
"context"
+ "sync"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-fil-markets/storagemarket"
@@ -20,25 +21,145 @@ type sectorCommittedEventsAPI interface {
Called(check events.CheckFunc, msgHnd events.MsgHandler, rev events.RevertHandler, confidence int, timeout abi.ChainEpoch, mf events.MsgMatchFunc) error
}
-func OnDealSectorCommitted(ctx context.Context, api getCurrentDealInfoAPI, eventsApi sectorCommittedEventsAPI, provider address.Address, dealID abi.DealID, proposal market.DealProposal, publishCid *cid.Cid, cb storagemarket.DealSectorCommittedCallback) error {
- checkFunc := func(ts *types.TipSet) (done bool, more bool, err error) {
- newDealID, sd, err := GetCurrentDealInfo(ctx, ts, api, dealID, proposal, publishCid)
- if err != nil {
- // TODO: This may be fine for some errors
- return false, false, xerrors.Errorf("failed to look up deal on chain: %w", err)
- }
- dealID = newDealID
+func OnDealSectorPreCommitted(ctx context.Context, api getCurrentDealInfoAPI, eventsApi sectorCommittedEventsAPI, provider address.Address, dealID abi.DealID, proposal market.DealProposal, publishCid *cid.Cid, callback storagemarket.DealSectorPreCommittedCallback) error {
+ // Ensure callback is only called once
+ var once sync.Once
+ cb := func(sectorNumber abi.SectorNumber, isActive bool, err error) {
+ once.Do(func() {
+ callback(sectorNumber, isActive, err)
+ })
+ }
- if sd.State.SectorStartEpoch > 0 {
+ // First check if the deal is already active, and if so, bail out
+ checkFunc := func(ts *types.TipSet) (done bool, more bool, err error) {
+ isActive, err := checkIfDealAlreadyActive(ctx, api, ts, dealID, proposal, publishCid)
+ if err != nil {
+ // Note: the error returned from here will end up being returned
+ // from OnDealSectorPreCommitted so no need to call the callback
+ // with the error
+ return false, false, err
+ }
+
+ if isActive {
+ // Deal is already active, bail out
+ cb(0, true, nil)
+ return true, false, nil
+ }
+
+ // Not yet active, start matching against incoming messages
+ return false, true, nil
+ }
+
+ // Watch for a pre-commit message to the provider.
+ matchEvent := func(msg *types.Message) (bool, error) {
+ matched := msg.To == provider && msg.Method == miner.Methods.PreCommitSector
+ return matched, nil
+ }
+
+ // The deal must be accepted by the deal proposal start epoch, so timeout
+ // if the chain reaches that epoch
+ timeoutEpoch := proposal.StartEpoch + 1
+
+ // Check if the message params included the deal ID we're looking for.
+ called := func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH abi.ChainEpoch) (more bool, err error) {
+ defer func() {
+ if err != nil {
+ cb(0, false, xerrors.Errorf("handling applied event: %w", err))
+ }
+ }()
+
+ // If the deal hasn't been activated by the proposed start epoch, the
+ // deal will timeout (when msg == nil it means the timeout epoch was reached)
+ if msg == nil {
+ err = xerrors.Errorf("deal %d was not activated by proposed deal start epoch %d", dealID, proposal.StartEpoch)
+ return false, err
+ }
+
+ // Extract the message parameters
+ var params miner.SectorPreCommitInfo
+ if err := params.UnmarshalCBOR(bytes.NewReader(msg.Params)); err != nil {
+ return false, xerrors.Errorf("unmarshal pre commit: %w", err)
+ }
+
+ // When the deal is published, the deal ID may change, so get the
+ // current deal ID from the publish message CID
+ dealID, _, err = GetCurrentDealInfo(ctx, ts, api, dealID, proposal, publishCid)
+ if err != nil {
+ return false, err
+ }
+
+ // Check through the deal IDs associated with this message
+ for _, did := range params.DealIDs {
+ if did == dealID {
+ // Found the deal ID in this message. Callback with the sector ID.
+ cb(params.SectorNumber, false, nil)
+ return false, nil
+ }
+ }
+
+ // Didn't find the deal ID in this message, so keep looking
+ return true, nil
+ }
+
+ revert := func(ctx context.Context, ts *types.TipSet) error {
+ log.Warn("deal pre-commit reverted; TODO: actually handle this!")
+ // TODO: Just go back to DealSealing?
+ return nil
+ }
+
+ if err := eventsApi.Called(checkFunc, called, revert, int(build.MessageConfidence+1), timeoutEpoch, matchEvent); err != nil {
+ return xerrors.Errorf("failed to set up called handler: %w", err)
+ }
+
+ return nil
+}
+
+func OnDealSectorCommitted(ctx context.Context, api getCurrentDealInfoAPI, eventsApi sectorCommittedEventsAPI, provider address.Address, dealID abi.DealID, sectorNumber abi.SectorNumber, proposal market.DealProposal, publishCid *cid.Cid, callback storagemarket.DealSectorCommittedCallback) error {
+ // Ensure callback is only called once
+ var once sync.Once
+ cb := func(err error) {
+ once.Do(func() {
+ callback(err)
+ })
+ }
+
+ // First check if the deal is already active, and if so, bail out
+ checkFunc := func(ts *types.TipSet) (done bool, more bool, err error) {
+ isActive, err := checkIfDealAlreadyActive(ctx, api, ts, dealID, proposal, publishCid)
+ if err != nil {
+ // Note: the error returned from here will end up being returned
+ // from OnDealSectorCommitted so no need to call the callback
+ // with the error
+ return false, false, err
+ }
+
+ if isActive {
+ // Deal is already active, bail out
cb(nil)
return true, false, nil
}
+ // Not yet active, start matching against incoming messages
return false, true, nil
}
- var sectorNumber abi.SectorNumber
- var sectorFound bool
+ // Match a prove-commit sent to the provider with the given sector number
+ matchEvent := func(msg *types.Message) (matched bool, err error) {
+ if msg.To != provider || msg.Method != miner.Methods.ProveCommitSector {
+ return false, nil
+ }
+
+ var params miner.ProveCommitSectorParams
+ if err := params.UnmarshalCBOR(bytes.NewReader(msg.Params)); err != nil {
+ return false, xerrors.Errorf("failed to unmarshal prove commit sector params: %w", err)
+ }
+
+ return params.SectorNumber == sectorNumber, nil
+ }
+
+ // The deal must be accepted by the deal proposal start epoch, so timeout
+ // if the chain reaches that epoch
+ timeoutEpoch := proposal.StartEpoch + 1
called := func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH abi.ChainEpoch) (more bool, err error) {
defer func() {
@@ -46,49 +167,30 @@ func OnDealSectorCommitted(ctx context.Context, api getCurrentDealInfoAPI, event
cb(xerrors.Errorf("handling applied event: %w", err))
}
}()
- switch msg.Method {
- case miner.Methods.PreCommitSector:
- var params miner.SectorPreCommitInfo
- if err := params.UnmarshalCBOR(bytes.NewReader(msg.Params)); err != nil {
- return false, xerrors.Errorf("unmarshal pre commit: %w", err)
- }
- dealID, _, err = GetCurrentDealInfo(ctx, ts, api, dealID, proposal, publishCid)
- if err != nil {
- return false, err
- }
-
- for _, did := range params.DealIDs {
- if did == dealID {
- sectorNumber = params.SectorNumber
- sectorFound = true
- return true, nil
- }
- }
- return true, nil
- case miner.Methods.ProveCommitSector:
- if msg == nil {
- log.Error("timed out waiting for deal activation... what now?")
- return false, nil
- }
-
- _, sd, err := GetCurrentDealInfo(ctx, ts, api, dealID, proposal, publishCid)
- if err != nil {
- return false, xerrors.Errorf("failed to look up deal on chain: %w", err)
- }
-
- if sd.State.SectorStartEpoch < 1 {
- return false, xerrors.Errorf("deal wasn't active: deal=%d, parentState=%s, h=%d", dealID, ts.ParentState(), ts.Height())
- }
-
- log.Infof("Storage deal %d activated at epoch %d", dealID, sd.State.SectorStartEpoch)
-
- cb(nil)
-
- return false, nil
- default:
- return false, nil
+ // If the deal hasn't been activated by the proposed start epoch, the
+ // deal will timeout (when msg == nil it means the timeout epoch was reached)
+ if msg == nil {
+ err := xerrors.Errorf("deal %d was not activated by proposed deal start epoch %d", dealID, proposal.StartEpoch)
+ return false, err
}
+
+ // Get the deal info
+ _, sd, err := GetCurrentDealInfo(ctx, ts, api, dealID, proposal, publishCid)
+ if err != nil {
+ return false, xerrors.Errorf("failed to look up deal on chain: %w", err)
+ }
+
+ // Make sure the deal is active
+ if sd.State.SectorStartEpoch < 1 {
+ return false, xerrors.Errorf("deal wasn't active: deal=%d, parentState=%s, h=%d", dealID, ts.ParentState(), ts.Height())
+ }
+
+ log.Infof("Storage deal %d activated at epoch %d", dealID, sd.State.SectorStartEpoch)
+
+ cb(nil)
+
+ return false, nil
}
revert := func(ctx context.Context, ts *types.TipSet) error {
@@ -97,38 +199,29 @@ func OnDealSectorCommitted(ctx context.Context, api getCurrentDealInfoAPI, event
return nil
}
- matchEvent := func(msg *types.Message) (matched bool, err error) {
- if msg.To != provider {
- return false, nil
- }
-
- switch msg.Method {
- case miner.Methods.PreCommitSector:
- return !sectorFound, nil
- case miner.Methods.ProveCommitSector:
- if !sectorFound {
- return false, nil
- }
-
- var params miner.ProveCommitSectorParams
- if err := params.UnmarshalCBOR(bytes.NewReader(msg.Params)); err != nil {
- return false, xerrors.Errorf("failed to unmarshal prove commit sector params: %w", err)
- }
-
- if params.SectorNumber != sectorNumber {
- return false, nil
- }
-
- return true, nil
- default:
- return false, nil
- }
-
- }
-
- if err := eventsApi.Called(checkFunc, called, revert, int(build.MessageConfidence+1), events.NoTimeout, matchEvent); err != nil {
+ if err := eventsApi.Called(checkFunc, called, revert, int(build.MessageConfidence+1), timeoutEpoch, matchEvent); err != nil {
return xerrors.Errorf("failed to set up called handler: %w", err)
}
return nil
}
+
+func checkIfDealAlreadyActive(ctx context.Context, api getCurrentDealInfoAPI, ts *types.TipSet, dealID abi.DealID, proposal market.DealProposal, publishCid *cid.Cid) (bool, error) {
+ _, sd, err := GetCurrentDealInfo(ctx, ts, api, dealID, proposal, publishCid)
+ if err != nil {
+ // TODO: This may be fine for some errors
+ return false, xerrors.Errorf("failed to look up deal on chain: %w", err)
+ }
+
+ // Sector with deal is already active
+ if sd.State.SectorStartEpoch > 0 {
+ return true, nil
+ }
+
+ // Sector was slashed
+ if sd.State.SlashEpoch > 0 {
+ return false, xerrors.Errorf("deal %d was slashed at epoch %d", dealID, sd.State.SlashEpoch)
+ }
+
+ return false, nil
+}
diff --git a/markets/storageadapter/ondealsectorcommitted_test.go b/markets/storageadapter/ondealsectorcommitted_test.go
index 8bab7c3d4..30fbfea76 100644
--- a/markets/storageadapter/ondealsectorcommitted_test.go
+++ b/markets/storageadapter/ondealsectorcommitted_test.go
@@ -8,6 +8,8 @@ import (
"math/rand"
"testing"
+ "golang.org/x/xerrors"
+
blocks "github.com/ipfs/go-block-format"
"github.com/filecoin-project/go-address"
@@ -24,7 +26,7 @@ import (
"github.com/stretchr/testify/require"
)
-func TestOnDealSectorCommitted(t *testing.T) {
+func TestOnDealSectorPreCommitted(t *testing.T) {
provider := address.TestAddress
ctx := context.Background()
publishCid := generateCids(1)[0]
@@ -53,15 +55,19 @@ func TestOnDealSectorCommitted(t *testing.T) {
LastUpdatedEpoch: 2,
},
}
- testCases := map[string]struct {
- searchMessageLookup *api.MsgLookup
- searchMessageErr error
- checkTsDeals map[abi.DealID]*api.MarketDeal
- matchStates []matchState
- expectedCBCallCount uint64
- expectedCBError error
- expectedError error
- }{
+ type testCase struct {
+ searchMessageLookup *api.MsgLookup
+ searchMessageErr error
+ checkTsDeals map[abi.DealID]*api.MarketDeal
+ matchStates []matchState
+ dealStartEpochTimeout bool
+ expectedCBCallCount uint64
+ expectedCBSectorNumber abi.SectorNumber
+ expectedCBIsActive bool
+ expectedCBError error
+ expectedError error
+ }
+ testCases := map[string]testCase{
"normal sequence": {
checkTsDeals: map[abi.DealID]*api.MarketDeal{
startDealID: unfinishedDeal,
@@ -77,16 +83,10 @@ func TestOnDealSectorCommitted(t *testing.T) {
startDealID: unfinishedDeal,
},
},
- {
- msg: makeMessage(t, provider, miner.Methods.ProveCommitSector, &miner.ProveCommitSectorParams{
- SectorNumber: sectorNumber,
- }),
- deals: map[abi.DealID]*api.MarketDeal{
- startDealID: successDeal,
- },
- },
},
- expectedCBCallCount: 1,
+ expectedCBCallCount: 1,
+ expectedCBIsActive: false,
+ expectedCBSectorNumber: sectorNumber,
},
"deal id changes in called": {
searchMessageLookup: &api.MsgLookup{
@@ -109,96 +109,10 @@ func TestOnDealSectorCommitted(t *testing.T) {
newDealID: unfinishedDeal,
},
},
- {
- msg: makeMessage(t, provider, miner.Methods.ProveCommitSector, &miner.ProveCommitSectorParams{
- SectorNumber: sectorNumber,
- }),
- deals: map[abi.DealID]*api.MarketDeal{
- newDealID: successDeal,
- },
- },
},
- expectedCBCallCount: 1,
- },
- "deal id changes in precommit": {
- searchMessageLookup: &api.MsgLookup{
- Receipt: types.MessageReceipt{
- ExitCode: exitcode.Ok,
- Return: newValueReturn,
- },
- },
- checkTsDeals: map[abi.DealID]*api.MarketDeal{
- startDealID: unfinishedDeal,
- },
- matchStates: []matchState{
- {
- msg: makeMessage(t, provider, miner.Methods.PreCommitSector, &miner.SectorPreCommitInfo{
- SectorNumber: sectorNumber,
- SealedCID: sealedCid,
- DealIDs: []abi.DealID{newDealID},
- }),
- deals: map[abi.DealID]*api.MarketDeal{
- newDealID: unfinishedDeal,
- },
- },
- {
- msg: makeMessage(t, provider, miner.Methods.ProveCommitSector, &miner.ProveCommitSectorParams{
- SectorNumber: sectorNumber,
- }),
- deals: map[abi.DealID]*api.MarketDeal{
- newDealID: successDeal,
- },
- },
- },
- expectedCBCallCount: 1,
- },
- "deal id changes in prove-commit": {
- searchMessageLookup: &api.MsgLookup{
- Receipt: types.MessageReceipt{
- ExitCode: exitcode.Ok,
- Return: newValueReturn,
- },
- },
- checkTsDeals: map[abi.DealID]*api.MarketDeal{
- startDealID: unfinishedDeal,
- },
- matchStates: []matchState{
- {
- msg: makeMessage(t, provider, miner.Methods.PreCommitSector, &miner.SectorPreCommitInfo{
- SectorNumber: sectorNumber,
- SealedCID: sealedCid,
- DealIDs: []abi.DealID{startDealID},
- }),
- deals: map[abi.DealID]*api.MarketDeal{
- startDealID: unfinishedDeal,
- },
- },
- {
- msg: makeMessage(t, provider, miner.Methods.ProveCommitSector, &miner.ProveCommitSectorParams{
- SectorNumber: sectorNumber,
- }),
- deals: map[abi.DealID]*api.MarketDeal{
- newDealID: successDeal,
- },
- },
- },
- expectedCBCallCount: 1,
- },
- "prove commit but no sector recorded": {
- checkTsDeals: map[abi.DealID]*api.MarketDeal{
- startDealID: unfinishedDeal,
- },
- matchStates: []matchState{
- {
- msg: makeMessage(t, provider, miner.Methods.ProveCommitSector, &miner.ProveCommitSectorParams{
- SectorNumber: sectorNumber,
- }),
- deals: map[abi.DealID]*api.MarketDeal{
- startDealID: successDeal,
- },
- },
- },
- expectedCBCallCount: 0,
+ expectedCBCallCount: 1,
+ expectedCBIsActive: false,
+ expectedCBSectorNumber: sectorNumber,
},
"error on deal in check": {
checkTsDeals: map[abi.DealID]*api.MarketDeal{},
@@ -211,6 +125,7 @@ func TestOnDealSectorCommitted(t *testing.T) {
startDealID: successDeal,
},
expectedCBCallCount: 1,
+ expectedCBIsActive: true,
},
"error on deal in pre-commit": {
searchMessageErr: errors.New("something went wrong"),
@@ -231,16 +146,16 @@ func TestOnDealSectorCommitted(t *testing.T) {
expectedCBError: errors.New("handling applied event: something went wrong"),
expectedError: errors.New("failed to set up called handler: something went wrong"),
},
+ "proposed deal epoch timeout": {
+ checkTsDeals: map[abi.DealID]*api.MarketDeal{
+ startDealID: unfinishedDeal,
+ },
+ dealStartEpochTimeout: true,
+ expectedCBCallCount: 1,
+ expectedCBError: xerrors.Errorf("handling applied event: deal %d was not activated by proposed deal start epoch 0", startDealID),
+ },
}
- runTestCase := func(testCase string, data struct {
- searchMessageLookup *api.MsgLookup
- searchMessageErr error
- checkTsDeals map[abi.DealID]*api.MarketDeal
- matchStates []matchState
- expectedCBCallCount uint64
- expectedCBError error
- expectedError error
- }) {
+ runTestCase := func(testCase string, data testCase) {
t.Run(testCase, func(t *testing.T) {
// ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
// defer cancel()
@@ -269,9 +184,192 @@ func TestOnDealSectorCommitted(t *testing.T) {
}
}
eventsAPI := &fakeEvents{
- Ctx: ctx,
- CheckTs: checkTs,
- MatchMessages: matchMessages,
+ Ctx: ctx,
+ CheckTs: checkTs,
+ MatchMessages: matchMessages,
+ DealStartEpochTimeout: data.dealStartEpochTimeout,
+ }
+ cbCallCount := uint64(0)
+ var cbSectorNumber abi.SectorNumber
+ var cbIsActive bool
+ var cbError error
+ cb := func(secNum abi.SectorNumber, isActive bool, err error) {
+ cbCallCount++
+ cbSectorNumber = secNum
+ cbIsActive = isActive
+ cbError = err
+ }
+ err = OnDealSectorPreCommitted(ctx, api, eventsAPI, provider, startDealID, proposal, &publishCid, cb)
+ if data.expectedError == nil {
+ require.NoError(t, err)
+ } else {
+ require.EqualError(t, err, data.expectedError.Error())
+ }
+ require.Equal(t, data.expectedCBSectorNumber, cbSectorNumber)
+ require.Equal(t, data.expectedCBIsActive, cbIsActive)
+ require.Equal(t, data.expectedCBCallCount, cbCallCount)
+ if data.expectedCBError == nil {
+ require.NoError(t, cbError)
+ } else {
+ require.EqualError(t, cbError, data.expectedCBError.Error())
+ }
+ })
+ }
+ for testCase, data := range testCases {
+ runTestCase(testCase, data)
+ }
+}
+
+func TestOnDealSectorCommitted(t *testing.T) {
+ provider := address.TestAddress
+ ctx := context.Background()
+ publishCid := generateCids(1)[0]
+ pieceCid := generateCids(1)[0]
+ startDealID := abi.DealID(rand.Uint64())
+ newDealID := abi.DealID(rand.Uint64())
+ newValueReturn := makePublishDealsReturnBytes(t, []abi.DealID{newDealID})
+ sectorNumber := abi.SectorNumber(rand.Uint64())
+ proposal := market.DealProposal{
+ PieceCID: pieceCid,
+ PieceSize: abi.PaddedPieceSize(rand.Uint64()),
+ Label: "success",
+ }
+ unfinishedDeal := &api.MarketDeal{
+ Proposal: proposal,
+ State: market.DealState{
+ SectorStartEpoch: -1,
+ LastUpdatedEpoch: 2,
+ },
+ }
+ successDeal := &api.MarketDeal{
+ Proposal: proposal,
+ State: market.DealState{
+ SectorStartEpoch: 1,
+ LastUpdatedEpoch: 2,
+ },
+ }
+ type testCase struct {
+ searchMessageLookup *api.MsgLookup
+ searchMessageErr error
+ checkTsDeals map[abi.DealID]*api.MarketDeal
+ matchStates []matchState
+ dealStartEpochTimeout bool
+ expectedCBCallCount uint64
+ expectedCBError error
+ expectedError error
+ }
+ testCases := map[string]testCase{
+ "normal sequence": {
+ checkTsDeals: map[abi.DealID]*api.MarketDeal{
+ startDealID: unfinishedDeal,
+ },
+ matchStates: []matchState{
+ {
+ msg: makeMessage(t, provider, miner.Methods.ProveCommitSector, &miner.ProveCommitSectorParams{
+ SectorNumber: sectorNumber,
+ }),
+ deals: map[abi.DealID]*api.MarketDeal{
+ startDealID: successDeal,
+ },
+ },
+ },
+ expectedCBCallCount: 1,
+ },
+ "deal id changes in called": {
+ searchMessageLookup: &api.MsgLookup{
+ Receipt: types.MessageReceipt{
+ ExitCode: exitcode.Ok,
+ Return: newValueReturn,
+ },
+ },
+ checkTsDeals: map[abi.DealID]*api.MarketDeal{
+ newDealID: unfinishedDeal,
+ },
+ matchStates: []matchState{
+ {
+ msg: makeMessage(t, provider, miner.Methods.ProveCommitSector, &miner.ProveCommitSectorParams{
+ SectorNumber: sectorNumber,
+ }),
+ deals: map[abi.DealID]*api.MarketDeal{
+ newDealID: successDeal,
+ },
+ },
+ },
+ expectedCBCallCount: 1,
+ },
+ "error on deal in check": {
+ checkTsDeals: map[abi.DealID]*api.MarketDeal{},
+ searchMessageErr: errors.New("something went wrong"),
+ expectedCBCallCount: 0,
+ expectedError: errors.New("failed to set up called handler: failed to look up deal on chain: something went wrong"),
+ },
+ "sector start epoch > 0 in check": {
+ checkTsDeals: map[abi.DealID]*api.MarketDeal{
+ startDealID: successDeal,
+ },
+ expectedCBCallCount: 1,
+ },
+ "error on deal in called": {
+ searchMessageErr: errors.New("something went wrong"),
+ checkTsDeals: map[abi.DealID]*api.MarketDeal{
+ startDealID: unfinishedDeal,
+ },
+ matchStates: []matchState{
+ {
+ msg: makeMessage(t, provider, miner.Methods.ProveCommitSector, &miner.ProveCommitSectorParams{
+ SectorNumber: sectorNumber,
+ }),
+ deals: map[abi.DealID]*api.MarketDeal{
+ newDealID: successDeal,
+ },
+ },
+ },
+ expectedCBCallCount: 1,
+ expectedCBError: errors.New("handling applied event: failed to look up deal on chain: something went wrong"),
+ expectedError: errors.New("failed to set up called handler: failed to look up deal on chain: something went wrong"),
+ },
+ "proposed deal epoch timeout": {
+ checkTsDeals: map[abi.DealID]*api.MarketDeal{
+ startDealID: unfinishedDeal,
+ },
+ dealStartEpochTimeout: true,
+ expectedCBCallCount: 1,
+ expectedCBError: xerrors.Errorf("handling applied event: deal %d was not activated by proposed deal start epoch 0", startDealID),
+ },
+ }
+ runTestCase := func(testCase string, data testCase) {
+ t.Run(testCase, func(t *testing.T) {
+ // ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
+ // defer cancel()
+ api := &mockGetCurrentDealInfoAPI{
+ SearchMessageLookup: data.searchMessageLookup,
+ SearchMessageErr: data.searchMessageErr,
+ MarketDeals: make(map[marketDealKey]*api.MarketDeal),
+ }
+ checkTs, err := test.MockTipset(provider, rand.Uint64())
+ require.NoError(t, err)
+ for dealID, deal := range data.checkTsDeals {
+ api.MarketDeals[marketDealKey{dealID, checkTs.Key()}] = deal
+ }
+ matchMessages := make([]matchMessage, len(data.matchStates))
+ for i, ms := range data.matchStates {
+ matchTs, err := test.MockTipset(provider, rand.Uint64())
+ require.NoError(t, err)
+ for dealID, deal := range ms.deals {
+ api.MarketDeals[marketDealKey{dealID, matchTs.Key()}] = deal
+ }
+ matchMessages[i] = matchMessage{
+ curH: 5,
+ msg: ms.msg,
+ msgReceipt: nil,
+ ts: matchTs,
+ }
+ }
+ eventsAPI := &fakeEvents{
+ Ctx: ctx,
+ CheckTs: checkTs,
+ MatchMessages: matchMessages,
+ DealStartEpochTimeout: data.dealStartEpochTimeout,
}
cbCallCount := uint64(0)
var cbError error
@@ -279,7 +377,7 @@ func TestOnDealSectorCommitted(t *testing.T) {
cbCallCount++
cbError = err
}
- err = OnDealSectorCommitted(ctx, api, eventsAPI, provider, startDealID, proposal, &publishCid, cb)
+ err = OnDealSectorCommitted(ctx, api, eventsAPI, provider, startDealID, sectorNumber, proposal, &publishCid, cb)
if data.expectedError == nil {
require.NoError(t, err)
} else {
@@ -311,12 +409,18 @@ type matchMessage struct {
doesRevert bool
}
type fakeEvents struct {
- Ctx context.Context
- CheckTs *types.TipSet
- MatchMessages []matchMessage
+ Ctx context.Context
+ CheckTs *types.TipSet
+ MatchMessages []matchMessage
+ DealStartEpochTimeout bool
}
func (fe *fakeEvents) Called(check events.CheckFunc, msgHnd events.MsgHandler, rev events.RevertHandler, confidence int, timeout abi.ChainEpoch, mf events.MsgMatchFunc) error {
+ if fe.DealStartEpochTimeout {
+ msgHnd(nil, nil, nil, 100) // nolint:errcheck
+ return nil
+ }
+
_, more, err := check(fe.CheckTs)
if err != nil {
return err
diff --git a/markets/storageadapter/provider.go b/markets/storageadapter/provider.go
index cfff6cfe1..79fd718f8 100644
--- a/markets/storageadapter/provider.go
+++ b/markets/storageadapter/provider.go
@@ -270,8 +270,12 @@ func (n *ProviderNodeAdapter) DealProviderCollateralBounds(ctx context.Context,
return bounds.Min, bounds.Max, nil
}
-func (n *ProviderNodeAdapter) OnDealSectorCommitted(ctx context.Context, provider address.Address, dealID abi.DealID, proposal market2.DealProposal, publishCid *cid.Cid, cb storagemarket.DealSectorCommittedCallback) error {
- return OnDealSectorCommitted(ctx, n, n.ev, provider, dealID, market.DealProposal(proposal), publishCid, cb)
+func (n *ProviderNodeAdapter) OnDealSectorPreCommitted(ctx context.Context, provider address.Address, dealID abi.DealID, proposal market2.DealProposal, publishCid *cid.Cid, cb storagemarket.DealSectorPreCommittedCallback) error {
+ return OnDealSectorPreCommitted(ctx, n, n.ev, provider, dealID, market.DealProposal(proposal), publishCid, cb)
+}
+
+func (n *ProviderNodeAdapter) OnDealSectorCommitted(ctx context.Context, provider address.Address, dealID abi.DealID, sectorNumber abi.SectorNumber, proposal market2.DealProposal, publishCid *cid.Cid, cb storagemarket.DealSectorCommittedCallback) error {
+ return OnDealSectorCommitted(ctx, n, n.ev, provider, dealID, sectorNumber, market.DealProposal(proposal), publishCid, cb)
}
func (n *ProviderNodeAdapter) GetChainHead(ctx context.Context) (shared.TipSetToken, abi.ChainEpoch, error) {
diff --git a/miner/miner.go b/miner/miner.go
index cabf26ecd..d24850d9f 100644
--- a/miner/miner.go
+++ b/miner/miner.go
@@ -423,6 +423,8 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg,
return nil, xerrors.Errorf("failed to compute winning post proof: %w", err)
}
+ tProof := build.Clock.Now()
+
// get pending messages early,
msgs, err := m.api.MpoolSelect(context.TODO(), base.TipSet.Key(), ticket.Quality())
if err != nil {
@@ -451,7 +453,8 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg,
"tPowercheck ", tPowercheck.Sub(tDrand),
"tTicket ", tTicket.Sub(tPowercheck),
"tSeed ", tSeed.Sub(tTicket),
- "tPending ", tPending.Sub(tSeed),
+ "tProof ", tProof.Sub(tSeed),
+ "tPending ", tPending.Sub(tProof),
"tCreateBlock ", tCreateBlock.Sub(tPending))
}
diff --git a/node/builder.go b/node/builder.go
index 8c49e44ad..04400e1e1 100644
--- a/node/builder.go
+++ b/node/builder.go
@@ -26,6 +26,7 @@ import (
"github.com/libp2p/go-libp2p-peerstore/pstoremem"
pubsub "github.com/libp2p/go-libp2p-pubsub"
record "github.com/libp2p/go-libp2p-record"
+ "github.com/libp2p/go-libp2p/p2p/net/conngater"
"github.com/multiformats/go-multiaddr"
"go.uber.org/fx"
"golang.org/x/xerrors"
@@ -98,6 +99,7 @@ var (
ConnectionManagerKey = special{9} // Libp2p option
AutoNATSvcKey = special{10} // Libp2p option
BandwidthReporterKey = special{11} // Libp2p option
+ ConnGaterKey = special{12} // libp2p option
)
type invoke int
@@ -220,6 +222,9 @@ func libp2p() Option {
Override(PstoreAddSelfKeysKey, lp2p.PstoreAddSelfKeys),
Override(StartListeningKey, lp2p.StartListening(config.DefaultFullNode().Libp2p.ListenAddresses)),
+
+ Override(new(*conngater.BasicConnectionGater), lp2p.ConnGater),
+ Override(ConnGaterKey, lp2p.ConnGaterOption),
)
}
@@ -285,7 +290,7 @@ func Online() Option {
Override(new(exchange.Server), exchange.NewServer),
Override(new(*peermgr.PeerMgr), peermgr.NewPeerMgr),
- Override(new(dtypes.Graphsync), modules.Graphsync),
+ Override(new(dtypes.Graphsync), modules.Graphsync(config.DefaultFullNode().Client.SimultaneousTransfers)),
Override(new(*dtypes.MpoolLocker), new(dtypes.MpoolLocker)),
Override(new(*discoveryimpl.Local), modules.NewLocalDiscovery),
Override(new(discovery.PeerResolver), modules.RetrievalResolver),
@@ -460,12 +465,15 @@ func ConfigFullNode(c interface{}) Option {
ipfsMaddr := cfg.Client.IpfsMAddr
return Options(
ConfigCommon(&cfg.Common),
+
If(cfg.Client.UseIpfs,
Override(new(dtypes.ClientBlockstore), modules.IpfsClientBlockstore(ipfsMaddr, cfg.Client.IpfsOnlineMode)),
If(cfg.Client.IpfsUseForRetrieval,
Override(new(dtypes.ClientRetrievalStoreManager), modules.ClientBlockstoreRetrievalStoreManager),
),
),
+ Override(new(dtypes.Graphsync), modules.Graphsync(cfg.Client.SimultaneousTransfers)),
+
If(cfg.Metrics.HeadNotifs,
Override(HeadMetricsKey, metrics.SendHeadNotifs(cfg.Metrics.Nickname)),
),
diff --git a/node/config/def.go b/node/config/def.go
index 3e109e84a..7e45b88a3 100644
--- a/node/config/def.go
+++ b/node/config/def.go
@@ -105,10 +105,11 @@ type Metrics struct {
}
type Client struct {
- UseIpfs bool
- IpfsOnlineMode bool
- IpfsMAddr string
- IpfsUseForRetrieval bool
+ UseIpfs bool
+ IpfsOnlineMode bool
+ IpfsMAddr string
+ IpfsUseForRetrieval bool
+ SimultaneousTransfers uint64
}
type Wallet struct {
@@ -149,6 +150,7 @@ func defCommon() Common {
}
var DefaultDefaultMaxFee = types.MustParseFIL("0.007")
+var DefaultSimultaneousTransfers = uint64(20)
// DefaultFullNode returns the default config
func DefaultFullNode() *FullNode {
@@ -157,6 +159,9 @@ func DefaultFullNode() *FullNode {
Fees: FeeConfig{
DefaultMaxFee: DefaultDefaultMaxFee,
},
+ Client: Client{
+ SimultaneousTransfers: DefaultSimultaneousTransfers,
+ },
}
}
diff --git a/node/impl/client/client.go b/node/impl/client/client.go
index ef8e9e022..7ecdbc869 100644
--- a/node/impl/client/client.go
+++ b/node/impl/client/client.go
@@ -33,9 +33,10 @@ import (
"go.uber.org/fx"
"github.com/filecoin-project/go-address"
+ "github.com/filecoin-project/go-commp-utils/ffiwrapper"
+ "github.com/filecoin-project/go-commp-utils/writer"
datatransfer "github.com/filecoin-project/go-data-transfer"
"github.com/filecoin-project/go-fil-markets/discovery"
- "github.com/filecoin-project/go-fil-markets/pieceio"
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
rm "github.com/filecoin-project/go-fil-markets/retrievalmarket"
"github.com/filecoin-project/go-fil-markets/shared"
@@ -49,7 +50,6 @@ import (
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
- "github.com/filecoin-project/lotus/lib/commp"
"github.com/filecoin-project/lotus/markets/utils"
"github.com/filecoin-project/lotus/node/impl/full"
"github.com/filecoin-project/lotus/node/impl/paych"
@@ -642,7 +642,7 @@ func (a *API) ClientQueryAsk(ctx context.Context, p peer.ID, miner address.Addre
func (a *API) ClientCalcCommP(ctx context.Context, inpath string) (*api.CommPRet, error) {
// Hard-code the sector type to 32GiBV1_1, because:
- // - pieceio.GeneratePieceCommitment requires a RegisteredSealProof
+ // - ffiwrapper.GeneratePieceCIDFromFile requires a RegisteredSealProof
// - commP itself is sector-size independent, with rather low probability of that changing
// ( note how the final rust call is identical for every RegSP type )
// https://github.com/filecoin-project/rust-filecoin-proofs-api/blob/v5.0.0/src/seal.rs#L1040-L1050
@@ -662,7 +662,8 @@ func (a *API) ClientCalcCommP(ctx context.Context, inpath string) (*api.CommPRet
return nil, err
}
- commP, pieceSize, err := pieceio.GeneratePieceCommitment(arbitraryProofType, rdr, uint64(stat.Size()))
+ pieceReader, pieceSize := padreader.New(rdr, uint64(stat.Size()))
+ commP, err := ffiwrapper.GeneratePieceCIDFromFile(arbitraryProofType, pieceReader, pieceSize)
if err != nil {
return nil, xerrors.Errorf("computing commP failed: %w", err)
@@ -702,8 +703,8 @@ func (a *API) ClientDealSize(ctx context.Context, root cid.Cid) (api.DataSize, e
func (a *API) ClientDealPieceCID(ctx context.Context, root cid.Cid) (api.DataCIDSize, error) {
dag := merkledag.NewDAGService(blockservice.New(a.CombinedBstore, offline.Exchange(a.CombinedBstore)))
- w := &commp.Writer{}
- bw := bufio.NewWriterSize(w, int(commp.CommPBuf))
+ w := &writer.Writer{}
+ bw := bufio.NewWriterSize(w, int(writer.CommPBuf))
err := car.WriteCar(ctx, dag, []cid.Cid{root}, w)
if err != nil {
@@ -714,7 +715,8 @@ func (a *API) ClientDealPieceCID(ctx context.Context, root cid.Cid) (api.DataCID
return api.DataCIDSize{}, err
}
- return w.Sum()
+ dataCIDSize, err := w.Sum()
+ return api.DataCIDSize(dataCIDSize), err
}
func (a *API) ClientGenCar(ctx context.Context, ref api.FileRef, outputPath string) error {
diff --git a/node/impl/common/common.go b/node/impl/common/common.go
index 79478e489..94bcd5ace 100644
--- a/node/impl/common/common.go
+++ b/node/impl/common/common.go
@@ -15,6 +15,7 @@ import (
protocol "github.com/libp2p/go-libp2p-core/protocol"
swarm "github.com/libp2p/go-libp2p-swarm"
basichost "github.com/libp2p/go-libp2p/p2p/host/basic"
+ "github.com/libp2p/go-libp2p/p2p/net/conngater"
ma "github.com/multiformats/go-multiaddr"
"go.uber.org/fx"
"golang.org/x/xerrors"
@@ -36,6 +37,7 @@ type CommonAPI struct {
RawHost lp2p.RawHost
Host host.Host
Router lp2p.BaseIpfsRouting
+ ConnGater *conngater.BasicConnectionGater
Reporter metrics.Reporter
Sk *dtypes.ScoreKeeper
ShutdownChan dtypes.ShutdownChan
diff --git a/node/impl/common/conngater.go b/node/impl/common/conngater.go
new file mode 100644
index 000000000..ab387631c
--- /dev/null
+++ b/node/impl/common/conngater.go
@@ -0,0 +1,136 @@
+package common
+
+import (
+ "context"
+ "net"
+
+ "golang.org/x/xerrors"
+
+ logging "github.com/ipfs/go-log/v2"
+ manet "github.com/multiformats/go-multiaddr/net"
+
+ "github.com/filecoin-project/lotus/api"
+)
+
+var cLog = logging.Logger("conngater")
+
+func (a *CommonAPI) NetBlockAdd(ctx context.Context, acl api.NetBlockList) error {
+ for _, p := range acl.Peers {
+ err := a.ConnGater.BlockPeer(p)
+ if err != nil {
+ return xerrors.Errorf("error blocking peer %s: %w", p, err)
+ }
+
+ for _, c := range a.Host.Network().ConnsToPeer(p) {
+ err = c.Close()
+ if err != nil {
+ // just log this, don't fail
+ cLog.Warnf("error closing connection to %s: %s", p, err)
+ }
+ }
+ }
+
+ for _, addr := range acl.IPAddrs {
+ ip := net.ParseIP(addr)
+ if ip == nil {
+ return xerrors.Errorf("error parsing IP address %s", addr)
+ }
+
+ err := a.ConnGater.BlockAddr(ip)
+ if err != nil {
+ return xerrors.Errorf("error blocking IP address %s: %w", addr, err)
+ }
+
+ for _, c := range a.Host.Network().Conns() {
+ remote := c.RemoteMultiaddr()
+ remoteIP, err := manet.ToIP(remote)
+ if err != nil {
+ continue
+ }
+
+ if ip.Equal(remoteIP) {
+ err = c.Close()
+ if err != nil {
+ // just log this, don't fail
+ cLog.Warnf("error closing connection to %s: %s", remoteIP, err)
+ }
+ }
+ }
+ }
+
+ for _, subnet := range acl.IPSubnets {
+ _, cidr, err := net.ParseCIDR(subnet)
+ if err != nil {
+ return xerrors.Errorf("error parsing subnet %s: %w", subnet, err)
+ }
+
+ err = a.ConnGater.BlockSubnet(cidr)
+ if err != nil {
+ return xerrors.Errorf("error blocking subunet %s: %w", subnet, err)
+ }
+
+ for _, c := range a.Host.Network().Conns() {
+ remote := c.RemoteMultiaddr()
+ remoteIP, err := manet.ToIP(remote)
+ if err != nil {
+ continue
+ }
+
+ if cidr.Contains(remoteIP) {
+ err = c.Close()
+ if err != nil {
+ // just log this, don't fail
+ cLog.Warnf("error closing connection to %s: %s", remoteIP, err)
+ }
+ }
+ }
+ }
+
+ return nil
+}
+
+func (a *CommonAPI) NetBlockRemove(ctx context.Context, acl api.NetBlockList) error {
+ for _, p := range acl.Peers {
+ err := a.ConnGater.UnblockPeer(p)
+ if err != nil {
+ return xerrors.Errorf("error unblocking peer %s: %w", p, err)
+ }
+ }
+
+ for _, addr := range acl.IPAddrs {
+ ip := net.ParseIP(addr)
+ if ip == nil {
+ return xerrors.Errorf("error parsing IP address %s", addr)
+ }
+
+ err := a.ConnGater.UnblockAddr(ip)
+ if err != nil {
+ return xerrors.Errorf("error unblocking IP address %s: %w", addr, err)
+ }
+ }
+
+ for _, subnet := range acl.IPSubnets {
+ _, cidr, err := net.ParseCIDR(subnet)
+ if err != nil {
+ return xerrors.Errorf("error parsing subnet %s: %w", subnet, err)
+ }
+
+ err = a.ConnGater.UnblockSubnet(cidr)
+ if err != nil {
+ return xerrors.Errorf("error unblocking subunet %s: %w", subnet, err)
+ }
+ }
+
+ return nil
+}
+
+func (a *CommonAPI) NetBlockList(ctx context.Context) (result api.NetBlockList, err error) {
+ result.Peers = a.ConnGater.ListBlockedPeers()
+ for _, ip := range a.ConnGater.ListBlockedAddrs() {
+ result.IPAddrs = append(result.IPAddrs, ip.String())
+ }
+ for _, subnet := range a.ConnGater.ListBlockedSubnets() {
+ result.IPSubnets = append(result.IPSubnets, subnet.String())
+ }
+ return
+}
diff --git a/node/impl/full/state.go b/node/impl/full/state.go
index 5e01eaa5b..957cf0b5b 100644
--- a/node/impl/full/state.go
+++ b/node/impl/full/state.go
@@ -54,6 +54,7 @@ type StateModuleAPI interface {
StateMinerProvingDeadline(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*dline.Info, error)
StateMinerPower(context.Context, address.Address, types.TipSetKey) (*api.MinerPower, error)
StateNetworkVersion(ctx context.Context, key types.TipSetKey) (network.Version, error)
+ StateSectorGetInfo(ctx context.Context, maddr address.Address, n abi.SectorNumber, tsk types.TipSetKey) (*miner.SectorOnChainInfo, error)
StateVerifiedClientStatus(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*abi.StoragePower, error)
StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error)
}
@@ -792,17 +793,19 @@ func (a *StateAPI) StateSectorPreCommitInfo(ctx context.Context, maddr address.A
pci, err := stmgr.PreCommitInfo(ctx, a.StateManager, maddr, n, ts)
if err != nil {
return miner.SectorPreCommitOnChainInfo{}, err
+ } else if pci == nil {
+ return miner.SectorPreCommitOnChainInfo{}, xerrors.Errorf("precommit info is not exists")
}
return *pci, err
}
-func (a *StateAPI) StateSectorGetInfo(ctx context.Context, maddr address.Address, n abi.SectorNumber, tsk types.TipSetKey) (*miner.SectorOnChainInfo, error) {
- ts, err := a.Chain.GetTipSetFromKey(tsk)
+func (m *StateModule) StateSectorGetInfo(ctx context.Context, maddr address.Address, n abi.SectorNumber, tsk types.TipSetKey) (*miner.SectorOnChainInfo, error) {
+ ts, err := m.Chain.GetTipSetFromKey(tsk)
if err != nil {
return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err)
}
- return stmgr.MinerSectorInfo(ctx, a.StateManager, maddr, n, ts)
+ return stmgr.MinerSectorInfo(ctx, m.StateManager, maddr, n, ts)
}
func (a *StateAPI) StateSectorExpiration(ctx context.Context, maddr address.Address, sectorNumber abi.SectorNumber, tsk types.TipSetKey) (*miner.SectorExpiration, error) {
diff --git a/node/impl/full/wallet.go b/node/impl/full/wallet.go
index 05a7a5768..80ae65576 100644
--- a/node/impl/full/wallet.go
+++ b/node/impl/full/wallet.go
@@ -56,7 +56,7 @@ func (a *WalletAPI) WalletSignMessage(ctx context.Context, k address.Address, ms
return nil, xerrors.Errorf("serializing message: %w", err)
}
- sig, err := a.WalletAPI.WalletSign(ctx, k, mb.Cid().Bytes(), api.MsgMeta{
+ sig, err := a.WalletAPI.WalletSign(ctx, keyAddr, mb.Cid().Bytes(), api.MsgMeta{
Type: api.MTChainMsg,
Extra: mb.RawData(),
})
diff --git a/node/impl/storminer.go b/node/impl/storminer.go
index 89c4bbb8a..a1a6bec24 100644
--- a/node/impl/storminer.go
+++ b/node/impl/storminer.go
@@ -37,6 +37,7 @@ import (
"github.com/filecoin-project/lotus/node/modules/dtypes"
"github.com/filecoin-project/lotus/storage"
"github.com/filecoin-project/lotus/storage/sectorblocks"
+ sto "github.com/filecoin-project/specs-storage/storage"
)
type StorageMinerAPI struct {
@@ -543,4 +544,33 @@ func (sm *StorageMinerAPI) CreateBackup(ctx context.Context, fpath string) error
return backup(sm.DS, fpath)
}
+func (sm *StorageMinerAPI) CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof, sectors []sto.SectorRef, expensive bool) (map[abi.SectorNumber]string, error) {
+ var rg storiface.RGetter
+ if expensive {
+ rg = func(ctx context.Context, id abi.SectorID) (cid.Cid, error) {
+ si, err := sm.Miner.GetSectorInfo(id.Number)
+ if err != nil {
+ return cid.Undef, err
+ }
+ if si.CommR == nil {
+ return cid.Undef, xerrors.Errorf("commr is nil")
+ }
+
+ return *si.CommR, nil
+ }
+ }
+
+ bad, err := sm.StorageMgr.CheckProvable(ctx, pp, sectors, rg)
+ if err != nil {
+ return nil, err
+ }
+
+ var out = make(map[abi.SectorNumber]string)
+ for sid, err := range bad {
+ out[sid.Number] = err
+ }
+
+ return out, nil
+}
+
var _ api.StorageMiner = &StorageMinerAPI{}
diff --git a/node/modules/client.go b/node/modules/client.go
index 67eaffe88..18bba2417 100644
--- a/node/modules/client.go
+++ b/node/modules/client.go
@@ -137,6 +137,7 @@ func NewClientGraphsyncDataTransfer(lc fx.Lifecycle, h host.Host, gs dtypes.Grap
dt.OnReady(marketevents.ReadyLogger("client data transfer"))
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
+ dt.SubscribeToEvents(marketevents.DataTransferLogger)
return dt.Start(ctx)
},
OnStop: func(ctx context.Context) error {
diff --git a/node/modules/graphsync.go b/node/modules/graphsync.go
index 9bdc9bcca..bbb039957 100644
--- a/node/modules/graphsync.go
+++ b/node/modules/graphsync.go
@@ -3,6 +3,7 @@ package modules
import (
"github.com/filecoin-project/lotus/node/modules/dtypes"
"github.com/filecoin-project/lotus/node/modules/helpers"
+ "github.com/filecoin-project/lotus/node/repo"
"github.com/ipfs/go-graphsync"
graphsyncimpl "github.com/ipfs/go-graphsync/impl"
gsnet "github.com/ipfs/go-graphsync/network"
@@ -13,31 +14,34 @@ import (
)
// Graphsync creates a graphsync instance from the given loader and storer
-func Graphsync(mctx helpers.MetricsCtx, lc fx.Lifecycle, clientBs dtypes.ClientBlockstore, chainBs dtypes.ChainBlockstore, h host.Host) (dtypes.Graphsync, error) {
- graphsyncNetwork := gsnet.NewFromLibp2pHost(h)
- loader := storeutil.LoaderForBlockstore(clientBs)
- storer := storeutil.StorerForBlockstore(clientBs)
- gs := graphsyncimpl.New(helpers.LifecycleCtx(mctx, lc), graphsyncNetwork, loader, storer, graphsyncimpl.RejectAllRequestsByDefault())
- chainLoader := storeutil.LoaderForBlockstore(chainBs)
- chainStorer := storeutil.StorerForBlockstore(chainBs)
- err := gs.RegisterPersistenceOption("chainstore", chainLoader, chainStorer)
- if err != nil {
- return nil, err
+func Graphsync(parallelTransfers uint64) func(mctx helpers.MetricsCtx, lc fx.Lifecycle, r repo.LockedRepo, clientBs dtypes.ClientBlockstore, chainBs dtypes.ChainBlockstore, h host.Host) (dtypes.Graphsync, error) {
+ return func(mctx helpers.MetricsCtx, lc fx.Lifecycle, r repo.LockedRepo, clientBs dtypes.ClientBlockstore, chainBs dtypes.ChainBlockstore, h host.Host) (dtypes.Graphsync, error) {
+ graphsyncNetwork := gsnet.NewFromLibp2pHost(h)
+ loader := storeutil.LoaderForBlockstore(clientBs)
+ storer := storeutil.StorerForBlockstore(clientBs)
+
+ gs := graphsyncimpl.New(helpers.LifecycleCtx(mctx, lc), graphsyncNetwork, loader, storer, graphsyncimpl.RejectAllRequestsByDefault(), graphsyncimpl.MaxInProgressRequests(parallelTransfers))
+ chainLoader := storeutil.LoaderForBlockstore(chainBs)
+ chainStorer := storeutil.StorerForBlockstore(chainBs)
+ err := gs.RegisterPersistenceOption("chainstore", chainLoader, chainStorer)
+ if err != nil {
+ return nil, err
+ }
+ gs.RegisterIncomingRequestHook(func(p peer.ID, requestData graphsync.RequestData, hookActions graphsync.IncomingRequestHookActions) {
+ _, has := requestData.Extension("chainsync")
+ if has {
+ // TODO: we should confirm the selector is a reasonable one before we validate
+ // TODO: this code will get more complicated and should probably not live here eventually
+ hookActions.ValidateRequest()
+ hookActions.UsePersistenceOption("chainstore")
+ }
+ })
+ gs.RegisterOutgoingRequestHook(func(p peer.ID, requestData graphsync.RequestData, hookActions graphsync.OutgoingRequestHookActions) {
+ _, has := requestData.Extension("chainsync")
+ if has {
+ hookActions.UsePersistenceOption("chainstore")
+ }
+ })
+ return gs, nil
}
- gs.RegisterIncomingRequestHook(func(p peer.ID, requestData graphsync.RequestData, hookActions graphsync.IncomingRequestHookActions) {
- _, has := requestData.Extension("chainsync")
- if has {
- // TODO: we should confirm the selector is a reasonable one before we validate
- // TODO: this code will get more complicated and should probably not live here eventually
- hookActions.ValidateRequest()
- hookActions.UsePersistenceOption("chainstore")
- }
- })
- gs.RegisterOutgoingRequestHook(func(p peer.ID, requestData graphsync.RequestData, hookActions graphsync.OutgoingRequestHookActions) {
- _, has := requestData.Extension("chainsync")
- if has {
- hookActions.UsePersistenceOption("chainstore")
- }
- })
- return gs, nil
}
diff --git a/node/modules/lp2p/conngater.go b/node/modules/lp2p/conngater.go
new file mode 100644
index 000000000..29087a1bc
--- /dev/null
+++ b/node/modules/lp2p/conngater.go
@@ -0,0 +1,17 @@
+package lp2p
+
+import (
+ "github.com/libp2p/go-libp2p"
+ "github.com/libp2p/go-libp2p/p2p/net/conngater"
+
+ "github.com/filecoin-project/lotus/node/modules/dtypes"
+)
+
+func ConnGater(ds dtypes.MetadataDS) (*conngater.BasicConnectionGater, error) {
+ return conngater.NewBasicConnectionGater(ds)
+}
+
+func ConnGaterOption(cg *conngater.BasicConnectionGater) (opts Libp2pOpts, err error) {
+ opts.Opts = append(opts.Opts, libp2p.ConnectionGater(cg))
+ return
+}
diff --git a/node/modules/storageminer.go b/node/modules/storageminer.go
index 3281142b5..c5d8e7e54 100644
--- a/node/modules/storageminer.go
+++ b/node/modules/storageminer.go
@@ -300,6 +300,7 @@ func NewProviderDAGServiceDataTransfer(lc fx.Lifecycle, h host.Host, gs dtypes.S
dt.OnReady(marketevents.ReadyLogger("provider data transfer"))
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
+ dt.SubscribeToEvents(marketevents.DataTransferLogger)
return dt.Start(ctx)
},
OnStop: func(ctx context.Context) error {
@@ -423,15 +424,8 @@ func NewStorageAsk(ctx helpers.MetricsCtx, fapi lapi.FullNode, ds dtypes.Metadat
if err != nil {
return nil, err
}
- storedAsk, err := storedask.NewStoredAsk(namespace.Wrap(providerDs, datastore.NewKey("/storage-ask")), datastore.NewKey("latest"), spn, address.Address(minerAddress),
+ return storedask.NewStoredAsk(namespace.Wrap(providerDs, datastore.NewKey("/storage-ask")), datastore.NewKey("latest"), spn, address.Address(minerAddress),
storagemarket.MaxPieceSize(abi.PaddedPieceSize(mi.SectorSize)))
- if err != nil {
- return nil, err
- }
- if err != nil {
- return storedAsk, err
- }
- return storedAsk, nil
}
func BasicDealFilter(user dtypes.StorageDealFilter) func(onlineOk dtypes.ConsiderOnlineStorageDealsConfigFunc,
diff --git a/node/node_test.go b/node/node_test.go
index b8009aa78..0baa047da 100644
--- a/node/node_test.go
+++ b/node/node_test.go
@@ -38,17 +38,24 @@ func TestAPIDealFlow(t *testing.T) {
logging.SetLogLevel("sub", "ERROR")
logging.SetLogLevel("storageminer", "ERROR")
+ blockTime := 10 * time.Millisecond
+
+ // For these tests where the block time is artificially short, just use
+ // a deal start epoch that is guaranteed to be far enough in the future
+ // so that the deal starts sealing in time
+ dealStartEpoch := abi.ChainEpoch(2 << 12)
+
t.Run("TestDealFlow", func(t *testing.T) {
- test.TestDealFlow(t, builder.MockSbBuilder, 10*time.Millisecond, false, false)
+ test.TestDealFlow(t, builder.MockSbBuilder, blockTime, false, false, dealStartEpoch)
})
t.Run("WithExportedCAR", func(t *testing.T) {
- test.TestDealFlow(t, builder.MockSbBuilder, 10*time.Millisecond, true, false)
+ test.TestDealFlow(t, builder.MockSbBuilder, blockTime, true, false, dealStartEpoch)
})
t.Run("TestDoubleDealFlow", func(t *testing.T) {
- test.TestDoubleDealFlow(t, builder.MockSbBuilder, 10*time.Millisecond)
+ test.TestDoubleDealFlow(t, builder.MockSbBuilder, blockTime, dealStartEpoch)
})
t.Run("TestFastRetrievalDealFlow", func(t *testing.T) {
- test.TestFastRetrievalDealFlow(t, builder.MockSbBuilder, 10*time.Millisecond)
+ test.TestFastRetrievalDealFlow(t, builder.MockSbBuilder, blockTime, dealStartEpoch)
})
}
@@ -71,15 +78,15 @@ func TestAPIDealFlowReal(t *testing.T) {
})
t.Run("basic", func(t *testing.T) {
- test.TestDealFlow(t, builder.Builder, time.Second, false, false)
+ test.TestDealFlow(t, builder.Builder, time.Second, false, false, 0)
})
t.Run("fast-retrieval", func(t *testing.T) {
- test.TestDealFlow(t, builder.Builder, time.Second, false, true)
+ test.TestDealFlow(t, builder.Builder, time.Second, false, true, 0)
})
t.Run("retrieval-second", func(t *testing.T) {
- test.TestSenondDealRetrieval(t, builder.Builder, time.Second)
+ test.TestSecondDealRetrieval(t, builder.Builder, time.Second)
})
}
diff --git a/scripts/devnet.bash b/scripts/devnet.bash
index 8ffddaea4..a53cbc7b9 100755
--- a/scripts/devnet.bash
+++ b/scripts/devnet.bash
@@ -64,7 +64,7 @@ cat > "${BASEDIR}/scripts/create_miner.bash" <&1 | tee -a ${BASEDIR}/daemon.log" C-m
@@ -186,7 +187,7 @@ export LOTUS_PATH="${BASEDIR}/.lotus"
${BASEDIR}/bin/lotus wait-api
tmux send-keys -t $session:$wminer "${BASEDIR}/scripts/create_miner.bash" C-m
-tmux send-keys -t $session:$wminer "lotus-miner run --api 48020 --nosync 2>&1 | tee -a ${BASEDIR}/miner.log" C-m
+tmux send-keys -t $session:$wminer "lotus-miner run --miner-api 48020 --nosync 2>&1 | tee -a ${BASEDIR}/miner.log" C-m
tmux send-keys -t $session:$wcli "${BASEDIR}/scripts/monitor.bash" C-m
tmux send-keys -t $session:$wpleding "${BASEDIR}/scripts/pledge_sectors.bash" C-m
diff --git a/storage/addresses.go b/storage/addresses.go
index f5640794e..f34625a56 100644
--- a/storage/addresses.go
+++ b/storage/addresses.go
@@ -3,13 +3,11 @@ package storage
import (
"context"
- "github.com/filecoin-project/lotus/chain/actors/builtin/miner"
-
- "golang.org/x/xerrors"
-
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
+ "github.com/filecoin-project/go-state-types/big"
+ "github.com/filecoin-project/lotus/chain/actors/builtin/miner"
"github.com/filecoin-project/lotus/chain/types"
)
@@ -28,67 +26,62 @@ type addrSelectApi interface {
StateAccountKey(context.Context, address.Address, types.TipSetKey) (address.Address, error)
}
-func AddressFor(ctx context.Context, a addrSelectApi, mi miner.MinerInfo, use AddrUse, minFunds abi.TokenAmount) (address.Address, error) {
+func AddressFor(ctx context.Context, a addrSelectApi, mi miner.MinerInfo, use AddrUse, goodFunds, minFunds abi.TokenAmount) (address.Address, abi.TokenAmount, error) {
switch use {
case PreCommitAddr, CommitAddr:
// always use worker, at least for now
- return mi.Worker, nil
+ return mi.Worker, big.Zero(), nil
}
- for _, addr := range mi.ControlAddresses {
- b, err := a.WalletBalance(ctx, addr)
- if err != nil {
- return address.Undef, xerrors.Errorf("checking control address balance: %w", err)
+ leastBad := mi.Worker
+ bestAvail := minFunds
+
+ for _, addr := range append(mi.ControlAddresses, mi.Owner, mi.Worker) {
+ if maybeUseAddress(ctx, a, addr, goodFunds, &leastBad, &bestAvail) {
+ return leastBad, bestAvail, nil
}
-
- if b.GreaterThanEqual(minFunds) {
- k, err := a.StateAccountKey(ctx, addr, types.EmptyTSK)
- if err != nil {
- log.Errorw("getting account key", "error", err)
- continue
- }
-
- have, err := a.WalletHas(ctx, k)
- if err != nil {
- return address.Undef, xerrors.Errorf("failed to check control address: %w", err)
- }
-
- if !have {
- log.Errorw("don't have key", "key", k)
- continue
- }
-
- return addr, nil
- }
-
- log.Warnw("control address didn't have enough funds for window post message", "address", addr, "required", types.FIL(minFunds), "balance", types.FIL(b))
}
- // Try to use the owner account if we can, fallback to worker if we can't
+ log.Warnw("No address had enough funds to for full PoSt message Fee, selecting least bad address", "address", leastBad, "balance", types.FIL(bestAvail), "optimalFunds", types.FIL(goodFunds), "minFunds", types.FIL(minFunds))
- b, err := a.WalletBalance(ctx, mi.Owner)
- if err != nil {
- return address.Undef, xerrors.Errorf("checking owner balance: %w", err)
- }
-
- if !b.GreaterThanEqual(minFunds) {
- return mi.Worker, nil
- }
-
- k, err := a.StateAccountKey(ctx, mi.Owner, types.EmptyTSK)
- if err != nil {
- log.Errorw("getting owner account key", "error", err)
- return mi.Worker, nil
- }
-
- have, err := a.WalletHas(ctx, k)
- if err != nil {
- return address.Undef, xerrors.Errorf("failed to check owner address: %w", err)
- }
-
- if !have {
- return mi.Worker, nil
- }
-
- return mi.Owner, nil
+ return leastBad, bestAvail, nil
+}
+
+func maybeUseAddress(ctx context.Context, a addrSelectApi, addr address.Address, goodFunds abi.TokenAmount, leastBad *address.Address, bestAvail *abi.TokenAmount) bool {
+ b, err := a.WalletBalance(ctx, addr)
+ if err != nil {
+ log.Errorw("checking control address balance", "addr", addr, "error", err)
+ return false
+ }
+
+ if b.GreaterThanEqual(goodFunds) {
+ k, err := a.StateAccountKey(ctx, addr, types.EmptyTSK)
+ if err != nil {
+ log.Errorw("getting account key", "error", err)
+ return false
+ }
+
+ have, err := a.WalletHas(ctx, k)
+ if err != nil {
+ log.Errorw("failed to check control address", "addr", addr, "error", err)
+ return false
+ }
+
+ if !have {
+ log.Errorw("don't have key", "key", k)
+ return false
+ }
+
+ *leastBad = addr
+ *bestAvail = b
+ return true
+ }
+
+ if b.GreaterThan(*bestAvail) {
+ *leastBad = addr
+ *bestAvail = b
+ }
+
+ log.Warnw("address didn't have enough funds for window post message", "address", addr, "required", types.FIL(goodFunds), "balance", types.FIL(b))
+ return false
}
diff --git a/storage/miner.go b/storage/miner.go
index daeb0ef20..dcec234fd 100644
--- a/storage/miner.go
+++ b/storage/miner.go
@@ -95,6 +95,8 @@ type storageMinerApi interface {
MpoolPushMessage(context.Context, *types.Message, *api.MessageSendSpec) (*types.SignedMessage, error)
GasEstimateMessageGas(context.Context, *types.Message, *api.MessageSendSpec, types.TipSetKey) (*types.Message, error)
+ GasEstimateFeeCap(context.Context, *types.Message, int64, types.TipSetKey) (types.BigInt, error)
+ GasEstimateGasPremium(_ context.Context, nblocksincl uint64, sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error)
ChainHead(context.Context) (*types.TipSet, error)
ChainNotify(context.Context) (<-chan []*api.HeadChange, error)
diff --git a/storage/mockstorage/preseal.go b/storage/mockstorage/preseal.go
index d119c273f..66a2a5054 100644
--- a/storage/mockstorage/preseal.go
+++ b/storage/mockstorage/preseal.go
@@ -4,6 +4,7 @@ import (
"fmt"
"github.com/filecoin-project/go-address"
+ "github.com/filecoin-project/go-commp-utils/zerocomm"
commcid "github.com/filecoin-project/go-fil-commcid"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
@@ -13,7 +14,6 @@ import (
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/wallet"
- "github.com/filecoin-project/lotus/extern/sector-storage/zerocomm"
"github.com/filecoin-project/lotus/genesis"
)
diff --git a/storage/wdpost_run.go b/storage/wdpost_run.go
index 8bf2cc6ad..4d3d4b726 100644
--- a/storage/wdpost_run.go
+++ b/storage/wdpost_run.go
@@ -26,6 +26,7 @@ import (
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
"github.com/filecoin-project/lotus/chain/actors/policy"
+ "github.com/filecoin-project/lotus/chain/messagepool"
"github.com/filecoin-project/lotus/chain/types"
)
@@ -211,14 +212,13 @@ func (s *WindowPoStScheduler) checkSectors(ctx context.Context, check bitfield.B
Number: info.SectorNumber,
},
})
-
}
- bad, err := s.faultTracker.CheckProvable(ctx, s.proofType, tocheck)
+ bad, err := s.faultTracker.CheckProvable(ctx, s.proofType, tocheck, nil)
if err != nil {
return bitfield.BitField{}, xerrors.Errorf("checking provable sectors: %w", err)
}
- for _, id := range bad {
+ for id := range bad {
delete(sectors, id.Number)
}
@@ -781,14 +781,38 @@ func (s *WindowPoStScheduler) setSender(ctx context.Context, msg *types.Message,
}
*msg = *gm
- minFunds := big.Add(msg.RequiredFunds(), msg.Value)
+ // estimate
+ minGasFeeMsg := *msg
- pa, err := AddressFor(ctx, s.api, mi, PoStAddr, minFunds)
+ minGasFeeMsg.GasPremium, err = s.api.GasEstimateGasPremium(ctx, 5, msg.From, msg.GasLimit, types.TipSetKey{})
+ if err != nil {
+ log.Errorf("failed to estimate minimum gas premium: %+v", err)
+ minGasFeeMsg.GasPremium = msg.GasPremium
+ }
+
+ minGasFeeMsg.GasFeeCap, err = s.api.GasEstimateFeeCap(ctx, &minGasFeeMsg, 4, types.EmptyTSK)
+ if err != nil {
+ log.Errorf("failed to estimate minimum gas fee cap: %+v", err)
+ minGasFeeMsg.GasFeeCap = msg.GasFeeCap
+ }
+
+ goodFunds := big.Add(msg.RequiredFunds(), msg.Value)
+ minFunds := big.Min(big.Add(minGasFeeMsg.RequiredFunds(), minGasFeeMsg.Value), goodFunds)
+
+ pa, avail, err := AddressFor(ctx, s.api, mi, PoStAddr, goodFunds, minFunds)
if err != nil {
log.Errorw("error selecting address for window post", "error", err)
return nil
}
msg.From = pa
+ bestReq := big.Add(msg.RequiredFunds(), msg.Value)
+ if avail.LessThan(bestReq) {
+ mff := func() (abi.TokenAmount, error) {
+ return msg.RequiredFunds(), nil
+ }
+
+ messagepool.CapGasFee(mff, msg, big.Min(big.Sub(avail, msg.Value), msg.RequiredFunds()))
+ }
return nil
}
diff --git a/storage/wdpost_run_test.go b/storage/wdpost_run_test.go
index 436141295..65b6b0183 100644
--- a/storage/wdpost_run_test.go
+++ b/storage/wdpost_run_test.go
@@ -27,6 +27,7 @@ import (
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
"github.com/filecoin-project/lotus/chain/types"
+ "github.com/filecoin-project/lotus/extern/sector-storage/storiface"
"github.com/filecoin-project/lotus/journal"
)
@@ -49,6 +50,10 @@ func (m *mockStorageMinerAPI) StateMinerInfo(ctx context.Context, a address.Addr
}, nil
}
+func (m *mockStorageMinerAPI) StateNetworkVersion(ctx context.Context, key types.TipSetKey) (network.Version, error) {
+ return build.NewestNetworkVersion, nil
+}
+
func (m *mockStorageMinerAPI) ChainGetRandomnessFromTickets(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) {
return abi.Randomness("ticket rand"), nil
}
@@ -94,8 +99,12 @@ func (m *mockStorageMinerAPI) StateWaitMsg(ctx context.Context, cid cid.Cid, con
}, nil
}
-func (m *mockStorageMinerAPI) StateNetworkVersion(context.Context, types.TipSetKey) (network.Version, error) {
- return build.NewestNetworkVersion, nil
+func (m *mockStorageMinerAPI) GasEstimateGasPremium(_ context.Context, nblocksincl uint64, sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error) {
+ return big.Zero(), nil
+}
+
+func (m *mockStorageMinerAPI) GasEstimateFeeCap(context.Context, *types.Message, int64, types.TipSetKey) (types.BigInt, error) {
+ return big.Zero(), nil
}
type mockProver struct {
@@ -117,9 +126,9 @@ func (m *mockProver) GenerateWindowPoSt(ctx context.Context, aid abi.ActorID, si
type mockFaultTracker struct {
}
-func (m mockFaultTracker) CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof, sectors []storage.SectorRef) ([]abi.SectorID, error) {
- // Returns "bad" sectors so just return nil meaning all sectors are good
- return nil, nil
+func (m mockFaultTracker) CheckProvable(ctx context.Context, pp abi.RegisteredPoStProof, sectors []storage.SectorRef, rg storiface.RGetter) (map[abi.SectorID]string, error) {
+ // Returns "bad" sectors so just return empty map meaning all sectors are good
+ return map[abi.SectorID]string{}, nil
}
// TestWDPostDoPost verifies that doPost will send the correct number of window
diff --git a/testplans/DELVING.md b/testplans/DELVING.md
new file mode 100644
index 000000000..4c2d36390
--- /dev/null
+++ b/testplans/DELVING.md
@@ -0,0 +1,193 @@
+# Delving into the unknown
+
+This write-up summarises how to debug what appears to be a mischievous Lotus
+instance during our Testground tests. It also goes enumerates which assets are
+useful to report suspicious behaviours upstream, in a way that they are
+actionable.
+
+## Querying the Lotus RPC API
+
+The `local:docker` and `cluster:k8s` map ports that you specify in the
+composition.toml, so you can access them externally.
+
+All our compositions should carry this fragment:
+
+```toml
+[global.run_config]
+ exposed_ports = { pprof = "6060", node_rpc = "1234", miner_rpc = "2345" }
+```
+
+This tells Testground to expose the following ports:
+
+* `6060` => Go pprof.
+* `1234` => Lotus full node RPC.
+* `2345` => Lotus storage miner RPC.
+
+### `local:docker`
+
+1. Install the `lotus` binary on your host.
+2. Find the container that you want to connect to in `docker ps`.
+ * Note that our _container names_ are slightly long, and they're the last
+ field on every line, so if your terminal is wrapping text, the port
+ numbers will end up ABOVE the friendly/recognizable container name (e.g. `tg-lotus-soup-deals-e2e-acfc60bc1727-miners-1`).
+ * The testground output displays the _container ID_ inside coloured angle
+ brackets, so if you spot something spurious in a particular node, you can
+ hone in on that one, e.g. `<< 54dd5ad916b2 >>`.
+
+ ```
+ ⟩ docker ps
+ CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
+ 54dd5ad916b2 be3c18d7f0d4 "/testplan" 10 seconds ago Up 8 seconds 0.0.0.0:32788->1234/tcp, 0.0.0.0:32783->2345/tcp, 0.0.0.0:32773->6060/tcp, 0.0.0.0:32777->6060/tcp tg-lotus-soup-deals-e2e-acfc60bc1727-clients-2
+ 53757489ce71 be3c18d7f0d4 "/testplan" 10 seconds ago Up 8 seconds 0.0.0.0:32792->1234/tcp, 0.0.0.0:32790->2345/tcp, 0.0.0.0:32781->6060/tcp, 0.0.0.0:32786->6060/tcp tg-lotus-soup-deals-e2e-acfc60bc1727-clients-1
+ 9d3e83b71087 be3c18d7f0d4 "/testplan" 10 seconds ago Up 8 seconds 0.0.0.0:32791->1234/tcp, 0.0.0.0:32789->2345/tcp, 0.0.0.0:32779->6060/tcp, 0.0.0.0:32784->6060/tcp tg-lotus-soup-deals-e2e-acfc60bc1727-clients-0
+ 7bd60e75ed0e be3c18d7f0d4 "/testplan" 10 seconds ago Up 8 seconds 0.0.0.0:32787->1234/tcp, 0.0.0.0:32782->2345/tcp, 0.0.0.0:32772->6060/tcp, 0.0.0.0:32776->6060/tcp tg-lotus-soup-deals-e2e-acfc60bc1727-miners-1
+ dff229d7b342 be3c18d7f0d4 "/testplan" 10 seconds ago Up 9 seconds 0.0.0.0:32778->1234/tcp, 0.0.0.0:32774->2345/tcp, 0.0.0.0:32769->6060/tcp, 0.0.0.0:32770->6060/tcp tg-lotus-soup-deals-e2e-acfc60bc1727-miners-0
+ 4cd67690e3b8 be3c18d7f0d4 "/testplan" 11 seconds ago Up 8 seconds 0.0.0.0:32785->1234/tcp, 0.0.0.0:32780->2345/tcp, 0.0.0.0:32771->6060/tcp, 0.0.0.0:32775->6060/tcp tg-lotus-soup-deals-e2e-acfc60bc1727-bootstrapper-0
+ aeb334adf88d iptestground/sidecar:edge "testground sidecar …" 43 hours ago Up About an hour 0.0.0.0:32768->6060/tcp testground-sidecar
+ c1157500282b influxdb:1.8 "/entrypoint.sh infl…" 43 hours ago Up 25 seconds 0.0.0.0:8086->8086/tcp testground-influxdb
+ 99ca4c07fecc redis "docker-entrypoint.s…" 43 hours ago Up About an hour 0.0.0.0:6379->6379/tcp testground-redis
+ bf25c87488a5 bitnami/grafana "/run.sh" 43 hours ago Up 26 seconds 0.0.0.0:3000->3000/tcp testground-grafana
+ cd1d6383eff7 goproxy/goproxy "/goproxy" 45 hours ago Up About a minute 8081/tcp testground-goproxy
+ ```
+
+3. Take note of the port mapping. Imagine in the output above, we want to query
+ `54dd5ad916b2`. We'd use `localhost:32788`, as it forwards to the container's
+ 1234 port (Lotus Full Node RPC).
+4. Run your Lotus CLI command setting the `FULLNODE_API_INFO` env variable,
+ which is a multiaddr:
+
+ ```sh
+ $ FULLNODE_API_INFO=":/ip4/127.0.0.1/tcp/$port/http" lotus chain list
+ [...]
+ ```
+
+---
+
+Alternatively, you could download gawk and setup a script in you .bashrc or .zshrc similar to:
+
+```
+lprt() {
+ NAME=$1
+ PORT=$2
+
+ docker ps --format "table {{.Names}}" | grep $NAME | xargs -I {} docker port {} $PORT | gawk --field-separator=":" '{print $2}'
+}
+
+envs() {
+ NAME=$1
+
+ local REMOTE_PORT_1234=$(lprt $NAME 1234)
+ local REMOTE_PORT_2345=$(lprt $NAME 2345)
+
+ export FULLNODE_API_INFO=":/ip4/127.0.0.1/tcp/$REMOTE_PORT_1234/http"
+ export STORAGE_API_INFO=":/ip4/127.0.0.1/tcp/$REMOTE_PORT_2345/http"
+
+ echo "Setting \$FULLNODE_API_INFO to $FULLNODE_API_INFO"
+ echo "Setting \$STORAGE_API_INFO to $STORAGE_API_INFO"
+}
+```
+
+Then call commands like:
+```
+envs miners-0
+lotus chain list
+```
+
+### `cluster:k8s`
+
+Similar to `local:docker`, you pick a pod that you want to connect to and port-forward 1234 and 2345 to that specific pod, such as:
+
+```
+export PODNAME="tg-lotus-soup-ae620dfb2e19-miners-0"
+kubectl port-forward pods/$PODNAME 1234:1234 2345:2345
+
+export FULLNODE_API_INFO=":/ip4/127.0.0.1/tcp/1234/http"
+export STORAGE_API_INFO=":/ip4/127.0.0.1/tcp/2345/http"
+lotus-storage-miner storage-deals list
+lotus-storage-miner storage-deals get-ask
+```
+
+### Useful commands / checks
+
+* **Making sure miners are on the same chain:** compare outputs of `lotus chain list`.
+* **Checking deals:** `lotus client list-deals`.
+* **Sector queries:** `lotus-storage-miner info` , `lotus-storage-miner proving info`
+* **Sector sealing errors:**
+ * `STORAGE_API_INFO=":/ip4/127.0.0.1/tcp/53624/http" FULLNODE_API_INFO=":/ip4/127.0.0.1/tcp/53623/http" lotus-storage-miner sector info`
+ * `STORAGE_API_INFO=":/ip4/127.0.0.1/tcp/53624/http" FULLNODE_API_INFO=":/ip4/127.0.0.1/tcp/53623/http" lotus-storage-miner sector status `
+ * `STORAGE_API_INFO=":/ip4/127.0.0.1/tcp/53624/http" FULLNODE_API_INFO=":/ip4/127.0.0.1/tcp/53623/http" lotus-storage-miner sector status --log `
+
+## Viewing logs of a particular container `local:docker`
+
+This works for both started and stopped containers. Just get the container ID
+(in double angle brackets in Testground output, on every log line), and do a:
+
+```shell script
+$ docker logs $container_id
+```
+
+## Accessing the golang instrumentation
+
+Testground exposes a pprof endpoint under local port 6060, which both
+`local:docker` and `cluster:k8s` map.
+
+For `local:docker`, see above to figure out which host port maps to the
+container's 6060 port.
+
+## Acquiring a goroutine dump
+
+When things appear to be stuck, get a goroutine dump.
+
+```shell script
+$ wget -o goroutine.out http://localhost:${pprof_port}/debug/pprof/goroutine?debug=2
+```
+
+You can use whyrusleeping/stackparse to extract a summary:
+
+```shell script
+$ go get https://github.com/whyrusleeping/stackparse
+$ stackparse --summary goroutine.out
+```
+
+## Acquiring a CPU profile
+
+When the CPU appears to be spiking/rallying, grab a CPU profile.
+
+```shell script
+$ wget -o profile.out http://localhost:${pprof_port}/debug/pprof/profile
+```
+
+Analyse it using `go tool pprof`. Usually, generating a `png` graph is useful:
+
+```shell script
+$ go tool pprof profile.out
+File: testground
+Type: cpu
+Time: Jul 3, 2020 at 12:00am (WEST)
+Duration: 30.07s, Total samples = 2.81s ( 9.34%)
+Entering interactive mode (type "help" for commands, "o" for options)
+(pprof) png
+Generating report in profile003.png
+```
+
+## Submitting actionable reports / findings
+
+This is useful both internally (within the Oni team, so that peers can help) and
+externally (when submitting a finding upstream).
+
+We don't need to play the full bug-hunting game on Lotus, but it's tremendously
+useful to provide the necessary data so that any reports are actionable.
+
+These include:
+
+* test outputs (use `testground collect`).
+* stack traces that appear in logs (whether panics or not).
+* output of relevant Lotus CLI commands.
+* if this is some kind of blockage / deadlock, goroutine dumps.
+* if this is a CPU hotspot, a CPU profile would be useful.
+* if this is a memory issue, a heap dump would be useful.
+
+**When submitting bugs upstream (Lotus), make sure to indicate:**
+
+* Lotus commit.
+* FFI commit.
diff --git a/testplans/Makefile b/testplans/Makefile
new file mode 100644
index 000000000..b8d7dae5b
--- /dev/null
+++ b/testplans/Makefile
@@ -0,0 +1,20 @@
+SHELL = /bin/bash
+
+.DEFAULT_GOAL := download-proofs
+
+download-proofs:
+ go run github.com/filecoin-project/go-paramfetch/paramfetch 2048 ./docker-images/proof-parameters.json
+
+build-images:
+ docker build -t "iptestground/oni-buildbase:v11-lotus" -f "docker-images/Dockerfile.oni-buildbase" "docker-images"
+ docker build -t "iptestground/oni-runtime:v5" -f "docker-images/Dockerfile.oni-runtime" "docker-images"
+
+push-images:
+ docker push iptestground/oni-buildbase:v11-lotus
+ docker push iptestground/oni-runtime:v5
+
+pull-images:
+ docker pull iptestground/oni-buildbase:v11-lotus
+ docker pull iptestground/oni-runtime:v5
+
+.PHONY: download-proofs build-images push-images pull-images
diff --git a/testplans/README-old-from-oni.md b/testplans/README-old-from-oni.md
new file mode 100644
index 000000000..1a6b5debe
--- /dev/null
+++ b/testplans/README-old-from-oni.md
@@ -0,0 +1,254 @@
+# Project Oni 👹
+
+Our mandate is:
+
+> To verify the successful end-to-end outcome of the filecoin protocol and filecoin implementations, under a variety of real-world and simulated scenarios.
+
+➡️ Find out more about our goals, requirements, execution plan, and team culture, in our [Project Description](https://docs.google.com/document/d/16jYL--EWYpJhxT9bakYq7ZBGLQ9SB940Wd1lTDOAbNE).
+
+## Table of Contents
+
+- [Testing topics](#testing-topics)
+- [Repository contents](#repository-contents)
+- [Running the test cases](#running-the-test-cases)
+- [Catalog](#catalog)
+- [Debugging](#debugging)
+- [Dependencies](#dependencies)
+- [Docker images changelog](#docker-images-changelog)
+- [Team](#team)
+
+## Testing topics
+
+These are the topics we are currently centering our testing efforts on. Our testing efforts include fault induction, stress tests, and end-to-end testing.
+
+* **slashing:** [_(view test scenarios)_](https://github.com/filecoin-project/oni/issues?q=is%3Aissue+sort%3Aupdated-desc+label%3Atopic%2Fslashing)
+ * We are recreating the scenarios that lead to slashing, as they are not readily seen in mono-client testnets.
+ * Context: slashing is the negative economic consequence of penalising a miner that has breached protocol by deducing FIL and/or removing their power from the network.
+* **windowed PoSt/sector proving faults:** [_(view test scenarios)_](https://github.com/filecoin-project/oni/issues?q=is%3Aissue+sort%3Aupdated-desc+label%3Atopic%2Fsector-proving)
+ * We are recreating the proving fault scenarios and triggering them in an accelerated fasion (by modifying the system configuration), so that we're able to verify that the sector state transitions properly through the different milestones (temporary faults, termination, etc.), and under chain fork conditions.
+ * Context: every 24 hours there are 36 windows where miners need to submit their proofs of sector liveness, correctness, and validity. Failure to do so will mark a sector as faulted, and will eventually terminate the sector, triggering slashing consequences for the miner.
+* **syncing/fork selection:** [_(view test scenarios)_](https://github.com/filecoin-project/oni/issues?q=is%3Aissue+sort%3Aupdated-desc+label%3Atopic%2Fsync-forks)
+ * Newly bootstrapped clients, and paused-then-resumed clients, are able to latch on to the correct chain even in the presence of a large number of forks in the network, either in the present, or throughout history.
+* **present-time mining/tipset assembly:** [_(view test scenarios)_](https://github.com/filecoin-project/oni/issues?q=is%3Aissue+sort%3Aupdated-desc+label%3Atopic%2Fmining-present)
+ * Induce forks in the network, create network partitions, simulate chain halts, long-range forks, etc. Stage many kinds of convoluted chain shapes, and network partitions, and ensure that miners are always able to arrive to consensus when disruptions subside.
+* **catch-up/rush mining:** [_(view test scenarios)_](https://github.com/filecoin-project/oni/issues?q=is%3Aissue+sort%3Aupdated-desc+label%3Atopic%2Fmining-rush)
+ * Induce network-wide, or partition-wide arrests, and investigate what the resulting chain is after the system is allowed to recover.
+ * Context: catch-up/rush mining is a dedicated pathway in the mining logic that brings the chain up to speed with present time, in order to recover from network halts. Basically it entails producing backdated blocks in a hot loop. Imagine all miners recover in unison from a network-wide disruption; miners will produce blocks for their winning rounds, and will label losing rounds as _null rounds_. In the current implementation, there is no time for block propagation, so miners will produce solo-chains, and the assumption is that when all these chains hit the network, the _fork choice rule_ will pick the heaviest one. Unfortunately this process is brittle and unbalanced, as it favours the miner that held the highest power before the disruption commenced.
+* **storage and retrieval deals:** [_(view test scenarios)_](https://github.com/filecoin-project/oni/issues?q=is%3Aissue+sort%3Aupdated-desc+label%3Atopic%2Fdeals)
+ * end-to-end flows where clients store and retrieve pieces from miners, including stress testing the system.
+* **payment channels:** [_(view test scenarios)_](https://github.com/filecoin-project/oni/issues?q=is%3Aissue+sort%3Aupdated-desc+label%3Atopic%2Fpaych)
+ * stress testing payment channels via excessive lane creation, excessive payment voucher atomisation, and redemption.
+* **drand incidents and impact on the filecoin network/protocol/chain:** [_(view test scenarios)_](https://github.com/filecoin-project/oni/issues?q=is%3Aissue+sort%3Aupdated-desc+label%3Atopic%2Fdrand)
+ * drand total unavailabilities, drand catch-ups, drand slowness, etc.
+* **mempool message selection:** [_(view test scenarios)_](https://github.com/filecoin-project/oni/issues?q=is%3Aissue+sort%3Aupdated-desc+label%3Atopic%2Fmempool)
+ * soundness of message selection logic; potentially targeted attacks against miners by flooding their message pools with different kinds of messages.
+* **presealing:** [_(view test scenarios)_](https://github.com/filecoin-project/oni/issues?q=is%3Aissue+sort%3Aupdated-desc+label%3Atopic%2Fpresealing)
+ * TBD, anything related to this worth testing?
+
+## Repository contents
+
+This repository consists of [test plans](https://docs.testground.ai/concepts-and-architecture/test-structure) built to be run on [Testground](https://github.com/testground/testground).
+
+The source code for the various test cases can be found in the [`lotus-soup` directory](https://github.com/filecoin-project/oni/tree/master/lotus-soup).
+
+## Running the test cases
+
+If you are unfamiliar with Testground, we strongly suggest you read the Testground [Getting Started guide](https://docs.testground.ai/getting-started) in order to learn how to install Testground and how to use it.
+
+You can find various [composition files](https://docs.testground.ai/running-test-plans#composition-runs) describing various test scenarios built as part of Project Oni at [`lotus-soup/_compositions` directory](https://github.com/filecoin-project/oni/tree/master/lotus-soup/_compositions).
+
+We've designed the test cases so that you can run them via the `local:exec`, `local:docker` and the `cluster:k8s` runners. Note that Lotus miners are quite resource intensive, requiring gigabytes of memory. Hence you would have to run these test cases on a beafy machine (when using `local:docker` and `local:exec`), or on a Kubernetes cluster (when using `cluster:k8s`).
+
+Here are the basics of how to run the baseline deals end-to-end test case:
+
+### Running the baseline deals end-to-end test case
+
+1. Compile and Install Testground from source code.
+ * See the [Getting Started](https://github.com/testground/testground#getting-started) section of the README for instructions.
+
+2. Run a Testground daemon
+
+```
+testground daemon
+```
+
+3. Download required Docker images for the `lotus-soup` test plan
+
+```
+make pull-images
+```
+
+Alternatively you can build them locally with
+
+```
+make build-images
+```
+
+4. Import the `lotus-soup` test plan into your Testground home directory
+
+```
+testground plan import --from ./lotus-soup
+```
+
+5. Init the `filecoin-ffi` Git submodule in the `extra` folder.
+
+```
+git submodule update --init --recursive
+```
+
+6. Compile the `filecoin-ffi` version locally (necessary if you use `local:exec`)
+
+```
+cd extra/filecoin-ffi
+make
+```
+
+7. Run a composition for the baseline deals end-to-end test case
+
+```
+testground run composition -f ./lotus-soup/_compositions/baseline-docker-5-1.toml
+```
+
+## Batch-running randomised test cases
+
+The Oni testkit supports [range parameters](https://github.com/filecoin-project/oni/blob/master/lotus-soup/testkit/testenv_ranges.go),
+which test cases can use to generate random values, either at the instance level
+(each instance computes a random value within range), or at the run level (one
+instance computes the values, and propagates them to all other instances via the
+sync service).
+
+For example:
+
+```toml
+latency_range = '["20ms", "500ms"]'
+loss_range = '[0, 0.2]'
+```
+
+Could pick a random latency between 20ms and 500ms, and a packet loss
+probability between 0 and 0.2. We could apply those values through the
+`netclient.ConfigureNetwork` Testground SDK API.
+
+Randomized range-based parameters are specially interesting when combined with
+batch runs, as it enables Monte Carlo approaches to testing.
+
+The Oni codebase includes a batch test run driver in package `lotus-soup/runner`.
+You can point it at a composition file that uses range parameters and tell it to
+run N iterations of the test:
+
+```shell script
+$ go run ./runner -runs 5 _compositions/net-chaos/latency.toml
+```
+
+This will run the test as many times as instructed, and will place all outputs
+in a temporary directory. You can pass a concrete output directory with
+the `-output` flag.
+
+## Catalog
+
+### Test cases part of `lotus-soup`
+
+* `deals-e2e` - Deals end-to-end test case. Clients pick a miner at random, start a deal, wait for it to be sealed, and try to retrieve from another random miner who offers back the data.
+* `drand-halting` - Test case that instructs Drand with a sequence of halt/resume/wait events, while running deals between clients and miners at the same time.
+* `deals-stress` - Deals stress test case. Clients pick a miner and send multiple deals (concurrently or serially) in order to test how many deals miners can handle.
+* `paych-stress` - A test case exercising various payment channel stress tests.
+
+### Compositions part of `lotus-soup`
+
+* `baseline-docker-5-1.toml` - Runs a `baseline` test (deals e2e test) with a network of 5 clients and 1 miner targeting `local:docker`
+* `baseline-k8s-10-3.toml` - Runs a `baseline` test (deals e2e test) with a network of 10 clients and 3 miner targeting `cluster:k8s`
+* `baseline-k8s-3-1.toml` - Runs a `baseline` test (deals e2e test) with a network of 3 clients and 1 miner targeting `cluster:k8s`
+* `baseline-k8s-3-2.toml` - Runs a `baseline` test (deals e2e test) with a network of 3 clients and 2 miner targeting `cluster:k8s`
+* `baseline.toml` - Runs a `baseline` test (deals e2e test) with a network of 3 clients and 2 miner targeting `local:exec`. You have to manually download the proof parameters and place them in `/var/tmp`.
+* `deals-stress-concurrent-natural-k8s.toml`
+* `deals-stress-concurrent-natural.toml`
+* `deals-stress-concurrent.toml`
+* `deals-stress-serial-natural.toml`
+* `deals-stress-serial.toml`
+* `drand-halt.toml`
+* `local-drand.toml`
+* `natural.toml`
+* `paych-stress.toml`
+* `pubsub-tracer.toml`
+
+
+## Debugging
+
+Find commands and how-to guides on debugging test plans at [DELVING.md](https://github.com/filecoin-project/oni/blob/master/DELVING.md)
+
+1. Querying the Lotus RPC API
+
+2. Useful commands / checks
+
+* Making sure miners are on the same chain
+
+* Checking deals
+
+* Sector queries
+
+* Sector sealing errors
+
+## Dependencies
+
+Our current test plan `lotus-soup` is building programatically the Lotus filecoin implementation and therefore requires all it's dependencies. The build process is slightly more complicated than a normal Go project, because we are binding a bit of Rust code. Lotus codebase is in Go, however its `proofs` and `crypto` libraries are in Rust (BLS signatures, SNARK verification, etc.).
+
+Depending on the runner you want to use to run the test plan, these dependencies are included in the build process in a different way, which you should be aware of should you require to use the test plan with a newer version of Lotus:
+
+### Filecoin FFI libraries
+
+* `local:docker`
+
+The Rust libraries are included in the Filecoin FFI Git submodule, which is part of the `iptestground/oni-buildbase` image. If the FFI changes on Lotus, we have to rebuild this image with the `make build-images` command, where X is the next version (see [Docker images changelog](#docker-images-changelog)
+below).
+
+* `local:exec`
+
+The Rust libraries are included via the `extra` directory. Make sure that the test plan reference to Lotus in `go.mod` and the `extra` directory are pointing to the same commit of the FFI git submodule. You also need to compile the `extra/filecoin-ffi` libraries with `make`.
+
+* `cluster:k8s`
+
+The same process as for `local:docker`, however you need to make sure that the respective `iptestground/oni-buildbase` image is available as a public Docker image, so that the Kubernetes cluster can download it.
+
+### proof parameters
+
+Additional to the Filecoin FFI Git submodules, we are also bundling `proof parameters` in the `iptestground/oni-runtime` image. If these change, you will need to rebuild that image with `make build-images` command, where X is the next version.
+
+## Docker images changelog
+
+### oni-buildbase
+
+* `v1` => initial image locking in Filecoin FFI commit ca281af0b6c00314382a75ae869e5cb22c83655b.
+* `v2` => no changes; released only for aligning both images to aesthetically please @nonsense :D
+* `v3` => locking in Filecoin FFI commit 5342c7c97d1a1df4650629d14f2823d52889edd9.
+* `v4` => locking in Filecoin FFI commit 6a143e06f923f3a4f544c7a652e8b4df420a3d28.
+* `v5` => locking in Filecoin FFI commit cddc56607e1d851ea6d09d49404bd7db70cb3c2e.
+* `v6` => locking in Filecoin FFI commit 40569104603407c999d6c9e4c3f1228cbd4d0e5c.
+* `v7` => add Filecoin-BLST repo to buildbase.
+* `v8` => locking in Filecoin FFI commit f640612a1a1f7a2d.
+* `v9` => locking in Filecoin FFI commit 57e38efe4943f09d3127dcf6f0edd614e6acf68e and Filecoin-BLST commit 8609119cf4595d1741139c24378fcd8bc4f1c475.
+
+
+### oni-runtime
+
+* `v1` => initial image with 2048 parameters.
+* `v2` => adds auxiliary tools: `net-tools netcat traceroute iputils-ping wget vim curl telnet iproute2 dnsutils`.
+* `v3` => bump proof parameters from v27 to v28
+
+### oni-runtime-debug
+
+* `v1` => initial image
+* `v2` => locking in Lotus commit e21ea53
+* `v3` => locking in Lotus commit d557c40
+* `v4` => bump proof parameters from v27 to v28
+* `v5` => locking in Lotus commit 1a170e18a
+
+
+## Team
+
+* [@raulk](https://github.com/raulk) (Captain + TL)
+* [@nonsense](https://github.com/nonsense) (Testground TG + engineer)
+* [@yusefnapora](https://github.com/yusefnapora) (engineer and technical writer)
+* [@vyzo](https://github.com/vyzo) (engineer)
+* [@schomatis](https://github.com/schomatis) (advisor)
+* [@willscott](https://github.com/willscott) (engineer)
+* [@alanshaw](https://github.com/alanshaw) (engineer)
+
diff --git a/testplans/README.md b/testplans/README.md
new file mode 100644
index 000000000..bab10e690
--- /dev/null
+++ b/testplans/README.md
@@ -0,0 +1,60 @@
+# Testground testplans for Lotus
+
+This directory consists of [testplans](https://docs.testground.ai/concepts-and-architecture/test-structure) built to be run on [Testground](https://github.com/testground/testground) that exercise Lotus on [TaaS](https://ci.testground.ipfs.team).
+
+## Table of Contents
+
+- [Testing topics](#testing-topics)
+- [Running the test cases](#running-the-test-cases)
+
+## Testing topics
+
+* **storage and retrieval deals:**
+ * end-to-end flows where clients store and retrieve pieces from miners, including stress testing the system.
+* **payment channels:**
+ * stress testing payment channels via excessive lane creation, excessive payment voucher atomisation, and redemption.
+
+## Running the test cases
+
+If you are unfamiliar with Testground, we strongly suggest you read the Testground [Getting Started guide](https://docs.testground.ai/getting-started) in order to learn how to install Testground and how to use it.
+
+You can find various [composition files](https://docs.testground.ai/running-test-plans#composition-runs) describing various test scenarios built as part of Project Oni at [`lotus-soup/_compositions` directory](https://github.com/filecoin-project/oni/tree/master/lotus-soup/_compositions).
+
+We've designed the test cases so that you can run them via the `local:exec`, `local:docker` and the `cluster:k8s` runners. Note that Lotus miners are quite resource intensive, requiring gigabytes of memory. Hence you would have to run these test cases on a beafy machine (when using `local:docker` and `local:exec`), or on a Kubernetes cluster (when using `cluster:k8s`).
+
+Here are the basics of how to run the baseline deals end-to-end test case:
+
+### Running the baseline deals end-to-end test case
+
+1. Compile and Install Testground from source code.
+ * See the [Getting Started](https://github.com/testground/testground#getting-started) section of the README for instructions.
+
+2. Run a Testground daemon
+
+```
+testground daemon
+```
+
+3. Download required Docker images for the `lotus-soup` test plan
+
+```
+make pull-images
+```
+
+Alternatively you can build them locally with
+
+```
+make build-images
+```
+
+4. Import the `lotus-soup` test plan into your Testground home directory
+
+```
+testground plan import --from ./lotus-soup
+```
+
+6. Run a composition for the baseline deals end-to-end test case
+
+```
+testground run composition -f ./lotus-soup/_compositions/baseline-docker-5-1.toml
+```
diff --git a/testplans/composer/Dockerfile b/testplans/composer/Dockerfile
new file mode 100644
index 000000000..6142650be
--- /dev/null
+++ b/testplans/composer/Dockerfile
@@ -0,0 +1,29 @@
+FROM golang:1.15-buster as tg-build
+
+ARG TESTGROUND_REF="oni"
+WORKDIR /usr/src
+RUN git clone https://github.com/testground/testground.git
+RUN cd testground && git checkout $TESTGROUND_REF && go build .
+
+FROM python:3.8-buster
+
+WORKDIR /usr/src/app
+
+COPY --from=tg-build /usr/src/testground/testground /usr/bin/testground
+
+RUN mkdir /composer && chmod 777 /composer
+RUN mkdir /testground && chmod 777 /testground
+
+ENV HOME /composer
+ENV TESTGROUND_HOME /testground
+ENV LISTEN_PORT 5006
+ENV TESTGROUND_DAEMON_HOST host.docker.internal
+
+VOLUME /testground/plans
+
+
+COPY requirements.txt ./
+RUN pip install -r requirements.txt
+COPY . .
+
+CMD panel serve --address 0.0.0.0 --port $LISTEN_PORT composer.ipynb
diff --git a/testplans/composer/Makefile b/testplans/composer/Makefile
new file mode 100644
index 000000000..60f022110
--- /dev/null
+++ b/testplans/composer/Makefile
@@ -0,0 +1,4 @@
+all: docker
+
+docker:
+ docker build -t "iptestground/composer:latest" .
diff --git a/testplans/composer/README.md b/testplans/composer/README.md
new file mode 100644
index 000000000..82cd130cb
--- /dev/null
+++ b/testplans/composer/README.md
@@ -0,0 +1,63 @@
+# Testground Composer
+
+This is a work-in-progress UI for configuring and running testground compositions.
+
+The app code lives in [./app](./app), and there's a thin Jupyter notebook shell in [composer.ipynb](./composer.ipynb).
+
+## Running
+
+You can either run the app in docker, or in a local python virtualenv. Docker is recommended unless you're hacking
+on the code for Composer itself.
+
+### Running with docker
+
+Run the `./composer.sh` script to build a container with the latest source and run it. The first build
+will take a little while since it needs to build testground and fetch a bunch of python dependencies.
+
+You can skip the build if you set `SKIP_BUILD=true` when running `composer.sh`, and you can rebuild
+manually with `make docker`.
+
+The contents of `$TESTGROUND_HOME/plans` will be sync'd to a temporary directory and read-only mounted
+into the container.
+
+After building and starting the container, the script will open a browser to the composer UI.
+
+You should be able to load an existing composition or create a new one from one of the plans in
+`$TESTGROUND_HOME/plans`.
+
+Right now docker only supports the standalone webapp UI; to run the UI in a Jupyter notebook, see below.
+
+### Running with local python
+
+To run without docker, make a python3 virtual environment somewhere and activate it:
+
+```shell
+# make a virtualenv called "venv" in the current directory
+python3 -m venv ./venv
+
+# activate (bash/zsh):
+source ./venv/bin/activate
+
+# activate (fish):
+source ./venv/bin/activate.fish
+```
+
+Then install the python dependencies:
+
+```shell
+pip install -r requirements.txt
+```
+
+And start the UI:
+
+```shell
+panel serve composer.ipynb
+```
+
+That will start the standalone webapp UI. If you want a Jupyter notebook instead, run:
+
+```
+jupyter notebook
+```
+
+and open `composer.ipynb` in the Jupyter file picker.
\ No newline at end of file
diff --git a/testplans/composer/app/app.py b/testplans/composer/app/app.py
new file mode 100644
index 000000000..c8d4aa3c1
--- /dev/null
+++ b/testplans/composer/app/app.py
@@ -0,0 +1,94 @@
+import param
+import panel as pn
+import toml
+from .util import get_plans, get_manifest
+from .composition import Composition
+from .runner import TestRunner
+
+STAGE_WELCOME = 'Welcome'
+STAGE_CONFIG_COMPOSITION = 'Configure'
+STAGE_RUN_TEST = 'Run'
+
+
+class Welcome(param.Parameterized):
+ composition = param.Parameter()
+ composition_picker = pn.widgets.FileInput(accept='.toml')
+ plan_picker = param.Selector()
+ ready = param.Boolean()
+
+ def __init__(self, **params):
+ super().__init__(**params)
+ self.composition_picker.param.watch(self._composition_updated, 'value')
+ self.param.watch(self._plan_selected, 'plan_picker')
+ self.param['plan_picker'].objects = ['Select a Plan'] + get_plans()
+
+ def panel(self):
+ tabs = pn.Tabs(
+ ('New Compostion', self.param['plan_picker']),
+ ('Existing Composition', self.composition_picker),
+ )
+
+ return pn.Column(
+ "Either choose an existing composition or select a plan to create a new composition:",
+ tabs,
+ )
+
+ def _composition_updated(self, *args):
+ print('composition updated')
+ content = self.composition_picker.value.decode('utf8')
+ comp_toml = toml.loads(content)
+ manifest = get_manifest(comp_toml['global']['plan'])
+ self.composition = Composition.from_dict(comp_toml, manifest=manifest)
+ print('existing composition: {}'.format(self.composition))
+ self.ready = True
+
+ def _plan_selected(self, evt):
+ if evt.new == 'Select a Plan':
+ return
+ print('plan selected: {}'.format(evt.new))
+ manifest = get_manifest(evt.new)
+ self.composition = Composition(manifest=manifest, add_default_group=True)
+ print('new composition: ', self.composition)
+ self.ready = True
+
+
+class ConfigureComposition(param.Parameterized):
+ composition = param.Parameter()
+
+ @param.depends('composition')
+ def panel(self):
+ if self.composition is None:
+ return pn.Pane("no composition :(")
+ print('composition: ', self.composition)
+ return self.composition.panel()
+
+
+class WorkflowPipeline(object):
+ def __init__(self):
+ stages = [
+ (STAGE_WELCOME, Welcome(), dict(ready_parameter='ready')),
+ (STAGE_CONFIG_COMPOSITION, ConfigureComposition()),
+ (STAGE_RUN_TEST, TestRunner()),
+ ]
+
+ self.pipeline = pn.pipeline.Pipeline(debug=True, stages=stages)
+
+ def panel(self):
+ return pn.Column(
+ pn.Row(
+ self.pipeline.title,
+ self.pipeline.network,
+ self.pipeline.prev_button,
+ self.pipeline.next_button,
+ ),
+ self.pipeline.stage,
+ sizing_mode='stretch_width',
+ )
+
+
+class App(object):
+ def __init__(self):
+ self.workflow = WorkflowPipeline()
+
+ def ui(self):
+ return self.workflow.panel().servable("Testground Composer")
diff --git a/testplans/composer/app/composition.py b/testplans/composer/app/composition.py
new file mode 100644
index 000000000..f12034f8c
--- /dev/null
+++ b/testplans/composer/app/composition.py
@@ -0,0 +1,328 @@
+import param
+import panel as pn
+import toml
+from .util import get_manifest, print_err
+
+
+def value_dict(parameterized, renames=None, stringify=False):
+ d = dict()
+ if renames is None:
+ renames = dict()
+ for name, p in parameterized.param.objects().items():
+ if name == 'name':
+ continue
+ if name in renames:
+ name = renames[name]
+ val = p.__get__(parameterized, type(p))
+ if isinstance(val, param.Parameterized):
+ try:
+ val = val.to_dict()
+ except:
+ val = value_dict(val, renames=renames)
+ if stringify:
+ val = str(val)
+ d[name] = val
+ return d
+
+
+def make_group_params_class(testcase):
+ """Returns a subclass of param.Parameterized whose params are defined by the
+ 'params' dict inside of the given testcase dict"""
+ tc_params = dict()
+ for name, p in testcase.get('params', {}).items():
+ tc_params[name] = make_param(p)
+
+ name = 'Test Params for testcase {}'.format(testcase.get('name', ''))
+ cls = param.parameterized_class(name, tc_params, GroupParamsBase)
+ return cls
+
+
+def make_param(pdef):
+ """
+ :param pdef: a parameter definition dict from a testground plan manifest
+ :return: a param.Parameter that has the type, bounds, default value, etc from the definition
+ """
+ typ = pdef['type'].lower()
+ if typ == 'int':
+ return num_param(pdef, cls=param.Integer)
+ elif typ == 'float':
+ return num_param(pdef)
+ elif typ.startswith('bool'):
+ return bool_param(pdef)
+ else:
+ return str_param(pdef)
+
+
+def num_param(pdef, cls=param.Number):
+ lo = pdef.get('min', None)
+ hi = pdef.get('max', None)
+ bounds = (lo, hi)
+ if lo == hi and lo is not None:
+ bounds = None
+
+ default_val = pdef.get('default', None)
+ if default_val is not None:
+ if cls == param.Integer:
+ default_val = int(default_val)
+ else:
+ default_val = float(default_val)
+ return cls(default=default_val, bounds=bounds, doc=pdef.get('desc', ''))
+
+
+def bool_param(pdef):
+ default_val = str(pdef.get('default', 'false')).lower() == 'true'
+ return param.Boolean(
+ doc=pdef.get('desc', ''),
+ default=default_val
+ )
+
+
+def str_param(pdef):
+ return param.String(
+ default=pdef.get('default', ''),
+ doc=pdef.get('desc', ''),
+ )
+
+
+class Base(param.Parameterized):
+ @classmethod
+ def from_dict(cls, d):
+ return cls(**d)
+
+ def to_dict(self):
+ return value_dict(self)
+
+
+class GroupParamsBase(Base):
+ def to_dict(self):
+ return value_dict(self, stringify=True)
+
+
+class Metadata(Base):
+ composition_name = param.String()
+ author = param.String()
+
+ @classmethod
+ def from_dict(cls, d):
+ d['composition_name'] = d.get('name', '')
+ del d['name']
+ return Metadata(**d)
+
+ def to_dict(self):
+ return value_dict(self, {'composition_name': 'name'})
+
+
+class Global(Base):
+ plan = param.String()
+ case = param.Selector()
+ builder = param.String()
+ runner = param.String()
+
+ # TODO: link to instance counts in groups
+ total_instances = param.Integer()
+ # TODO: add ui widget for key/value maps instead of using Dict param type
+ build_config = param.Dict(default={}, allow_None=True)
+ run_config = param.Dict(default={}, allow_None=True)
+
+ def set_manifest(self, manifest):
+ if manifest is None:
+ return
+ print('manifest:', manifest)
+ self.plan = manifest['name']
+ cases = [tc['name'] for tc in manifest['testcases']]
+ self.param['case'].objects = cases
+ print('global config updated manifest. cases:', self.param['case'].objects)
+ if len(cases) != 0:
+ self.case = cases[0]
+
+ if 'defaults' in manifest:
+ print('manifest defaults', manifest['defaults'])
+ if self.builder == '':
+ self.builder = manifest['defaults'].get('builder', '')
+ if self.runner == '':
+ self.runner = manifest['defaults'].get('runner', '')
+
+
+class Resources(Base):
+ memory = param.String(allow_None=True)
+ cpu = param.String(allow_None=True)
+
+
+class Instances(Base):
+ count = param.Integer(allow_None=True)
+ percentage = param.Number(allow_None=True)
+
+
+class Dependency(Base):
+ module = param.String()
+ version = param.String()
+
+
+class Build(Base):
+ selectors = param.List(class_=str, allow_None=True)
+ dependencies = param.List(allow_None=True)
+
+
+class Run(Base):
+ artifact = param.String(allow_None=True)
+ test_params = param.Parameter(instantiate=True)
+
+ def __init__(self, params_class=None, **params):
+ super().__init__(**params)
+ if params_class is not None:
+ self.test_params = params_class()
+
+ @classmethod
+ def from_dict(cls, d, params_class=None):
+ return Run(artifact=d.get('artifact', None), params_class=params_class)
+
+ def panel(self):
+ return pn.Column(
+ self.param['artifact'],
+ pn.Param(self.test_params)
+ )
+
+
+class Group(Base):
+ id = param.String()
+ instances = param.Parameter(Instances(), instantiate=True)
+ resources = param.Parameter(Resources(), allow_None=True, instantiate=True)
+ build = param.Parameter(Build(), instantiate=True)
+ run = param.Parameter(Run(), instantiate=True)
+
+ def __init__(self, params_class=None, **params):
+ super().__init__(**params)
+ if params_class is not None:
+ self.run = Run(params_class=params_class)
+ self._set_name(self.id)
+
+ @classmethod
+ def from_dict(cls, d, params_class=None):
+ return Group(
+ id=d['id'],
+ resources=Resources.from_dict(d.get('resources', {})),
+ instances=Instances.from_dict(d.get('instances', {})),
+ build=Build.from_dict(d.get('build', {})),
+ run=Run.from_dict(d.get('params', {}), params_class=params_class),
+ )
+
+ def panel(self):
+ print('rendering groups panel for ' + self.id)
+ return pn.Column(
+ "**Group: {}**".format(self.id),
+ self.param['id'],
+ self.instances,
+ self.resources,
+ self.build,
+ self.run.panel(),
+ )
+
+
+class Composition(param.Parameterized):
+ metadata = param.Parameter(Metadata(), instantiate=True)
+ global_config = param.Parameter(Global(), instantiate=True)
+
+ groups = param.List(precedence=-1)
+ group_tabs = pn.Tabs()
+ groups_ui = None
+
+ def __init__(self, manifest=None, add_default_group=False, **params):
+ super(Composition, self).__init__(**params)
+ self.manifest = manifest
+ self.testcase_param_classes = dict()
+ self._set_manifest(manifest)
+ if add_default_group:
+ self._add_group()
+
+ @classmethod
+ def from_dict(cls, d, manifest=None):
+ if manifest is None:
+ try:
+ manifest = get_manifest(d['global']['plan'])
+ except FileNotFoundError:
+ print_err("Unable to find manifest for test plan {}. Please import into $TESTGROUND_HOME/plans and try again".format(d['global']['plan']))
+
+ c = Composition(
+ manifest=manifest,
+ metadata=Metadata.from_dict(d.get('metadata', {})),
+ global_config=Global.from_dict(d.get('global', {})),
+ )
+ params_class = c._params_class_for_current_testcase()
+ c.groups = [Group.from_dict(g, params_class=params_class) for g in d.get('groups', [])]
+
+ return c
+
+ @classmethod
+ def from_toml_file(cls, filename, manifest=None):
+ with open(filename, 'rt') as f:
+ d = toml.load(f)
+ return cls.from_dict(d, manifest=manifest)
+
+ @param.depends('groups', watch=True)
+ def panel(self):
+ add_group_button = pn.widgets.Button(name='Add Group')
+ add_group_button.on_click(self._add_group)
+
+ self._refresh_tabs()
+
+ if self.groups_ui is None:
+ self.groups_ui = pn.Column(
+ add_group_button,
+ self.group_tabs,
+ )
+
+ return pn.Row(
+ pn.Column(self.metadata, self.global_config),
+ self.groups_ui,
+ )
+
+ def _set_manifest(self, manifest):
+ if manifest is None:
+ return
+
+ g = self.global_config
+ print('global conifg: ', g)
+ g.set_manifest(manifest)
+ for tc in manifest.get('testcases', []):
+ self.testcase_param_classes[tc['name']] = make_group_params_class(tc)
+
+ def _params_class_for_current_testcase(self):
+ case = self.global_config.case
+ cls = self.testcase_param_classes.get(case, None)
+ if cls is None:
+ print_err("No testcase found in manifest named " + case)
+ return cls
+
+ def _add_group(self, *args):
+ group_id = 'group-{}'.format(len(self.groups) + 1)
+ g = Group(id=group_id, params_class=self._params_class_for_current_testcase())
+ g.param.watch(self._refresh_tabs, 'id')
+ groups = self.groups
+ groups.append(g)
+ self.groups = groups
+ self.group_tabs.active = len(groups)-1
+
+ @param.depends("global_config.case", watch=True)
+ def _test_case_changed(self):
+ print('test case changed', self.global_config.case)
+ cls = self._params_class_for_current_testcase()
+ for g in self.groups:
+ g.run.test_params = cls()
+ self._refresh_tabs()
+
+ def _refresh_tabs(self, *args):
+ self.group_tabs[:] = [(g.id, g.panel()) for g in self.groups]
+
+ def to_dict(self):
+ return {
+ 'metadata': value_dict(self.metadata, renames={'composition_name': 'name'}),
+ 'global': value_dict(self.global_config),
+ 'groups': [g.to_dict() for g in self.groups]
+ }
+
+ def to_toml(self):
+ return toml.dumps(self.to_dict())
+
+ def write_to_file(self, filename):
+ with open(filename, 'wt') as f:
+ toml.dump(self.to_dict(), f)
diff --git a/testplans/composer/app/runner.py b/testplans/composer/app/runner.py
new file mode 100644
index 000000000..6eb368795
--- /dev/null
+++ b/testplans/composer/app/runner.py
@@ -0,0 +1,111 @@
+import os
+import panel as pn
+import param
+from panel.io.server import unlocked
+from tornado.ioloop import IOLoop, PeriodicCallback
+from tornado.process import Subprocess
+from subprocess import STDOUT
+from bokeh.models.widgets import Div
+from ansi2html import Ansi2HTMLConverter
+
+from .composition import Composition
+
+TESTGROUND = 'testground'
+
+
+class AnsiColorText(pn.widgets.Widget):
+ style = param.Dict(default=None, doc="""
+ Dictionary of CSS property:value pairs to apply to this Div.""")
+
+ value = param.Parameter(default=None)
+
+ _format = '{value}
'
+
+ _rename = {'name': None, 'value': 'text'}
+
+ # _target_transforms = {'value': 'target.text.split(": ")[0]+": "+value'}
+ #
+ # _source_transforms = {'value': 'value.split(": ")[1]'}
+
+ _widget_type = Div
+
+ _converter = Ansi2HTMLConverter(inline=True)
+
+ def _process_param_change(self, msg):
+ msg = super(AnsiColorText, self)._process_property_change(msg)
+ if 'value' in msg:
+ text = str(msg.pop('value'))
+ text = self._converter.convert(text)
+ msg['text'] = text
+ return msg
+
+ def scroll_down(self):
+ # TODO: figure out how to automatically scroll down as text is added
+ pass
+
+
+class CommandRunner(param.Parameterized):
+ command_output = param.String()
+
+ def __init__(self, **params):
+ super().__init__(**params)
+ self._output_lines = []
+ self.proc = None
+ self._updater = PeriodicCallback(self._refresh_output, callback_time=1000)
+
+ @pn.depends('command_output')
+ def panel(self):
+ return pn.Param(self.param, show_name=False, sizing_mode='stretch_width', widgets={
+ 'command_output': dict(
+ type=AnsiColorText,
+ sizing_mode='stretch_width',
+ height=800)
+ })
+
+ def run(self, *cmd):
+ self.command_output = ''
+ self._output_lines = []
+ self.proc = Subprocess(cmd, stdout=Subprocess.STREAM, stderr=STDOUT)
+ self._get_next_line()
+ self._updater.start()
+
+ def _get_next_line(self):
+ if self.proc is None:
+ return
+ loop = IOLoop.current()
+ loop.add_future(self.proc.stdout.read_until(bytes('\n', encoding='utf8')), self._append_output)
+
+ def _append_output(self, future):
+ self._output_lines.append(future.result().decode('utf8'))
+ self._get_next_line()
+
+ def _refresh_output(self):
+ text = ''.join(self._output_lines)
+ if len(text) != len(self.command_output):
+ with unlocked():
+ self.command_output = text
+
+
+class TestRunner(param.Parameterized):
+ composition = param.ClassSelector(class_=Composition, precedence=-1)
+ testground_daemon_endpoint = param.String(default="{}:8042".format(os.environ.get('TESTGROUND_DAEMON_HOST', 'localhost')))
+ run_test = param.Action(lambda self: self.run())
+ runner = CommandRunner()
+
+ def __init__(self, **params):
+ super().__init__(**params)
+
+ def run(self):
+ # TODO: temp file management - maybe we should mount a volume and save there?
+ filename = '/tmp/composition.toml'
+ self.composition.write_to_file(filename)
+
+ self.runner.run(TESTGROUND, '--endpoint', self.testground_daemon_endpoint, 'run', 'composition', '-f', filename)
+
+ def panel(self):
+ return pn.Column(
+ self.param['testground_daemon_endpoint'],
+ self.param['run_test'],
+ self.runner.panel(),
+ sizing_mode='stretch_width',
+ )
diff --git a/testplans/composer/app/util.py b/testplans/composer/app/util.py
new file mode 100644
index 000000000..5321a95e8
--- /dev/null
+++ b/testplans/composer/app/util.py
@@ -0,0 +1,26 @@
+import toml
+import os
+import sys
+
+
+def parse_manifest(manifest_path):
+ with open(manifest_path, 'rt') as f:
+ return toml.load(f)
+
+
+def tg_home():
+ return os.environ.get('TESTGROUND_HOME',
+ os.path.join(os.environ['HOME'], 'testground'))
+
+
+def get_plans():
+ return list(os.listdir(os.path.join(tg_home(), 'plans')))
+
+
+def get_manifest(plan_name):
+ manifest_path = os.path.join(tg_home(), 'plans', plan_name, 'manifest.toml')
+ return parse_manifest(manifest_path)
+
+
+def print_err(*args):
+ print(*args, file=sys.stderr)
diff --git a/testplans/composer/chain-state.ipynb b/testplans/composer/chain-state.ipynb
new file mode 100644
index 000000000..bd833dd21
--- /dev/null
+++ b/testplans/composer/chain-state.ipynb
@@ -0,0 +1,174 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import json\n",
+ "import pandas as pd\n",
+ "import matplotlib.pyplot as plt\n",
+ "import hvplot.pandas\n",
+ "import panel as pn\n",
+ "\n",
+ "STATE_FILE = './chain-state.ndjson'\n",
+ "\n",
+ "MINER_STATE_COL_RENAMES = {\n",
+ " 'Info.MinerAddr': 'Miner',\n",
+ " 'Info.MinerPower.MinerPower.RawBytePower': 'Info.MinerPowerRaw',\n",
+ " 'Info.MinerPower.MinerPower.QualityAdjPower': 'Info.MinerPowerQualityAdj',\n",
+ " 'Info.MinerPower.TotalPower.RawBytePower': 'Info.TotalPowerRaw',\n",
+ " 'Info.MinerPower.TotalPower.QualityAdjPower': 'Info.TotalPowerQualityAdj',\n",
+ "}\n",
+ "\n",
+ "MINER_NUMERIC_COLS = [\n",
+ " 'Info.MinerPowerRaw',\n",
+ " 'Info.MinerPowerQualityAdj',\n",
+ " 'Info.TotalPowerRaw',\n",
+ " 'Info.TotalPowerQualityAdj',\n",
+ " 'Info.Balance',\n",
+ " 'Info.CommittedBytes',\n",
+ " 'Info.ProvingBytes',\n",
+ " 'Info.FaultyBytes',\n",
+ " 'Info.FaultyPercentage',\n",
+ " 'Info.PreCommitDeposits',\n",
+ " 'Info.LockedFunds',\n",
+ " 'Info.AvailableFunds',\n",
+ " 'Info.WorkerBalance',\n",
+ " 'Info.MarketEscrow',\n",
+ " 'Info.MarketLocked',\n",
+ "]\n",
+ "\n",
+ "DERIVED_COLS = [\n",
+ " 'CommittedSectors',\n",
+ " 'ProvingSectors',\n",
+ "]\n",
+ "\n",
+ "ATTO_FIL_COLS = [\n",
+ " 'Info.Balance',\n",
+ " 'Info.PreCommitDeposits',\n",
+ " 'Info.LockedFunds',\n",
+ " 'Info.AvailableFunds',\n",
+ " 'Info.WorkerBalance',\n",
+ " 'Info.MarketEscrow',\n",
+ " 'Info.MarketLocked',\n",
+ "]\n",
+ "\n",
+ "def atto_to_fil(x):\n",
+ " return float(x) * pow(10, -18)\n",
+ "\n",
+ "def chain_state_to_pandas(statefile):\n",
+ " chain = None\n",
+ " \n",
+ " with open(statefile, 'rt') as f:\n",
+ " for line in f.readlines():\n",
+ " j = json.loads(line)\n",
+ " chain_height = j['Height']\n",
+ " \n",
+ " miners = j['MinerStates']\n",
+ " for m in miners.values():\n",
+ " df = pd.json_normalize(m)\n",
+ " df['Height'] = chain_height\n",
+ " df.rename(columns=MINER_STATE_COL_RENAMES, inplace=True)\n",
+ " if chain is None:\n",
+ " chain = df\n",
+ " else:\n",
+ " chain = chain.append(df, ignore_index=True)\n",
+ " chain.fillna(0, inplace=True)\n",
+ " chain.set_index('Height', inplace=True)\n",
+ " \n",
+ " for c in ATTO_FIL_COLS:\n",
+ " chain[c] = chain[c].apply(atto_to_fil)\n",
+ " \n",
+ " for c in MINER_NUMERIC_COLS:\n",
+ " chain[c] = chain[c].apply(pd.to_numeric)\n",
+ " \n",
+ " # the Sectors.* fields are lists of sector ids, but we want to plot counts, so\n",
+ " # we pull the length of each list into a new column\n",
+ " chain['CommittedSectors'] = chain['Sectors.Committed'].apply(lambda x: len(x))\n",
+ " chain['ProvingSectors'] = chain['Sectors.Proving'].apply(lambda x: len(x))\n",
+ " return chain\n",
+ " \n",
+ "cs = chain_state_to_pandas(STATE_FILE)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# choose which col to plot using a widget\n",
+ "\n",
+ "cols_to_plot = MINER_NUMERIC_COLS + DERIVED_COLS\n",
+ "\n",
+ "col_selector = pn.widgets.Select(name='Field', options=cols_to_plot)\n",
+ "cols = ['Miner'] + cols_to_plot\n",
+ "plot = cs[cols].hvplot(by='Miner', y=col_selector)\n",
+ "pn.Column(pn.WidgetBox(col_selector), plot)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [],
+ "source": [
+ "# plot all line charts in a vertical stack\n",
+ "\n",
+ "plots = []\n",
+ "for c in cols_to_plot:\n",
+ " title = c.split('.')[-1]\n",
+ " p = cs[['Miner', c]].hvplot(by='Miner', y=c, title=title)\n",
+ " plots.append(p)\n",
+ "pn.Column(*plots)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# miner power area chart\n",
+ "\n",
+ "mp = cs[['Miner', 'Info.MinerPowerRaw']].rename(columns={'Info.MinerPowerRaw': 'Power'})\n",
+ "mp = mp.pivot_table(values=['Power'], index=cs.index, columns='Miner', aggfunc='sum')\n",
+ "mp = mp.div(mp.sum(1), axis=0)\n",
+ "mp.columns = mp.columns.get_level_values(1)\n",
+ "mp.hvplot.area(title='Miner Power Distribution')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.8.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/testplans/composer/composer.ipynb b/testplans/composer/composer.ipynb
new file mode 100644
index 000000000..148d1e861
--- /dev/null
+++ b/testplans/composer/composer.ipynb
@@ -0,0 +1,45 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [],
+ "source": [
+ "import param\n",
+ "import panel as pn\n",
+ "import app.app as app\n",
+ "import importlib\n",
+ "importlib.reload(app)\n",
+ "\n",
+ "pn.extension()\n",
+ "\n",
+ "a = app.App()\n",
+ "a.ui()"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.8.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/testplans/composer/composer.sh b/testplans/composer/composer.sh
new file mode 100755
index 000000000..0d8bc8eb9
--- /dev/null
+++ b/testplans/composer/composer.sh
@@ -0,0 +1,134 @@
+#!/bin/bash
+
+# this script runs jupyter inside a docker container and copies
+# plan manifests from the user's local filesystem into a temporary
+# directory that's bind-mounted into the container.
+
+set -o errexit
+set -o pipefail
+
+set -e
+
+err_report() {
+ echo "Error on line $1"
+}
+
+trap 'err_report $LINENO' ERR
+
+
+image_name="iptestground/composer"
+image_tag="latest"
+image_full_name="$image_name:$image_tag"
+tg_home=${TESTGROUND_HOME:-$HOME/testground}
+container_plans_dir="/testground/plans"
+jupyter_port=${JUPYTER_PORT:-8888}
+panel_port=${PANEL_PORT:-5006}
+
+poll_interval=30
+
+exists() {
+ command -v "$1" >/dev/null 2>&1
+}
+
+require_cmds() {
+ for cmd in $@; do
+ exists $cmd || { echo "This script requires the $cmd command. Please install it and try again." >&2; exit 1; }
+ done
+}
+
+update_plans() {
+ local dest_dir=$1
+ rsync -avzh --quiet --copy-links "${tg_home}/plans/" ${dest_dir}
+}
+
+watch_plans() {
+ local plans_dest=$1
+ while true; do
+ update_plans ${plans_dest}
+ sleep $poll_interval
+ done
+}
+
+open_url() {
+ local url=$1
+ if exists cmd.exe; then
+ cmd.exe /c start ${url} >/dev/null 2>&1
+ elif exists xdg-open; then
+ xdg-open ${url} >/dev/null 2>&1 &
+ elif exists open; then
+ open ${url}
+ else
+ echo "unable to automatically open url. copy/paste this into a browser: $url"
+ fi
+}
+
+# delete temp dir and stop docker container
+cleanup () {
+ if [[ "$container_id" != "" ]]; then
+ docker stop ${container_id} >/dev/null
+ fi
+
+ if [[ -d "$temp_plans_dir" ]]; then
+ rm -rf ${temp_plans_dir}
+ fi
+}
+
+get_host_ip() {
+ # get interface of default route
+ local net_if=$(netstat -rn | awk '/^0.0.0.0/ {thif=substr($0,74,10); print thif;} /^default.*UG/ {thif=substr($0,65,10); print thif;}')
+ # use ifconfig to get addr of that interface
+ detected_host_ip=`ifconfig ${net_if} | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1'`
+
+ if [ -z "$detected_host_ip" ]
+ then
+ detected_host_ip="host.docker.internal"
+ fi
+
+ echo $detected_host_ip
+}
+
+# run cleanup on exit
+trap "{ cleanup; }" EXIT
+
+# make sure we have the commands we need
+require_cmds jq docker rsync
+
+if [[ "$SKIP_BUILD" == "" ]]; then
+ echo "Building latest docker image. Set SKIP_BUILD env var to any value to bypass."
+ require_cmds make
+ make docker
+fi
+
+# make temp dir for manifests
+temp_base="/tmp"
+if [[ "$TEMP" != "" ]]; then
+ temp_base=$TEMP
+fi
+
+temp_plans_dir="$(mktemp -d ${temp_base}/testground-composer-XXXX)"
+echo "temp plans dir: $temp_plans_dir"
+
+# copy testplans from $TESTGROUND_HOME/plans to the temp dir
+update_plans ${temp_plans_dir}
+
+# run the container in detached mode and grab the id
+container_id=$(docker run -d \
+ -e TESTGROUND_DAEMON_HOST=$(get_host_ip) \
+ --user $(id -u):$(id -g) \
+ -p ${panel_port}:5006 \
+ -v ${temp_plans_dir}:${container_plans_dir}:ro \
+ $image_full_name)
+
+echo "container $container_id started"
+# print the log output
+docker logs -f ${container_id} &
+
+# sleep for a couple seconds to let the server start up
+sleep 2
+
+# open a browser to the app url
+panel_url="http://localhost:${panel_port}"
+open_url $panel_url
+
+# poll & sync testplan changes every few seconds
+watch_plans ${temp_plans_dir}
diff --git a/testplans/composer/fixtures/all-both-k8s.toml b/testplans/composer/fixtures/all-both-k8s.toml
new file mode 100644
index 000000000..ab9e0864e
--- /dev/null
+++ b/testplans/composer/fixtures/all-both-k8s.toml
@@ -0,0 +1,214 @@
+[metadata]
+ name = "all-both"
+ author = "adin"
+
+[global]
+ plan = "dht"
+ case = "all"
+ total_instances = 1000
+ builder = "docker:go"
+ runner = "cluster:k8s"
+ [global.build_config]
+ push_registry = true
+ registry_type = "aws"
+
+[[groups]]
+ id = "balsam-undialable-provider"
+ [groups.instances]
+ count = 5
+ percentage = 0.0
+ [groups.build]
+ selectors = ["balsam"]
+ [groups.run]
+ artifact = "909427826938.dkr.ecr.us-east-1.amazonaws.com/testground-us-east-1-dht:701251a63b92"
+ [groups.run.test_params]
+ bs_strategy = "7"
+ bucket_size = "10"
+ expect_dht = "false"
+ group_order = "4"
+ latency = "100"
+ record_count = "1"
+ timeout_secs = "600"
+ undialable = "true"
+
+[[groups]]
+ id = "balsam-undialable-searcher"
+ [groups.instances]
+ count = 5
+ percentage = 0.0
+ [groups.build]
+ selectors = ["balsam"]
+ [groups.run]
+ artifact = "909427826938.dkr.ecr.us-east-1.amazonaws.com/testground-us-east-1-dht:701251a63b92"
+ [groups.run.test_params]
+ bs_strategy = "7"
+ bucket_size = "10"
+ expect_dht = "false"
+ group_order = "5"
+ latency = "100"
+ search_records = "true"
+ timeout_secs = "600"
+ undialable = "true"
+
+[[groups]]
+ id = "balsam-dialable-passive"
+ [groups.instances]
+ count = 780
+ percentage = 0.0
+ [groups.build]
+ selectors = ["balsam"]
+ [groups.run]
+ artifact = "909427826938.dkr.ecr.us-east-1.amazonaws.com/testground-us-east-1-dht:701251a63b92"
+ [groups.run.test_params]
+ bs_strategy = "7"
+ bucket_size = "10"
+ expect_dht = "false"
+ group_order = "6"
+ latency = "100"
+ timeout_secs = "600"
+ undialable = "false"
+
+[[groups]]
+ id = "balsam-dialable-provider"
+ [groups.instances]
+ count = 5
+ percentage = 0.0
+ [groups.build]
+ selectors = ["balsam"]
+ [groups.run]
+ artifact = "909427826938.dkr.ecr.us-east-1.amazonaws.com/testground-us-east-1-dht:701251a63b92"
+ [groups.run.test_params]
+ bs_strategy = "7"
+ bucket_size = "10"
+ expect_dht = "false"
+ group_order = "7"
+ latency = "100"
+ record_count = "1"
+ timeout_secs = "600"
+ undialable = "false"
+
+[[groups]]
+ id = "balsam-dialable-searcher"
+ [groups.instances]
+ count = 5
+ percentage = 0.0
+ [groups.build]
+ selectors = ["balsam"]
+ [groups.run]
+ artifact = "909427826938.dkr.ecr.us-east-1.amazonaws.com/testground-us-east-1-dht:701251a63b92"
+ [groups.run.test_params]
+ bs_strategy = "7"
+ bucket_size = "10"
+ expect_dht = "false"
+ group_order = "8"
+ latency = "100"
+ search_records = "true"
+ timeout_secs = "600"
+ undialable = "false"
+
+[[groups]]
+ id = "cypress-passive"
+ [groups.instances]
+ count = 185
+ percentage = 0.0
+ [groups.build]
+ selectors = ["cypress"]
+
+ [[groups.build.dependencies]]
+ module = "github.com/libp2p/go-libp2p-kad-dht"
+ version = "180be07b8303d536e39809bc39c58be5407fedd9"
+
+ [[groups.build.dependencies]]
+ module = "github.com/libp2p/go-libp2p-xor"
+ version = "df24f5b04bcbdc0059b27989163a6090f4f6dc7a"
+ [groups.run]
+ artifact = "909427826938.dkr.ecr.us-east-1.amazonaws.com/testground-us-east-1-dht:ca78473d669d"
+ [groups.run.test_params]
+ alpha = "6"
+ beta = "3"
+ bs_strategy = "7"
+ bucket_size = "10"
+ group_order = "1"
+ latency = "100"
+ timeout_secs = "600"
+
+[[groups]]
+ id = "cypress-provider"
+ [groups.instances]
+ count = 5
+ percentage = 0.0
+ [groups.build]
+ selectors = ["cypress"]
+
+ [[groups.build.dependencies]]
+ module = "github.com/libp2p/go-libp2p-kad-dht"
+ version = "180be07b8303d536e39809bc39c58be5407fedd9"
+
+ [[groups.build.dependencies]]
+ module = "github.com/libp2p/go-libp2p-xor"
+ version = "df24f5b04bcbdc0059b27989163a6090f4f6dc7a"
+ [groups.run]
+ artifact = "909427826938.dkr.ecr.us-east-1.amazonaws.com/testground-us-east-1-dht:ca78473d669d"
+ [groups.run.test_params]
+ alpha = "6"
+ beta = "3"
+ bs_strategy = "7"
+ bucket_size = "10"
+ group_order = "2"
+ latency = "100"
+ record_count = "1"
+ timeout_secs = "600"
+
+[[groups]]
+ id = "cypress-searcher"
+ [groups.instances]
+ count = 5
+ percentage = 0.0
+ [groups.build]
+ selectors = ["cypress"]
+
+ [[groups.build.dependencies]]
+ module = "github.com/libp2p/go-libp2p-kad-dht"
+ version = "180be07b8303d536e39809bc39c58be5407fedd9"
+
+ [[groups.build.dependencies]]
+ module = "github.com/libp2p/go-libp2p-xor"
+ version = "df24f5b04bcbdc0059b27989163a6090f4f6dc7a"
+ [groups.run]
+ artifact = "909427826938.dkr.ecr.us-east-1.amazonaws.com/testground-us-east-1-dht:ca78473d669d"
+ [groups.run.test_params]
+ alpha = "6"
+ beta = "3"
+ bs_strategy = "7"
+ bucket_size = "10"
+ group_order = "3"
+ latency = "100"
+ search_records = "true"
+ timeout_secs = "600"
+
+[[groups]]
+ id = "cypress-bs"
+ [groups.instances]
+ count = 5
+ percentage = 0.0
+ [groups.build]
+ selectors = ["cypress"]
+
+ [[groups.build.dependencies]]
+ module = "github.com/libp2p/go-libp2p-kad-dht"
+ version = "180be07b8303d536e39809bc39c58be5407fedd9"
+
+ [[groups.build.dependencies]]
+ module = "github.com/libp2p/go-libp2p-xor"
+ version = "df24f5b04bcbdc0059b27989163a6090f4f6dc7a"
+ [groups.run]
+ artifact = "909427826938.dkr.ecr.us-east-1.amazonaws.com/testground-us-east-1-dht:ca78473d669d"
+ [groups.run.test_params]
+ alpha = "6"
+ beta = "3"
+ bootstrapper = "true"
+ bs_strategy = "7"
+ bucket_size = "10"
+ group_order = "0"
+ latency = "100"
+ timeout_secs = "600"
diff --git a/testplans/composer/fixtures/ping-pong-local.toml b/testplans/composer/fixtures/ping-pong-local.toml
new file mode 100644
index 000000000..d845daafd
--- /dev/null
+++ b/testplans/composer/fixtures/ping-pong-local.toml
@@ -0,0 +1,14 @@
+[metadata]
+name = "ping-pong-local"
+author = "yusef"
+
+[global]
+plan = "network"
+case = "ping-pong"
+total_instances = 2
+builder = "docker:go"
+runner = "local:docker"
+
+[[groups]]
+id = "nodes"
+instances = { count = 2 }
\ No newline at end of file
diff --git a/testplans/composer/requirements.txt b/testplans/composer/requirements.txt
new file mode 100644
index 000000000..cfdfaa463
--- /dev/null
+++ b/testplans/composer/requirements.txt
@@ -0,0 +1,8 @@
+param
+toml
+jupyter
+panel
+holoviews
+ansi2html
+matplotlib
+hvplot
diff --git a/testplans/dashboards/baseline.json b/testplans/dashboards/baseline.json
new file mode 100644
index 000000000..0678594ac
--- /dev/null
+++ b/testplans/dashboards/baseline.json
@@ -0,0 +1,2106 @@
+{
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": "-- Grafana --",
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "type": "dashboard"
+ }
+ ]
+ },
+ "editable": true,
+ "gnetId": null,
+ "graphTooltip": 0,
+ "id": 15,
+ "iteration": 1595335476624,
+ "links": [],
+ "panels": [
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": null,
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 0
+ },
+ "hiddenSeries": false,
+ "id": 21,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "$tag_instance",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "instance"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "previous"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "message/received",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "count"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "last"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "message/received",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": null,
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 0
+ },
+ "hiddenSeries": false,
+ "id": 22,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "$tag_instance",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "instance"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "previous"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "message/success",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "count"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "last"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "message/success",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": null,
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 8
+ },
+ "hiddenSeries": false,
+ "id": 18,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": true,
+ "max": false,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "total": false,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "$tag_instance",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "instance"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "none"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "chain/node_height",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "last"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "last"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "chain/node_height",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": null,
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 8
+ },
+ "hiddenSeries": false,
+ "id": 19,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": true,
+ "max": false,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "total": false,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "$tag_instance",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "instance"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "none"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "peer/count",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "last"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "last"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "peer/count",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "collapsed": false,
+ "datasource": null,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 16
+ },
+ "id": 10,
+ "panels": [],
+ "title": "Blocks",
+ "type": "row"
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 17
+ },
+ "hiddenSeries": false,
+ "id": 15,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": true,
+ "max": false,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "sort": "total",
+ "sortDesc": false,
+ "total": false,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "$tag_instance",
+ "groupBy": [
+ {
+ "params": [
+ "$myinterval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "instance"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "block/received",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "count"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "last"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "block/received",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 17
+ },
+ "hiddenSeries": false,
+ "id": 16,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": true,
+ "max": false,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "sort": "total",
+ "sortDesc": false,
+ "total": false,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "$tag_instance",
+ "groupBy": [
+ {
+ "params": [
+ "$myinterval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "instance"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "block/success",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "count"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "last"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "block/success",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 25
+ },
+ "hiddenSeries": false,
+ "id": 13,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "sort": "total",
+ "sortDesc": false,
+ "total": true,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "$tag_miner ($tag_run)",
+ "groupBy": [
+ {
+ "params": [
+ "$myinterval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "run"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "miner"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "0"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "diagnostics.block.mine.counter",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "count"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "sum"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "run",
+ "operator": "=~",
+ "value": "/^$runid$/"
+ }
+ ]
+ },
+ {
+ "alias": "all ($tag_run)",
+ "groupBy": [
+ {
+ "params": [
+ "$myinterval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "run"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "0"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "diagnostics.block.mine.counter",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "B",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "count"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "sum"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "run",
+ "operator": "=~",
+ "value": "/^$runid$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "mined blocks from testplan",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "collapsed": false,
+ "datasource": null,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 33
+ },
+ "id": 8,
+ "panels": [],
+ "title": "Data",
+ "type": "row"
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 9,
+ "w": 12,
+ "x": 0,
+ "y": 34
+ },
+ "hiddenSeries": false,
+ "id": 2,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": false,
+ "max": true,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "total": false,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "find-data - 95% max",
+ "groupBy": [
+ {
+ "params": [
+ "$myinterval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "run"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "0"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "diagnostics.find-data.histogram",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "p95"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "max"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "run",
+ "operator": "=~",
+ "value": "/^$runid$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "find data",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "ns",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 9,
+ "w": 12,
+ "x": 12,
+ "y": 34
+ },
+ "hiddenSeries": false,
+ "id": 14,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "sort": "total",
+ "sortDesc": true,
+ "total": true,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "send-data-to - $tag_miner ($tag_run)",
+ "groupBy": [
+ {
+ "params": [
+ "$myinterval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "miner"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "run"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "0"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "diagnostics.send-data-to.counter",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "count"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "sum"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "run",
+ "operator": "=~",
+ "value": "/^$runid$/"
+ }
+ ]
+ },
+ {
+ "alias": "find-data.offer - $tag_miner ($tag_run)",
+ "groupBy": [
+ {
+ "params": [
+ "$myinterval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "miner"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "run"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "0"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "diagnostics.find-data.offer.counter",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "D",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "count"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "sum"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "run",
+ "operator": "=~",
+ "value": "/^$runid$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "send data to ; got retrieve offers from",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "collapsed": false,
+ "datasource": null,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 43
+ },
+ "id": 6,
+ "panels": [],
+ "title": "Deals",
+ "type": "row"
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 9,
+ "w": 12,
+ "x": 0,
+ "y": 44
+ },
+ "hiddenSeries": false,
+ "id": 3,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": false,
+ "max": true,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "total": false,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "deal.sealed - 95% max",
+ "groupBy": [
+ {
+ "params": [
+ "$myinterval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "run"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "0"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "diagnostics.deal.sealed.histogram",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "p95"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "max"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "run",
+ "operator": "=~",
+ "value": "/^$runid$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "started -> sealed",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "ns",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 9,
+ "w": 12,
+ "x": 12,
+ "y": 44
+ },
+ "hiddenSeries": false,
+ "id": 4,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": false,
+ "max": true,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "total": false,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "deal.retrieved - 95% max ($tag_run)",
+ "groupBy": [
+ {
+ "params": [
+ "$myinterval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "run"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "run"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "0"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "diagnostics.deal.retrieved.histogram",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "p95"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "max"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "run",
+ "operator": "=~",
+ "value": "/^$runid$/"
+ }
+ ]
+ },
+ {
+ "alias": "deal.retrieved - min ($tag_run)",
+ "groupBy": [
+ {
+ "params": [
+ "$myinterval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "run"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "run"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "0"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "diagnostics.deal.retrieved.histogram",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "B",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "min"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "min"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "run",
+ "operator": "=~",
+ "value": "/^$runid$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "started -> retrieved",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "ns",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 9,
+ "w": 12,
+ "x": 0,
+ "y": 53
+ },
+ "hiddenSeries": false,
+ "id": 23,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "total": true,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "deal.sealed - count",
+ "groupBy": [
+ {
+ "params": [
+ "$myinterval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "run"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "0"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "diagnostics.deal.sealed.histogram",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "count"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "sum"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "run",
+ "operator": "=~",
+ "value": "/^$runid$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "started -> sealed",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "none",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 9,
+ "w": 12,
+ "x": 12,
+ "y": 53
+ },
+ "hiddenSeries": false,
+ "id": 24,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "total": true,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "deal.retrieved - count",
+ "groupBy": [
+ {
+ "params": [
+ "$myinterval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "run"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "0"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "diagnostics.deal.retrieved.histogram",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "count"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "sum"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "run",
+ "operator": "=~",
+ "value": "/^$runid$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "started -> retrieved",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "none",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ }
+ ],
+ "refresh": "5s",
+ "schemaVersion": 25,
+ "style": "dark",
+ "tags": [],
+ "templating": {
+ "list": [
+ {
+ "auto": false,
+ "auto_count": 30,
+ "auto_min": "10s",
+ "current": {
+ "selected": false,
+ "text": "10s",
+ "value": "10s"
+ },
+ "hide": 0,
+ "label": null,
+ "name": "myinterval",
+ "options": [
+ {
+ "selected": true,
+ "text": "10s",
+ "value": "10s"
+ },
+ {
+ "selected": false,
+ "text": "100s",
+ "value": "100s"
+ }
+ ],
+ "query": "10s,100s",
+ "queryValue": "",
+ "refresh": 2,
+ "skipUrlSync": false,
+ "type": "interval"
+ },
+ {
+ "allValue": null,
+ "current": {
+ "selected": false,
+ "text": "All",
+ "value": "$__all"
+ },
+ "datasource": "influxdb",
+ "definition": "SHOW TAG VALUES WITH KEY = run",
+ "hide": 0,
+ "includeAll": true,
+ "label": null,
+ "multi": false,
+ "name": "runid",
+ "options": [],
+ "query": "SHOW TAG VALUES WITH KEY = run",
+ "refresh": 1,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 1,
+ "tagValuesQuery": "",
+ "tags": [],
+ "tagsQuery": "",
+ "type": "query",
+ "useTags": false
+ }
+ ]
+ },
+ "time": {
+ "from": "now-15m",
+ "to": "now"
+ },
+ "timepicker": {
+ "refresh_intervals": [
+ "10s",
+ "30s",
+ "1m",
+ "5m",
+ "15m",
+ "30m",
+ "1h",
+ "2h",
+ "1d"
+ ]
+ },
+ "timezone": "",
+ "title": "Project Oni - Baseline test",
+ "uid": "8em8RXWMz",
+ "version": 1
+}
diff --git a/testplans/dashboards/chain.json b/testplans/dashboards/chain.json
new file mode 100644
index 000000000..c708c61cc
--- /dev/null
+++ b/testplans/dashboards/chain.json
@@ -0,0 +1,2748 @@
+{
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": "-- Grafana --",
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "type": "dashboard"
+ }
+ ]
+ },
+ "editable": true,
+ "gnetId": null,
+ "graphTooltip": 0,
+ "id": 15,
+ "links": [],
+ "panels": [
+ {
+ "aliasColors": {},
+ "bars": true,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "influxdb",
+ "decimals": 2,
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 3,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 9,
+ "w": 24,
+ "x": 0,
+ "y": 0
+ },
+ "hiddenSeries": false,
+ "hideTimeOverride": false,
+ "id": 38,
+ "interval": "",
+ "legend": {
+ "alignAsTable": true,
+ "avg": true,
+ "current": false,
+ "max": false,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "sort": "avg",
+ "sortDesc": true,
+ "total": false,
+ "values": true
+ },
+ "lines": false,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": true,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "$tag_miner",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "miner"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "chain.election",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT count(\"value\") FROM \"chain.election\" WHERE $timeFilter -10m GROUP BY time($__interval), \"miner\" fill(null)",
+ "rawQuery": true,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "count"
+ },
+ {
+ "params": [
+ "20"
+ ],
+ "type": "moving_average"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Blocks Won",
+ "tooltip": {
+ "shared": true,
+ "sort": 2,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "decimals": 2,
+ "format": "none",
+ "label": "",
+ "logBase": 1,
+ "max": null,
+ "min": "0",
+ "show": true
+ },
+ {
+ "decimals": null,
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "cacheTimeout": null,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 4,
+ "w": 8,
+ "x": 0,
+ "y": 9
+ },
+ "hiddenSeries": false,
+ "id": 22,
+ "interval": "",
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": false,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "links": [],
+ "nullPointMode": "null",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [
+ {
+ "alias": "/.*/",
+ "color": "rgb(31, 120, 193)"
+ }
+ ],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "groupBy": [],
+ "measurement": "chain.power",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": "4h",
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Total Power",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "bytes",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "cacheTimeout": null,
+ "colorBackground": false,
+ "colorValue": false,
+ "colors": [
+ "#299c46",
+ "rgba(237, 129, 40, 0.89)",
+ "#d44a3a"
+ ],
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "format": "s",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 4,
+ "w": 4,
+ "x": 8,
+ "y": 9
+ },
+ "id": 12,
+ "interval": null,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "nullText": null,
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": true,
+ "ymax": null,
+ "ymin": 0
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "groupBy": [],
+ "measurement": "chain.blocktime",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT difference(mean(\"value\")) FROM \"chain.blocktime\" WHERE $timeFilter GROUP BY time($__interval) fill(null)",
+ "rawQuery": false,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "difference"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": "",
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Avg Blocktime",
+ "type": "singlestat",
+ "valueFontSize": "100%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "avg"
+ },
+ {
+ "cacheTimeout": null,
+ "colorBackground": false,
+ "colorValue": false,
+ "colors": [
+ "#299c46",
+ "rgba(237, 129, 40, 0.89)",
+ "#d44a3a"
+ ],
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "format": "bytes",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 4,
+ "w": 4,
+ "x": 12,
+ "y": 9
+ },
+ "id": 42,
+ "interval": "",
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "nullText": null,
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": true,
+ "ymax": null,
+ "ymin": 0
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT sum(\"value\") FROM \"chain.miner_power\" WHERE $timeFilter GROUP BY time(2s)",
+ "rawQuery": true,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": "",
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Network Storage",
+ "type": "singlestat",
+ "valueFontSize": "80%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "current"
+ },
+ {
+ "cacheTimeout": null,
+ "colorBackground": false,
+ "colorValue": false,
+ "colors": [
+ "#299c46",
+ "rgba(237, 129, 40, 0.89)",
+ "#d44a3a"
+ ],
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "format": "none",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 4,
+ "w": 8,
+ "x": 16,
+ "y": 9
+ },
+ "id": 6,
+ "interval": null,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "nullText": null,
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": true,
+ "ymax": null,
+ "ymin": 0
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "groupBy": [
+ {
+ "params": [
+ "$interval"
+ ],
+ "type": "time"
+ }
+ ],
+ "measurement": "chain.election",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "sum"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": "",
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Blocks In Tipset",
+ "type": "singlestat",
+ "valueFontSize": "80%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "avg"
+ },
+ {
+ "cacheTimeout": null,
+ "colorBackground": false,
+ "colorPostfix": false,
+ "colorValue": false,
+ "colors": [
+ "#299c46",
+ "rgba(237, 129, 40, 0.89)",
+ "#d44a3a"
+ ],
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "format": "none",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 4,
+ "x": 0,
+ "y": 13
+ },
+ "id": 4,
+ "interval": null,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "nullText": null,
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": true,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": true,
+ "ymax": null,
+ "ymin": null
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "groupBy": [],
+ "measurement": "chain.height",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": "",
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Block Height",
+ "type": "singlestat",
+ "valueFontSize": "80%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "current"
+ },
+ {
+ "cacheTimeout": null,
+ "colorBackground": false,
+ "colorValue": false,
+ "colors": [
+ "#299c46",
+ "rgba(237, 129, 40, 0.89)",
+ "#d44a3a"
+ ],
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "format": "s",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 4,
+ "x": 4,
+ "y": 13
+ },
+ "id": 14,
+ "interval": null,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "nullText": null,
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": true,
+ "ymax": null,
+ "ymin": 0
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "groupBy": [],
+ "measurement": "chain.blocktime",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "difference"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": "30,90",
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Last Blocktime",
+ "type": "singlestat",
+ "valueFontSize": "80%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "current"
+ },
+ {
+ "cacheTimeout": null,
+ "colorBackground": false,
+ "colorValue": false,
+ "colors": [
+ "#299c46",
+ "rgba(237, 129, 40, 0.89)",
+ "#d44a3a"
+ ],
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "format": "none",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 4,
+ "x": 8,
+ "y": 13
+ },
+ "id": 32,
+ "interval": null,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "nullText": null,
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": true,
+ "ymax": null,
+ "ymin": null
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "chain.message_gasprice",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": "",
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Avg Gas Price",
+ "type": "singlestat",
+ "valueFontSize": "80%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "avg"
+ },
+ {
+ "cacheTimeout": null,
+ "colorBackground": false,
+ "colorValue": false,
+ "colors": [
+ "#299c46",
+ "rgba(237, 129, 40, 0.89)",
+ "#d44a3a"
+ ],
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "format": "decbytes",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 4,
+ "x": 12,
+ "y": 13
+ },
+ "id": 20,
+ "interval": null,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "nullText": null,
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": true,
+ "ymax": null,
+ "ymin": null
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "chain.message_size",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": "",
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Avg Message Size",
+ "type": "singlestat",
+ "valueFontSize": "80%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "avg"
+ },
+ {
+ "cacheTimeout": null,
+ "colorBackground": false,
+ "colorValue": false,
+ "colors": [
+ "#299c46",
+ "rgba(237, 129, 40, 0.89)",
+ "#d44a3a"
+ ],
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "format": "bytes",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 4,
+ "x": 16,
+ "y": 13
+ },
+ "id": 8,
+ "interval": null,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "nullText": null,
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": true,
+ "ymax": null,
+ "ymin": 0
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "chain.blockheader_size",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": "1024,2048",
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Avg Blockheader Size",
+ "type": "singlestat",
+ "valueFontSize": "80%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "avg"
+ },
+ {
+ "cacheTimeout": null,
+ "colorBackground": false,
+ "colorValue": false,
+ "colors": [
+ "#299c46",
+ "rgba(237, 129, 40, 0.89)",
+ "#d44a3a"
+ ],
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "format": "none",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 4,
+ "x": 20,
+ "y": 13
+ },
+ "id": 10,
+ "interval": null,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "nullText": null,
+ "pluginVersion": "6.4.2",
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": true,
+ "ymax": null,
+ "ymin": 0
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "chain.message_count",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT \"value\" FROM \"chain.message_count\" WHERE $timeFilter ",
+ "rawQuery": false,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "sum"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": "",
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Avg Messages in Tipset",
+ "type": "singlestat",
+ "valueFontSize": "80%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "avg"
+ },
+ {
+ "cacheTimeout": null,
+ "colorBackground": false,
+ "colorValue": false,
+ "colors": [
+ "#299c46",
+ "rgba(237, 129, 40, 0.89)",
+ "#d44a3a"
+ ],
+ "datasource": "influxdb",
+ "decimals": 0,
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "format": "dateTimeFromNow",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 4,
+ "x": 0,
+ "y": 16
+ },
+ "id": 16,
+ "interval": "",
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "nullText": null,
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": false,
+ "ymax": null,
+ "ymin": null
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "groupBy": [],
+ "measurement": "chain.blocktime",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [
+ "*1000"
+ ],
+ "type": "math"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": "",
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Head Updated",
+ "type": "singlestat",
+ "valueFontSize": "50%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "current"
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "cacheTimeout": null,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 3,
+ "w": 16,
+ "x": 4,
+ "y": 16
+ },
+ "hiddenSeries": false,
+ "id": 2,
+ "legend": {
+ "alignAsTable": true,
+ "avg": true,
+ "current": true,
+ "hideEmpty": false,
+ "hideZero": false,
+ "max": true,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "total": false,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 1,
+ "links": [],
+ "nullPointMode": "null",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [
+ {
+ "alias": "Null Blocks",
+ "yaxis": 2
+ },
+ {
+ "alias": "Block Time",
+ "color": "rgb(31, 120, 193)"
+ }
+ ],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "Block Time",
+ "groupBy": [],
+ "measurement": "chain.blocktime",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT difference(\"value\") FROM \"chain.blocktime\" WHERE $timeFilter",
+ "rawQuery": true,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "difference"
+ }
+ ]
+ ],
+ "tags": []
+ },
+ {
+ "alias": "Null Blocks",
+ "groupBy": [],
+ "measurement": "chain.height",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "B",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "difference"
+ },
+ {
+ "params": [
+ "-1"
+ ],
+ "type": "math"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Tipsets",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "s",
+ "label": "Time between tipsets",
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "decimals": 0,
+ "format": "short",
+ "label": "Number of Null blocks",
+ "logBase": 1,
+ "max": null,
+ "min": "0",
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "cacheTimeout": null,
+ "colorBackground": false,
+ "colorValue": false,
+ "colors": [
+ "#299c46",
+ "rgba(237, 129, 40, 0.89)",
+ "#d44a3a"
+ ],
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "format": "none",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 4,
+ "x": 20,
+ "y": 16
+ },
+ "id": 30,
+ "interval": null,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "nullText": null,
+ "postfix": "FIL",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": true,
+ "ymax": null,
+ "ymin": null
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "groupBy": [],
+ "measurement": "chain.pledge_collateral",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": "",
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Pledge Collateral",
+ "type": "singlestat",
+ "valueFontSize": "80%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "current"
+ },
+ {
+ "columns": [],
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fontSize": "100%",
+ "gridPos": {
+ "h": 21,
+ "w": 4,
+ "x": 0,
+ "y": 19
+ },
+ "id": 28,
+ "pageSize": null,
+ "showHeader": true,
+ "sort": {
+ "col": 1,
+ "desc": true
+ },
+ "styles": [
+ {
+ "alias": "Time",
+ "align": "auto",
+ "dateFormat": "YYYY-MM-DD HH:mm:ss",
+ "pattern": "Time",
+ "type": "hidden"
+ },
+ {
+ "alias": "",
+ "align": "auto",
+ "colorMode": null,
+ "colors": [
+ "rgba(245, 54, 54, 0.9)",
+ "rgba(237, 129, 40, 0.89)",
+ "rgba(50, 172, 45, 0.97)"
+ ],
+ "dateFormat": "YYYY-MM-DD HH:mm:ss",
+ "decimals": 2,
+ "mappingType": 1,
+ "pattern": "power",
+ "thresholds": [],
+ "type": "number",
+ "unit": "bytes"
+ },
+ {
+ "alias": "",
+ "align": "auto",
+ "colorMode": null,
+ "colors": [
+ "rgba(245, 54, 54, 0.9)",
+ "rgba(237, 129, 40, 0.89)",
+ "rgba(50, 172, 45, 0.97)"
+ ],
+ "decimals": 2,
+ "pattern": "/.*/",
+ "thresholds": [],
+ "type": "number",
+ "unit": "short"
+ }
+ ],
+ "targets": [
+ {
+ "groupBy": [],
+ "measurement": "chain.miner_power",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT top(\"value\", \"miner\", 20) as \"power\" FROM \"chain.miner_power\" WHERE $timeFilter",
+ "rawQuery": true,
+ "refId": "A",
+ "resultFormat": "table",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Top Power Table",
+ "transform": "table",
+ "type": "table-old"
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "cacheTimeout": null,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 5,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 4,
+ "y": 19
+ },
+ "hiddenSeries": false,
+ "id": 40,
+ "interval": "",
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": true,
+ "hideEmpty": true,
+ "hideZero": true,
+ "max": false,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "sort": "current",
+ "sortDesc": true,
+ "total": false,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 1,
+ "links": [],
+ "nullPointMode": "null",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": true,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": true,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "$tag_miner",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "miner"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "previous"
+ ],
+ "type": "fill"
+ }
+ ],
+ "limit": "",
+ "measurement": "chain.miner_power",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT mean(\"value\") FROM \"chain.miner_power\" WHERE $timeFilter GROUP BY time($__interval), \"miner\" fill(previous)",
+ "rawQuery": true,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [
+ "\"miner\",20"
+ ],
+ "type": "top"
+ }
+ ]
+ ],
+ "slimit": "",
+ "tags": []
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Top Miner Power",
+ "tooltip": {
+ "shared": true,
+ "sort": 2,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "decimals": 2,
+ "format": "bytes",
+ "label": "Power",
+ "logBase": 1,
+ "max": "100",
+ "min": "0",
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "columns": [],
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fontSize": "100%",
+ "gridPos": {
+ "h": 21,
+ "w": 8,
+ "x": 16,
+ "y": 19
+ },
+ "id": 18,
+ "pageSize": null,
+ "showHeader": true,
+ "sort": {
+ "col": 0,
+ "desc": true
+ },
+ "styles": [
+ {
+ "alias": "Height",
+ "align": "auto",
+ "dateFormat": "YYYY-MM-DD HH:mm:ss",
+ "link": false,
+ "mappingType": 1,
+ "pattern": "chain.height",
+ "preserveFormat": false,
+ "sanitize": false,
+ "type": "string"
+ },
+ {
+ "alias": "Tipset",
+ "align": "auto",
+ "colorMode": null,
+ "colors": [
+ "rgba(245, 54, 54, 0.9)",
+ "rgba(237, 129, 40, 0.89)",
+ "rgba(50, 172, 45, 0.97)"
+ ],
+ "decimals": 2,
+ "pattern": "chain.height.tipset",
+ "preserveFormat": false,
+ "sanitize": false,
+ "thresholds": [],
+ "type": "string",
+ "unit": "short"
+ },
+ {
+ "alias": "",
+ "align": "auto",
+ "colorMode": null,
+ "colors": [
+ "rgba(245, 54, 54, 0.9)",
+ "rgba(237, 129, 40, 0.89)",
+ "rgba(50, 172, 45, 0.97)"
+ ],
+ "dateFormat": "YYYY-MM-DD HH:mm:ss",
+ "decimals": 2,
+ "link": false,
+ "mappingType": 1,
+ "pattern": "Time",
+ "thresholds": [],
+ "type": "hidden",
+ "unit": "short"
+ }
+ ],
+ "targets": [
+ {
+ "groupBy": [],
+ "measurement": "chain.height",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT \"value\", \"tipset\" FROM \"chain.height\" WHERE $timeFilter",
+ "rawQuery": true,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Chain Table",
+ "transform": "timeseries_to_columns",
+ "type": "table-old"
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 4,
+ "y": 27
+ },
+ "hiddenSeries": false,
+ "id": 46,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": true,
+ "hideEmpty": true,
+ "max": false,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "sort": "current",
+ "sortDesc": true,
+ "total": false,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "$tag_miner",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT top(\"value\", \"miner\",20) FROM \"chain.miner_power\" WHERE $timeFilter GROUP BY time($__interval), \"miner\"",
+ "rawQuery": true,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Chain / Miner Power",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "bytes",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "cacheTimeout": null,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 6,
+ "w": 12,
+ "x": 4,
+ "y": 35
+ },
+ "hiddenSeries": false,
+ "id": 24,
+ "legend": {
+ "alignAsTable": false,
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "rightSide": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "links": [],
+ "nullPointMode": "null",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [
+ {
+ "alias": "/.*/",
+ "color": "rgb(31, 120, 193)"
+ }
+ ],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "previous"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "chain.pledge_collateral",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Pledge Collateral",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": "FIL",
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "cacheTimeout": null,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 7,
+ "w": 12,
+ "x": 4,
+ "y": 41
+ },
+ "hiddenSeries": false,
+ "id": 44,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "links": [],
+ "nullPointMode": "null",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "previous"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "chain.miner_power",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT count(\"value\") FROM \"chain.miner_power\" WHERE $timeFilter GROUP BY time($__interval)",
+ "rawQuery": false,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "count"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Miners on Chain",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "decimals": 0,
+ "format": "none",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": true,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 9,
+ "w": 12,
+ "x": 0,
+ "y": 48
+ },
+ "hiddenSeries": false,
+ "id": 34,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": false,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": true,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "Adr $tag_actor | Md $tag_method",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "actor"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "method"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "chain.message_count",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "sum"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Actor Messages Method",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": "",
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": true,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "influxdb",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 9,
+ "w": 12,
+ "x": 12,
+ "y": 48
+ },
+ "hiddenSeries": false,
+ "id": 36,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": false,
+ "hideEmpty": true,
+ "hideZero": true,
+ "max": false,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "sort": "avg",
+ "sortDesc": false,
+ "total": false,
+ "values": false
+ },
+ "lines": false,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": true,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "Adr $tag_actor | Md $tag_method | Ex $tag_exitcode",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "method"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "exitcode"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "actor"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "chain.message_count",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT sum(\"value\") FROM \"chain.message_count\" WHERE $timeFilter GROUP BY time($__interval), \"method\", \"exitcode\", \"actor\" fill(null)",
+ "rawQuery": true,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "count"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Actor Messages Method With Exitcode",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": "",
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ }
+ ],
+ "refresh": "5s",
+ "schemaVersion": 25,
+ "style": "dark",
+ "tags": [],
+ "templating": {
+ "list": []
+ },
+ "time": {
+ "from": "now-30m",
+ "to": "now"
+ },
+ "timepicker": {
+ "refresh_intervals": [
+ "10s",
+ "30s",
+ "45s",
+ "1m",
+ "5m",
+ "15m",
+ "30m",
+ "1h",
+ "2h",
+ "1d"
+ ]
+ },
+ "timezone": "",
+ "title": "Chain",
+ "uid": "z6FtI92Zz",
+ "version": 3
+}
diff --git a/testplans/docker-images/Dockerfile.oni-buildbase b/testplans/docker-images/Dockerfile.oni-buildbase
new file mode 100644
index 000000000..f29eb5896
--- /dev/null
+++ b/testplans/docker-images/Dockerfile.oni-buildbase
@@ -0,0 +1,24 @@
+ARG GO_VERSION=1.14.2
+
+FROM golang:${GO_VERSION}-buster
+
+RUN apt-get update && apt-get install -y ca-certificates llvm clang mesa-opencl-icd ocl-icd-opencl-dev jq gcc git pkg-config bzr libhwloc-dev
+
+ARG FILECOIN_FFI_COMMIT=1985275547f222e8c97a8ab70b5cc26bc1fa50b1
+ARG FFI_DIR=/extern/filecoin-ffi
+
+ARG BLST_COMMIT=1cbb16ed9580dcd3e9593b71221fcf2a048faaef
+ARG BLST_DIR=/extern/blst
+
+RUN mkdir -p ${FFI_DIR} \
+ && git clone https://github.com/filecoin-project/filecoin-ffi.git ${FFI_DIR} \
+ && cd ${FFI_DIR} \
+ && git checkout ${FILECOIN_FFI_COMMIT} \
+ && make
+
+RUN mkdir -p ${BLST_DIR} \
+ && git clone https://github.com/supranational/blst.git ${BLST_DIR} \
+ && cd ${BLST_DIR} \
+ && git checkout ${BLST_COMMIT}
+
+RUN ldconfig
diff --git a/testplans/docker-images/Dockerfile.oni-runtime b/testplans/docker-images/Dockerfile.oni-runtime
new file mode 100644
index 000000000..9da469e0c
--- /dev/null
+++ b/testplans/docker-images/Dockerfile.oni-runtime
@@ -0,0 +1,23 @@
+ARG GO_VERSION=1.14.2
+
+FROM golang:${GO_VERSION}-buster as downloader
+
+## Fetch the proof parameters.
+## 1. Install the paramfetch binary first, so it can be cached over builds.
+## 2. Then copy over the parameters (which could change).
+## 3. Trigger the download.
+## Output will be in /var/tmp/filecoin-proof-parameters.
+
+RUN go get github.com/filecoin-project/go-paramfetch/paramfetch
+COPY /proof-parameters.json /
+RUN paramfetch 2048 /proof-parameters.json
+
+FROM ubuntu:18.04
+
+RUN apt-get update && apt-get install -y ca-certificates llvm clang mesa-opencl-icd ocl-icd-opencl-dev gcc pkg-config libhwloc-dev
+
+RUN apt-get install -y jq net-tools netcat traceroute iputils-ping wget vim curl telnet iproute2 dnsutils
+
+COPY --from=downloader /var/tmp/filecoin-proof-parameters /var/tmp/filecoin-proof-parameters
+
+RUN ldconfig
diff --git a/testplans/docker-images/Dockerfile.oni-runtime-debug b/testplans/docker-images/Dockerfile.oni-runtime-debug
new file mode 100644
index 000000000..68629ed7e
--- /dev/null
+++ b/testplans/docker-images/Dockerfile.oni-runtime-debug
@@ -0,0 +1,30 @@
+ARG GO_VERSION=1.14.2
+
+FROM golang:${GO_VERSION}-buster as downloader
+
+## Fetch the proof parameters.
+## 1. Install the paramfetch binary first, so it can be cached over builds.
+## 2. Then copy over the parameters (which could change).
+## 3. Trigger the download.
+## Output will be in /var/tmp/filecoin-proof-parameters.
+
+RUN go get github.com/filecoin-project/go-paramfetch/paramfetch
+COPY /proof-parameters.json /
+RUN paramfetch 2048 /proof-parameters.json
+
+ARG LOTUS_COMMIT=1a170e18a
+
+## for debug purposes
+RUN apt update && apt install -y mesa-opencl-icd ocl-icd-opencl-dev gcc git bzr jq pkg-config curl && git clone https://github.com/filecoin-project/lotus.git && cd lotus/ && git checkout ${LOTUS_COMMIT} && make clean && make all && make install
+
+FROM ubuntu:18.04
+
+RUN apt-get update && apt-get install -y ca-certificates llvm clang mesa-opencl-icd ocl-icd-opencl-dev jq gcc pkg-config net-tools netcat traceroute iputils-ping wget vim curl telnet iproute2 dnsutils
+COPY --from=downloader /var/tmp/filecoin-proof-parameters /var/tmp/filecoin-proof-parameters
+
+## for debug purposes
+COPY --from=downloader /usr/local/bin/lotus /usr/local/bin/lll
+COPY --from=downloader /usr/local/bin/lotus-miner /usr/local/bin/lm
+
+ENV FULLNODE_API_INFO="dummytoken:/ip4/127.0.0.1/tcp/1234/http"
+ENV MINER_API_INFO="dummytoken:/ip4/127.0.0.1/tcp/2345/http"
diff --git a/testplans/docker-images/proof-parameters.json b/testplans/docker-images/proof-parameters.json
new file mode 100644
index 000000000..1d4584454
--- /dev/null
+++ b/testplans/docker-images/proof-parameters.json
@@ -0,0 +1,152 @@
+{
+ "v28-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-0170db1f394b35d995252228ee359194b13199d259380541dc529fb0099096b0.params": {
+ "cid": "QmVxjFRyhmyQaZEtCh7nk2abc7LhFkzhnRX4rcHqCCpikR",
+ "digest": "7610b9f82bfc88405b7a832b651ce2f6",
+ "sector_size": 2048
+ },
+ "v28-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-0170db1f394b35d995252228ee359194b13199d259380541dc529fb0099096b0.vk": {
+ "cid": "QmcS5JZs8X3TdtkEBpHAdUYjdNDqcL7fWQFtQz69mpnu2X",
+ "digest": "0e0958009936b9d5e515ec97b8cb792d",
+ "sector_size": 2048
+ },
+ "v28-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-0cfb4f178bbb71cf2ecfcd42accce558b27199ab4fb59cb78f2483fe21ef36d9.params": {
+ "cid": "QmUiRx71uxfmUE8V3H9sWAsAXoM88KR4eo1ByvvcFNeTLR",
+ "digest": "1a7d4a9c8a502a497ed92a54366af33f",
+ "sector_size": 536870912
+ },
+ "v28-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-0cfb4f178bbb71cf2ecfcd42accce558b27199ab4fb59cb78f2483fe21ef36d9.vk": {
+ "cid": "QmfCeddjFpWtavzfEzZpJfzSajGNwfL4RjFXWAvA9TSnTV",
+ "digest": "4dae975de4f011f101f5a2f86d1daaba",
+ "sector_size": 536870912
+ },
+ "v28-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-3ea05428c9d11689f23529cde32fd30aabd50f7d2c93657c1d3650bca3e8ea9e.params": {
+ "cid": "QmcSTqDcFVLGGVYz1njhUZ7B6fkKtBumsLUwx4nkh22TzS",
+ "digest": "82c88066be968bb550a05e30ff6c2413",
+ "sector_size": 2048
+ },
+ "v28-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-3ea05428c9d11689f23529cde32fd30aabd50f7d2c93657c1d3650bca3e8ea9e.vk": {
+ "cid": "QmSTCXF2ipGA3f6muVo6kHc2URSx6PzZxGUqu7uykaH5KU",
+ "digest": "ffd79788d614d27919ae5bd2d94eacb6",
+ "sector_size": 2048
+ },
+ "v28-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-50c7368dea9593ed0989e70974d28024efa9d156d585b7eea1be22b2e753f331.params": {
+ "cid": "QmU9SBzJNrcjRFDiFc4GcApqdApN6z9X7MpUr66mJ2kAJP",
+ "digest": "700171ecf7334e3199437c930676af82",
+ "sector_size": 8388608
+ },
+ "v28-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-50c7368dea9593ed0989e70974d28024efa9d156d585b7eea1be22b2e753f331.vk": {
+ "cid": "QmbmUMa3TbbW3X5kFhExs6WgC4KeWT18YivaVmXDkB6ANG",
+ "digest": "79ebb55f56fda427743e35053edad8fc",
+ "sector_size": 8388608
+ },
+ "v28-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-5294475db5237a2e83c3e52fd6c2b03859a1831d45ed08c4f35dbf9a803165a9.params": {
+ "cid": "QmdNEL2RtqL52GQNuj8uz6mVj5Z34NVnbaJ1yMyh1oXtBx",
+ "digest": "c49499bb76a0762884896f9683403f55",
+ "sector_size": 8388608
+ },
+ "v28-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-5294475db5237a2e83c3e52fd6c2b03859a1831d45ed08c4f35dbf9a803165a9.vk": {
+ "cid": "QmUiVYCQUgr6Y13pZFr8acWpSM4xvTXUdcvGmxyuHbKhsc",
+ "digest": "34d4feeacd9abf788d69ef1bb4d8fd00",
+ "sector_size": 8388608
+ },
+ "v28-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-7d739b8cf60f1b0709eeebee7730e297683552e4b69cab6984ec0285663c5781.params": {
+ "cid": "QmVgCsJFRXKLuuUhT3aMYwKVGNA9rDeR6DCrs7cAe8riBT",
+ "digest": "827359440349fe8f5a016e7598993b79",
+ "sector_size": 536870912
+ },
+ "v28-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-7d739b8cf60f1b0709eeebee7730e297683552e4b69cab6984ec0285663c5781.vk": {
+ "cid": "QmfA31fbCWojSmhSGvvfxmxaYCpMoXP95zEQ9sLvBGHNaN",
+ "digest": "bd2cd62f65c1ab84f19ca27e97b7c731",
+ "sector_size": 536870912
+ },
+ "v28-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-0-0377ded656c6f524f1618760bffe4e0a1c51d5a70c4509eedae8a27555733edc.params": {
+ "cid": "QmaUmfcJt6pozn8ndq1JVBzLRjRJdHMTPd4foa8iw5sjBZ",
+ "digest": "2cf49eb26f1fee94c85781a390ddb4c8",
+ "sector_size": 34359738368
+ },
+ "v28-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-0-0377ded656c6f524f1618760bffe4e0a1c51d5a70c4509eedae8a27555733edc.vk": {
+ "cid": "QmR9i9KL3vhhAqTBGj1bPPC7LvkptxrH9RvxJxLN1vvsBE",
+ "digest": "0f8ec542485568fa3468c066e9fed82b",
+ "sector_size": 34359738368
+ },
+ "v28-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-0-559e581f022bb4e4ec6e719e563bf0e026ad6de42e56c18714a2c692b1b88d7e.params": {
+ "cid": "Qmdtczp7p4wrbDofmHdGhiixn9irAcN77mV9AEHZBaTt1i",
+ "digest": "d84f79a16fe40e9e25a36e2107bb1ba0",
+ "sector_size": 34359738368
+ },
+ "v28-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-0-559e581f022bb4e4ec6e719e563bf0e026ad6de42e56c18714a2c692b1b88d7e.vk": {
+ "cid": "QmZCvxKcKP97vDAk8Nxs9R1fWtqpjQrAhhfXPoCi1nkDoF",
+ "digest": "fc02943678dd119e69e7fab8420e8819",
+ "sector_size": 34359738368
+ },
+ "v28-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-2-2627e4006b67f99cef990c0a47d5426cb7ab0a0ad58fc1061547bf2d28b09def.params": {
+ "cid": "QmeAN4vuANhXsF8xP2Lx5j2L6yMSdogLzpcvqCJThRGK1V",
+ "digest": "3810b7780ac0e299b22ae70f1f94c9bc",
+ "sector_size": 68719476736
+ },
+ "v28-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-2-2627e4006b67f99cef990c0a47d5426cb7ab0a0ad58fc1061547bf2d28b09def.vk": {
+ "cid": "QmWV8rqZLxs1oQN9jxNWmnT1YdgLwCcscv94VARrhHf1T7",
+ "digest": "59d2bf1857adc59a4f08fcf2afaa916b",
+ "sector_size": 68719476736
+ },
+ "v28-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-2-b62098629d07946e9028127e70295ed996fe3ed25b0f9f88eb610a0ab4385a3c.params": {
+ "cid": "QmVkrXc1SLcpgcudK5J25HH93QvR9tNsVhVTYHm5UymXAz",
+ "digest": "2170a91ad5bae22ea61f2ea766630322",
+ "sector_size": 68719476736
+ },
+ "v28-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-2-b62098629d07946e9028127e70295ed996fe3ed25b0f9f88eb610a0ab4385a3c.vk": {
+ "cid": "QmbfQjPD7EpzjhWGmvWAsyN2mAZ4PcYhsf3ujuhU9CSuBm",
+ "digest": "6d3789148fb6466d07ee1e24d6292fd6",
+ "sector_size": 68719476736
+ },
+ "v28-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-032d3138d22506ec0082ed72b2dcba18df18477904e35bafee82b3793b06832f.params": {
+ "cid": "QmWceMgnWYLopMuM4AoGMvGEau7tNe5UK83XFjH5V9B17h",
+ "digest": "434fb1338ecfaf0f59256f30dde4968f",
+ "sector_size": 2048
+ },
+ "v28-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-032d3138d22506ec0082ed72b2dcba18df18477904e35bafee82b3793b06832f.vk": {
+ "cid": "QmamahpFCstMUqHi2qGtVoDnRrsXhid86qsfvoyCTKJqHr",
+ "digest": "dc1ade9929ade1708238f155343044ac",
+ "sector_size": 2048
+ },
+ "v28-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-6babf46ce344ae495d558e7770a585b2382d54f225af8ed0397b8be7c3fcd472.params": {
+ "cid": "QmYBpTt7LWNAWr1JXThV5VxX7wsQFLd1PHrGYVbrU1EZjC",
+ "digest": "6c77597eb91ab936c1cef4cf19eba1b3",
+ "sector_size": 536870912
+ },
+ "v28-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-6babf46ce344ae495d558e7770a585b2382d54f225af8ed0397b8be7c3fcd472.vk": {
+ "cid": "QmWionkqH2B6TXivzBSQeSyBxojaiAFbzhjtwYRrfwd8nH",
+ "digest": "065179da19fbe515507267677f02823e",
+ "sector_size": 536870912
+ },
+ "v28-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-ecd683648512ab1765faa2a5f14bab48f676e633467f0aa8aad4b55dcb0652bb.params": {
+ "cid": "QmPXAPPuQtuQz7Zz3MHMAMEtsYwqM1o9H1csPLeiMUQwZH",
+ "digest": "09e612e4eeb7a0eb95679a88404f960c",
+ "sector_size": 8388608
+ },
+ "v28-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-ecd683648512ab1765faa2a5f14bab48f676e633467f0aa8aad4b55dcb0652bb.vk": {
+ "cid": "QmYCuipFyvVW1GojdMrjK1JnMobXtT4zRCZs1CGxjizs99",
+ "digest": "b687beb9adbd9dabe265a7e3620813e4",
+ "sector_size": 8388608
+ },
+ "v28-stacked-proof-of-replication-merkletree-poseidon_hasher-8-8-0-sha256_hasher-82a357d2f2ca81dc61bb45f4a762807aedee1b0a53fd6c4e77b46a01bfef7820.params": {
+ "cid": "QmengpM684XLQfG8754ToonszgEg2bQeAGUan5uXTHUQzJ",
+ "digest": "6a388072a518cf46ebd661f5cc46900a",
+ "sector_size": 34359738368
+ },
+ "v28-stacked-proof-of-replication-merkletree-poseidon_hasher-8-8-0-sha256_hasher-82a357d2f2ca81dc61bb45f4a762807aedee1b0a53fd6c4e77b46a01bfef7820.vk": {
+ "cid": "Qmf93EMrADXAK6CyiSfE8xx45fkMfR3uzKEPCvZC1n2kzb",
+ "digest": "0c7b4aac1c40fdb7eb82bc355b41addf",
+ "sector_size": 34359738368
+ },
+ "v28-stacked-proof-of-replication-merkletree-poseidon_hasher-8-8-2-sha256_hasher-96f1b4a04c5c51e4759bbf224bbc2ef5a42c7100f16ec0637123f16a845ddfb2.params": {
+ "cid": "QmS7ye6Ri2MfFzCkcUJ7FQ6zxDKuJ6J6B8k5PN7wzSR9sX",
+ "digest": "1801f8a6e1b00bceb00cc27314bb5ce3",
+ "sector_size": 68719476736
+ },
+ "v28-stacked-proof-of-replication-merkletree-poseidon_hasher-8-8-2-sha256_hasher-96f1b4a04c5c51e4759bbf224bbc2ef5a42c7100f16ec0637123f16a845ddfb2.vk": {
+ "cid": "QmehSmC6BhrgRZakPDta2ewoH9nosNzdjCqQRXsNFNUkLN",
+ "digest": "a89884252c04c298d0b3c81bfd884164",
+ "sector_size": 68719476736
+ }
+}
diff --git a/testplans/graphsync/_compositions/stress-k8s.toml b/testplans/graphsync/_compositions/stress-k8s.toml
new file mode 100644
index 000000000..bfc854bcc
--- /dev/null
+++ b/testplans/graphsync/_compositions/stress-k8s.toml
@@ -0,0 +1,35 @@
+[metadata]
+ name = "stress"
+
+[global]
+ plan = "graphsync"
+ case = "stress"
+ total_instances = 2
+ builder = "docker:go"
+ runner = "cluster:k8s"
+
+[global.build_config]
+ push_registry=true
+ go_proxy_mode="remote"
+ go_proxy_url="http://localhost:8081"
+ registry_type="aws"
+
+[global.run.test_params]
+size = "10MB"
+latencies = '["50ms", "100ms", "200ms"]'
+bandwidths = '["32MiB", "16MiB", "8MiB", "4MiB", "1MiB"]'
+concurrency = "10"
+
+[[groups]]
+ id = "providers"
+ instances = { count = 1 }
+ [groups.resources]
+ memory = "4096Mi"
+ cpu = "1000m"
+
+[[groups]]
+ id = "requestors"
+ instances = { count = 1 }
+ [groups.resources]
+ memory = "4096Mi"
+ cpu = "1000m"
diff --git a/testplans/graphsync/_compositions/stress.toml b/testplans/graphsync/_compositions/stress.toml
new file mode 100644
index 000000000..4920f6ff3
--- /dev/null
+++ b/testplans/graphsync/_compositions/stress.toml
@@ -0,0 +1,23 @@
+[metadata]
+ name = "stress"
+
+[global]
+ plan = "graphsync"
+ case = "stress"
+ total_instances = 2
+ builder = "docker:go"
+ runner = "local:docker"
+
+[global.run.test_params]
+size = "10MB"
+latencies = '["50ms", "100ms", "200ms"]'
+bandwidths = '["32MiB", "16MiB", "8MiB", "4MiB", "1MiB"]'
+concurrency = "10"
+
+[[groups]]
+ id = "providers"
+ instances = { count = 1 }
+
+[[groups]]
+ id = "requestors"
+ instances = { count = 1 }
diff --git a/testplans/graphsync/_compositions/version_compat.toml b/testplans/graphsync/_compositions/version_compat.toml
new file mode 100644
index 000000000..b7e89a97f
--- /dev/null
+++ b/testplans/graphsync/_compositions/version_compat.toml
@@ -0,0 +1,34 @@
+[metadata]
+ name = "version_compat"
+
+[global]
+ plan = "graphsync"
+ case = "stress"
+ total_instances = 2
+ builder = "docker:go"
+ runner = "local:docker"
+
+[global.run.test_params]
+size = "10MB"
+latencies = '["50ms"]'
+bandwidths = '["4MiB"]'
+concurrency = "1"
+
+[[groups]]
+ id = "providers"
+ instances = { count = 1 }
+ [groups.build]
+ [[groups.build.dependencies]]
+ module = "github.com/ipfs/go-graphsync"
+ version = "v0.2.1"
+ [[groups.build.dependencies]]
+ module = "github.com/hannahhoward/all-selector"
+ version = "v0.2.0"
+
+[[groups]]
+ id = "requestors"
+ instances = { count = 1 }
+ [groups.build]
+ [[groups.build.dependencies]]
+ module = "github.com/ipfs/go-graphsync"
+ version = "v0.1.2"
\ No newline at end of file
diff --git a/testplans/graphsync/go.mod b/testplans/graphsync/go.mod
new file mode 100644
index 000000000..ffd131f83
--- /dev/null
+++ b/testplans/graphsync/go.mod
@@ -0,0 +1,33 @@
+module github.com/filecoin-project/lotus/testplans/graphsync
+
+go 1.14
+
+require (
+ github.com/dustin/go-humanize v1.0.0
+ github.com/hannahhoward/all-selector v0.1.0
+ github.com/ipfs/go-blockservice v0.1.3
+ github.com/ipfs/go-cid v0.0.6
+ github.com/ipfs/go-datastore v0.4.4
+ github.com/ipfs/go-graphsync v0.1.2
+ github.com/ipfs/go-ipfs-blockstore v0.1.4
+ github.com/ipfs/go-ipfs-chunker v0.0.5
+ github.com/ipfs/go-ipfs-exchange-offline v0.0.1
+ github.com/ipfs/go-ipfs-files v0.0.8
+ github.com/ipfs/go-ipld-format v0.2.0
+ github.com/ipfs/go-merkledag v0.3.1
+ github.com/ipfs/go-unixfs v0.2.4
+ github.com/ipld/go-ipld-prime v0.4.0
+ github.com/kr/text v0.2.0 // indirect
+ github.com/libp2p/go-libp2p v0.10.0
+ github.com/libp2p/go-libp2p-core v0.6.0
+ github.com/libp2p/go-libp2p-noise v0.1.1
+ github.com/libp2p/go-libp2p-secio v0.2.2
+ github.com/libp2p/go-libp2p-tls v0.1.3
+ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
+ github.com/testground/sdk-go v0.2.7-0.20201112151952-8ee00c80c3ec
+ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208
+ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae // indirect
+ google.golang.org/protobuf v1.25.0 // indirect
+ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
+ gopkg.in/yaml.v2 v2.2.8 // indirect
+)
diff --git a/testplans/graphsync/go.sum b/testplans/graphsync/go.sum
new file mode 100644
index 000000000..e80038c38
--- /dev/null
+++ b/testplans/graphsync/go.sum
@@ -0,0 +1,972 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
+dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
+dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
+dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
+dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
+git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
+github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
+github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9 h1:HD8gA2tkByhMAwYaFAX9w2l7vxvBQ5NMoxDrkhqhtn4=
+github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y=
+github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/Stebalien/go-bitfield v0.0.1 h1:X3kbSSPUaJK60wV2hjOPZwmpljr6VGCqdq4cBLhbQBo=
+github.com/Stebalien/go-bitfield v0.0.1/go.mod h1:GNjFpasyUVkHMsfEOk8EFLJ9syQ6SI+XWrX9Wf2XH0s=
+github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/avast/retry-go v2.6.0+incompatible h1:FelcMrm7Bxacr1/RM8+/eqkDkmVN7tjlsy51dOzB3LI=
+github.com/avast/retry-go v2.6.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
+github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8=
+github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
+github.com/btcsuite/btcd v0.0.0-20190605094302-a0d1e3e36d50/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
+github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
+github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
+github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
+github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
+github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
+github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
+github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
+github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
+github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
+github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
+github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
+github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
+github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
+github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
+github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
+github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
+github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 h1:HVTnpeuvF6Owjd5mniCL8DEXo7uYXdQEmOP4FJbV5tg=
+github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0=
+github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis=
+github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davidlazar/go-crypto v0.0.0-20170701192655-dcfb0a7ac018/go.mod h1:rQYf4tfk5sSwFsnDg3qYaBxSjsD9S8+59vW0dKUgme4=
+github.com/davidlazar/go-crypto v0.0.0-20190912175916-7055855a373f h1:BOaYiTvg8p9vBUXpklC22XSK/mifLF7lG9jtmYYi3Tc=
+github.com/davidlazar/go-crypto v0.0.0-20190912175916-7055855a373f/go.mod h1:rQYf4tfk5sSwFsnDg3qYaBxSjsD9S8+59vW0dKUgme4=
+github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ=
+github.com/dgraph-io/badger v1.6.0-rc1/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
+github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
+github.com/dgraph-io/badger v1.6.1 h1:w9pSFNSdq/JPM1N12Fz/F/bzo993Is1W+Q7HjPzi7yg=
+github.com/dgraph-io/badger v1.6.1/go.mod h1:FRmFw3uxvcpa8zG3Rxs0th+hCLIuaQg8HlNV5bjgnuU=
+github.com/dgraph-io/ristretto v0.0.2 h1:a5WaUrDa0qm0YrAAS1tUykT5El3kt62KNZZeMxQn3po=
+github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
+github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
+github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
+github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
+github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6 h1:u/UEqS66A5ckRmS4yNpjmVH56sVtS/RfclBAYocb4as=
+github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6/go.mod h1:1i71OnUq3iUe1ma7Lr6yG6/rjvM3emb6yoL7xLFzcVQ=
+github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
+github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
+github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
+github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
+github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4=
+github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
+github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.4.0 h1:Rd1kQnQu0Hq3qvJppYSG0HtP+f5LPPUiDswTLiEegLg=
+github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gopacket v1.1.17 h1:rMrlX2ZY2UbvT+sdz3+6J+pp2z+msCq9MxTU6ymxbBY=
+github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
+github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
+github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f h1:KMlcu9X58lhTA/KrfX8Bi1LQSO4pzoVjTiL3h4Jk+Zk=
+github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
+github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
+github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU=
+github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48=
+github.com/hannahhoward/all-selector v0.1.0 h1:B+hMG/8Vb0+XB3eHK2Cz6hYpSZWVZuSz401ebRvfGtk=
+github.com/hannahhoward/all-selector v0.1.0/go.mod h1:2wbwlpJCyAaTfpSYqKqqA5Xe0YPvJmyjylxKs6+PIvA=
+github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e h1:3YKHER4nmd7b5qy5t0GWDTwSn4OyRgfAXSmo6VnryBY=
+github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e/go.mod h1:I8h3MITA53gN9OnWGCgaMa0JWVRdXthWw4M3CPM54OY=
+github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
+github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
+github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo=
+github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc=
+github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/influxdata/influxdb1-client v0.0.0-20200515024757-02f0bf5dbca3 h1:k3/6a1Shi7GGCp9QpyYuXsMM6ncTOjCzOE9Fd6CDA+Q=
+github.com/influxdata/influxdb1-client v0.0.0-20200515024757-02f0bf5dbca3/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
+github.com/ipfs/bbloom v0.0.1/go.mod h1:oqo8CVWsJFMOZqTglBG4wydCE4IQA/G2/SEofB0rjUI=
+github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
+github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
+github.com/ipfs/go-bitswap v0.1.0/go.mod h1:FFJEf18E9izuCqUtHxbWEvq+reg7o4CW5wSAE1wsxj0=
+github.com/ipfs/go-bitswap v0.1.2/go.mod h1:qxSWS4NXGs7jQ6zQvoPY3+NmOfHHG47mhkiLzBpJQIs=
+github.com/ipfs/go-bitswap v0.1.8 h1:38X1mKXkiU6Nzw4TOSWD8eTVY5eX3slQunv3QEWfXKg=
+github.com/ipfs/go-bitswap v0.1.8/go.mod h1:TOWoxllhccevbWFUR2N7B1MTSVVge1s6XSMiCSA4MzM=
+github.com/ipfs/go-block-format v0.0.1/go.mod h1:DK/YYcsSUIVAFNwo/KZCdIIbpN0ROH/baNLgayt4pFc=
+github.com/ipfs/go-block-format v0.0.2 h1:qPDvcP19izTjU8rgo6p7gTXZlkMkF5bz5G3fqIsSCPE=
+github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJgCQF+KIgOPJY=
+github.com/ipfs/go-blockservice v0.1.0/go.mod h1:hzmMScl1kXHg3M2BjTymbVPjv627N7sYcvYaKbop39M=
+github.com/ipfs/go-blockservice v0.1.3 h1:9XgsPMwwWJSC9uVr2pMDsW2qFTBSkxpGMhmna8mIjPM=
+github.com/ipfs/go-blockservice v0.1.3/go.mod h1:OTZhFpkgY48kNzbgyvcexW9cHrpjBYIjSR0KoDOFOLU=
+github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
+github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
+github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
+github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M=
+github.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67FexhXog=
+github.com/ipfs/go-cid v0.0.6 h1:go0y+GcDOGeJIV01FeBsta4FHngoA4Wz7KMeLkXAhMs=
+github.com/ipfs/go-cid v0.0.6/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I=
+github.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE=
+github.com/ipfs/go-datastore v0.0.5/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE=
+github.com/ipfs/go-datastore v0.1.0/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE=
+github.com/ipfs/go-datastore v0.1.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw=
+github.com/ipfs/go-datastore v0.3.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw=
+github.com/ipfs/go-datastore v0.4.0/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA=
+github.com/ipfs/go-datastore v0.4.1/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA=
+github.com/ipfs/go-datastore v0.4.4 h1:rjvQ9+muFaJ+QZ7dN5B1MSDNQ0JVZKkkES/rMZmA8X8=
+github.com/ipfs/go-datastore v0.4.4/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA=
+github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk=
+github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps=
+github.com/ipfs/go-ds-badger v0.0.2/go.mod h1:Y3QpeSFWQf6MopLTiZD+VT6IC1yZqaGmjvRcKeSGij8=
+github.com/ipfs/go-ds-badger v0.0.5/go.mod h1:g5AuuCGmr7efyzQhLL8MzwqcauPojGPUaHzfGTzuE3s=
+github.com/ipfs/go-ds-badger v0.2.1/go.mod h1:Tx7l3aTph3FMFrRS838dcSJh+jjA7cX9DrGVwx/NOwE=
+github.com/ipfs/go-ds-badger v0.2.3 h1:J27YvAcpuA5IvZUbeBxOcQgqnYHUPxoygc6QxxkodZ4=
+github.com/ipfs/go-ds-badger v0.2.3/go.mod h1:pEYw0rgg3FIrywKKnL+Snr+w/LjJZVMTBRn4FS6UHUk=
+github.com/ipfs/go-ds-leveldb v0.0.1/go.mod h1:feO8V3kubwsEF22n0YRQCffeb79OOYIykR4L04tMOYc=
+github.com/ipfs/go-ds-leveldb v0.4.1/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s=
+github.com/ipfs/go-ds-leveldb v0.4.2/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s=
+github.com/ipfs/go-graphsync v0.1.2 h1:25Ll9kIXCE+DY0dicvfS3KMw+U5sd01b/FJbA7KAbhg=
+github.com/ipfs/go-graphsync v0.1.2/go.mod h1:sLXVXm1OxtE2XYPw62MuXCdAuNwkAdsbnfrmos5odbA=
+github.com/ipfs/go-ipfs-blockstore v0.0.1/go.mod h1:d3WClOmRQKFnJ0Jz/jj/zmksX0ma1gROTlovZKBmN08=
+github.com/ipfs/go-ipfs-blockstore v0.1.0/go.mod h1:5aD0AvHPi7mZc6Ci1WCAhiBQu2IsfTduLl+422H6Rqw=
+github.com/ipfs/go-ipfs-blockstore v0.1.4 h1:2SGI6U1B44aODevza8Rde3+dY30Pb+lbcObe1LETxOQ=
+github.com/ipfs/go-ipfs-blockstore v0.1.4/go.mod h1:Jxm3XMVjh6R17WvxFEiyKBLUGr86HgIYJW/D/MwqeYQ=
+github.com/ipfs/go-ipfs-blocksutil v0.0.1 h1:Eh/H4pc1hsvhzsQoMEP3Bke/aW5P5rVM1IWFJMcGIPQ=
+github.com/ipfs/go-ipfs-blocksutil v0.0.1/go.mod h1:Yq4M86uIOmxmGPUHv/uI7uKqZNtLb449gwKqXjIsnRk=
+github.com/ipfs/go-ipfs-chunker v0.0.1/go.mod h1:tWewYK0we3+rMbOh7pPFGDyypCtvGcBFymgY4rSDLAw=
+github.com/ipfs/go-ipfs-chunker v0.0.5 h1:ojCf7HV/m+uS2vhUGWcogIIxiO5ubl5O57Q7NapWLY8=
+github.com/ipfs/go-ipfs-chunker v0.0.5/go.mod h1:jhgdF8vxRHycr00k13FM8Y0E+6BoalYeobXmUyTreP8=
+github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=
+github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ=
+github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=
+github.com/ipfs/go-ipfs-ds-help v0.0.1/go.mod h1:gtP9xRaZXqIQRh1HRpp595KbBEdgqWFxefeVKOV8sxo=
+github.com/ipfs/go-ipfs-ds-help v0.1.1 h1:IW/bXGeaAZV2VH0Kuok+Ohva/zHkHmeLFBxC1k7mNPc=
+github.com/ipfs/go-ipfs-ds-help v0.1.1/go.mod h1:SbBafGJuGsPI/QL3j9Fc5YPLeAu+SzOkI0gFwAg+mOs=
+github.com/ipfs/go-ipfs-exchange-interface v0.0.1 h1:LJXIo9W7CAmugqI+uofioIpRb6rY30GUu7G6LUfpMvM=
+github.com/ipfs/go-ipfs-exchange-interface v0.0.1/go.mod h1:c8MwfHjtQjPoDyiy9cFquVtVHkO9b9Ob3FG91qJnWCM=
+github.com/ipfs/go-ipfs-exchange-offline v0.0.1 h1:P56jYKZF7lDDOLx5SotVh5KFxoY6C81I1NSHW1FxGew=
+github.com/ipfs/go-ipfs-exchange-offline v0.0.1/go.mod h1:WhHSFCVYX36H/anEKQboAzpUws3x7UeEGkzQc3iNkM0=
+github.com/ipfs/go-ipfs-files v0.0.3/go.mod h1:INEFm0LL2LWXBhNJ2PMIIb2w45hpXgPjNoE7yA8Y1d4=
+github.com/ipfs/go-ipfs-files v0.0.8 h1:8o0oFJkJ8UkO/ABl8T6ac6tKF3+NIpj67aAB6ZpusRg=
+github.com/ipfs/go-ipfs-files v0.0.8/go.mod h1:wiN/jSG8FKyk7N0WyctKSvq3ljIa2NNTiZB55kpTdOs=
+github.com/ipfs/go-ipfs-posinfo v0.0.1 h1:Esoxj+1JgSjX0+ylc0hUmJCOv6V2vFoZiETLR6OtpRs=
+github.com/ipfs/go-ipfs-posinfo v0.0.1/go.mod h1:SwyeVP+jCwiDu0C313l/8jg6ZxM0qqtlt2a0vILTc1A=
+github.com/ipfs/go-ipfs-pq v0.0.1/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY=
+github.com/ipfs/go-ipfs-pq v0.0.2 h1:e1vOOW6MuOwG2lqxcLA+wEn93i/9laCY8sXAw76jFOY=
+github.com/ipfs/go-ipfs-pq v0.0.2/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY=
+github.com/ipfs/go-ipfs-routing v0.1.0 h1:gAJTT1cEeeLj6/DlLX6t+NxD9fQe2ymTO6qWRDI/HQQ=
+github.com/ipfs/go-ipfs-routing v0.1.0/go.mod h1:hYoUkJLyAUKhF58tysKpids8RNDPO42BVMgK5dNsoqY=
+github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc=
+github.com/ipfs/go-ipfs-util v0.0.2 h1:59Sswnk1MFaiq+VcaknX7aYEyGyGDAA73ilhEK2POp8=
+github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ=
+github.com/ipfs/go-ipld-cbor v0.0.2/go.mod h1:wTBtrQZA3SoFKMVkp6cn6HMRteIB1VsmHA0AQFOn7Nc=
+github.com/ipfs/go-ipld-cbor v0.0.3/go.mod h1:wTBtrQZA3SoFKMVkp6cn6HMRteIB1VsmHA0AQFOn7Nc=
+github.com/ipfs/go-ipld-cbor v0.0.4 h1:Aw3KPOKXjvrm6VjwJvFf1F1ekR/BH3jdof3Bk7OTiSA=
+github.com/ipfs/go-ipld-cbor v0.0.4/go.mod h1:BkCduEx3XBCO6t2Sfo5BaHzuok7hbhdMm9Oh8B2Ftq4=
+github.com/ipfs/go-ipld-format v0.0.1/go.mod h1:kyJtbkDALmFHv3QR6et67i35QzO3S0dCDnkOJhcZkms=
+github.com/ipfs/go-ipld-format v0.0.2/go.mod h1:4B6+FM2u9OJ9zCV+kSbgFAZlOrv1Hqbf0INGQgiKf9k=
+github.com/ipfs/go-ipld-format v0.2.0 h1:xGlJKkArkmBvowr+GMCX0FEZtkro71K1AwiKnL37mwA=
+github.com/ipfs/go-ipld-format v0.2.0/go.mod h1:3l3C1uKoadTPbeNfrDi+xMInYKlx2Cvg1BuydPSdzQs=
+github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM=
+github.com/ipfs/go-log v1.0.2/go.mod h1:1MNjMxe0u6xvJZgeqbJ8vdo2TKaGwZ1a0Bpza+sr2Sk=
+github.com/ipfs/go-log v1.0.3/go.mod h1:OsLySYkwIbiSUR/yBTdv1qPtcE4FW3WPWk/ewz9Ru+A=
+github.com/ipfs/go-log v1.0.4 h1:6nLQdX4W8P9yZZFH7mO+X/PzjN8Laozm/lMJ6esdgzY=
+github.com/ipfs/go-log v1.0.4/go.mod h1:oDCg2FkjogeFOhqqb+N39l2RpTNPL6F/StPkB3kPgcs=
+github.com/ipfs/go-log/v2 v2.0.2/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0=
+github.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0=
+github.com/ipfs/go-log/v2 v2.0.5 h1:fL4YI+1g5V/b1Yxr1qAiXTMg1H8z9vx/VmJxBuQMHvU=
+github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw=
+github.com/ipfs/go-merkledag v0.2.3/go.mod h1:SQiXrtSts3KGNmgOzMICy5c0POOpUNQLvB3ClKnBAlk=
+github.com/ipfs/go-merkledag v0.3.1 h1:3UqWINBEr3/N+r6OwgFXAddDP/8zpQX/8J7IGVOCqRQ=
+github.com/ipfs/go-merkledag v0.3.1/go.mod h1:fvkZNNZixVW6cKSZ/JfLlON5OlgTXNdRLz0p6QG/I2M=
+github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fGD6n0jO4kdg=
+github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY=
+github.com/ipfs/go-peertaskqueue v0.1.0/go.mod h1:Jmk3IyCcfl1W3jTW3YpghSwSEC6IJ3Vzz/jUmWw8Z0U=
+github.com/ipfs/go-peertaskqueue v0.1.1/go.mod h1:Jmk3IyCcfl1W3jTW3YpghSwSEC6IJ3Vzz/jUmWw8Z0U=
+github.com/ipfs/go-peertaskqueue v0.2.0 h1:2cSr7exUGKYyDeUyQ7P/nHPs9P7Ht/B+ROrpN1EJOjc=
+github.com/ipfs/go-peertaskqueue v0.2.0/go.mod h1:5/eNrBEbtSKWCG+kQK8K8fGNixoYUnr+P7jivavs9lY=
+github.com/ipfs/go-unixfs v0.2.4 h1:6NwppOXefWIyysZ4LR/qUBPvXd5//8J3jiMdvpbw6Lo=
+github.com/ipfs/go-unixfs v0.2.4/go.mod h1:SUdisfUjNoSDzzhGVxvCL9QO/nKdwXdr+gbMUdqcbYw=
+github.com/ipfs/go-verifcid v0.0.1 h1:m2HI7zIuR5TFyQ1b79Da5N9dnnCP1vcu2QqawmWlK2E=
+github.com/ipfs/go-verifcid v0.0.1/go.mod h1:5Hrva5KBeIog4A+UpqlaIU+DEstipcJYQQZc0g37pY0=
+github.com/ipld/go-ipld-prime v0.0.2-0.20200428162820-8b59dc292b8e/go.mod h1:uVIwe/u0H4VdKv3kaN1ck7uCb6yD9cFLS9/ELyXbsw8=
+github.com/ipld/go-ipld-prime v0.0.4-0.20200828224805-5ff8c8b0b6ef h1:/yPelt/0CuzZsmRkYzBBnJ499JnAOGaIaAXHujx96ic=
+github.com/ipld/go-ipld-prime v0.0.4-0.20200828224805-5ff8c8b0b6ef/go.mod h1:uVIwe/u0H4VdKv3kaN1ck7uCb6yD9cFLS9/ELyXbsw8=
+github.com/ipld/go-ipld-prime v0.4.0 h1:ySDtWeWl+TDMokXlwGANSMeD5TN618cZp9NnxqZ452M=
+github.com/ipld/go-ipld-prime v0.4.0/go.mod h1:uVIwe/u0H4VdKv3kaN1ck7uCb6yD9cFLS9/ELyXbsw8=
+github.com/ipld/go-ipld-prime-proto v0.0.0-20200828231332-ae0aea07222b h1:ZtlW6pubN17TDaStlxgrwEXXwwUfJaXu9RobwczXato=
+github.com/ipld/go-ipld-prime-proto v0.0.0-20200828231332-ae0aea07222b/go.mod h1:OAV6xBmuTLsPZ+epzKkPB1e25FHk/vCtyatkdHcArLs=
+github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA=
+github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
+github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
+github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
+github.com/jbenet/go-cienv v0.0.0-20150120210510-1bb1476777ec/go.mod h1:rGaEvXB4uRSZMmzKNLoXvTu1sfx+1kv/DojUlPrSZGs=
+github.com/jbenet/go-cienv v0.1.0 h1:Vc/s0QbQtoxX8MwwSLWWh+xNNZvM3Lw7NsTcHrvvhMc=
+github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA=
+github.com/jbenet/go-random v0.0.0-20190219211222-123a90aedc0c h1:uUx61FiAa1GI6ZmVd2wf2vULeQZIKG66eybjNXKYCz4=
+github.com/jbenet/go-random v0.0.0-20190219211222-123a90aedc0c/go.mod h1:sdx1xVM9UuLw1tXnhJWN3piypTUO3vCIHYmG15KE/dU=
+github.com/jbenet/go-temp-err-catcher v0.0.0-20150120210811-aac704a3f4f2/go.mod h1:8GXXJV31xl8whumTzdZsTt3RnUIiPqzkyf7mxToRCMs=
+github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk=
+github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk=
+github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY=
+github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4=
+github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o=
+github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4=
+github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
+github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
+github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
+github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
+github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/koron/go-ssdp v0.0.0-20180514024734-4a0ed625a78b/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk=
+github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d h1:68u9r4wEvL3gYg2jvAOgROwZ3H+Y3hIDk4tbbmIjcYQ=
+github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ=
+github.com/libp2p/go-addr-util v0.0.2 h1:7cWK5cdA5x72jX0g8iLrQWm5TRJZ6CzGdPEhWj7plWU=
+github.com/libp2p/go-addr-util v0.0.2/go.mod h1:Ecd6Fb3yIuLzq4bD7VcywcVSBtefcAwnUISBM3WG15E=
+github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ=
+github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs=
+github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM=
+github.com/libp2p/go-conn-security-multistream v0.1.0/go.mod h1:aw6eD7LOsHEX7+2hJkDxw1MteijaVcI+/eP2/x3J1xc=
+github.com/libp2p/go-conn-security-multistream v0.2.0 h1:uNiDjS58vrvJTg9jO6bySd1rMKejieG7v45ekqHbZ1M=
+github.com/libp2p/go-conn-security-multistream v0.2.0/go.mod h1:hZN4MjlNetKD3Rq5Jb/P5ohUnFLNzEAR4DLSzpn2QLU=
+github.com/libp2p/go-eventbus v0.1.0/go.mod h1:vROgu5cs5T7cv7POWlWxBaVLxfSegC5UGQf8A2eEmx4=
+github.com/libp2p/go-eventbus v0.2.1 h1:VanAdErQnpTioN2TowqNcOijf6YwhuODe4pPKSDpxGc=
+github.com/libp2p/go-eventbus v0.2.1/go.mod h1:jc2S4SoEVPP48H9Wpzm5aiGwUCBMfGhVhhBjyhhCJs8=
+github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8=
+github.com/libp2p/go-flow-metrics v0.0.3 h1:8tAs/hSdNvUiLgtlSy3mxwxWP4I9y/jlkPFT7epKdeM=
+github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs=
+github.com/libp2p/go-libp2p v0.1.0/go.mod h1:6D/2OBauqLUoqcADOJpn9WbKqvaM07tDw68qHM0BxUM=
+github.com/libp2p/go-libp2p v0.1.1/go.mod h1:I00BRo1UuUSdpuc8Q2mN7yDF/oTUTRAX6JWpTiK9Rp8=
+github.com/libp2p/go-libp2p v0.6.0/go.mod h1:mfKWI7Soz3ABX+XEBR61lGbg+ewyMtJHVt043oWeqwg=
+github.com/libp2p/go-libp2p v0.6.1/go.mod h1:CTFnWXogryAHjXAKEbOf1OWY+VeAP3lDMZkfEI5sT54=
+github.com/libp2p/go-libp2p v0.7.0/go.mod h1:hZJf8txWeCduQRDC/WSqBGMxaTHCOYHt2xSU1ivxn0k=
+github.com/libp2p/go-libp2p v0.7.4/go.mod h1:oXsBlTLF1q7pxr+9w6lqzS1ILpyHsaBPniVO7zIHGMw=
+github.com/libp2p/go-libp2p v0.8.1/go.mod h1:QRNH9pwdbEBpx5DTJYg+qxcVaDMAz3Ee/qDKwXujH5o=
+github.com/libp2p/go-libp2p v0.8.3/go.mod h1:EsH1A+8yoWK+L4iKcbPYu6MPluZ+CHWI9El8cTaefiM=
+github.com/libp2p/go-libp2p v0.10.0 h1:7ooOvK1wi8eLpyTppy8TeH43UHy5uI75GAHGJxenUi0=
+github.com/libp2p/go-libp2p v0.10.0/go.mod h1:yBJNpb+mGJdgrwbKAKrhPU0u3ogyNFTfjJ6bdM+Q/G8=
+github.com/libp2p/go-libp2p-autonat v0.1.0/go.mod h1:1tLf2yXxiE/oKGtDwPYWTSYG3PtvYlJmg7NeVtPRqH8=
+github.com/libp2p/go-libp2p-autonat v0.1.1/go.mod h1:OXqkeGOY2xJVWKAGV2inNF5aKN/djNA3fdpCWloIudE=
+github.com/libp2p/go-libp2p-autonat v0.2.0/go.mod h1:DX+9teU4pEEoZUqR1PiMlqliONQdNbfzE1C718tcViI=
+github.com/libp2p/go-libp2p-autonat v0.2.1/go.mod h1:MWtAhV5Ko1l6QBsHQNSuM6b1sRkXrpk0/LqCr+vCVxI=
+github.com/libp2p/go-libp2p-autonat v0.2.2/go.mod h1:HsM62HkqZmHR2k1xgX34WuWDzk/nBwNHoeyyT4IWV6A=
+github.com/libp2p/go-libp2p-autonat v0.2.3 h1:w46bKK3KTOUWDe5mDYMRjJu1uryqBp8HCNDp/TWMqKw=
+github.com/libp2p/go-libp2p-autonat v0.2.3/go.mod h1:2U6bNWCNsAG9LEbwccBDQbjzQ8Krdjge1jLTE9rdoMM=
+github.com/libp2p/go-libp2p-blankhost v0.1.1/go.mod h1:pf2fvdLJPsC1FsVrNP3DUUvMzUts2dsLLBEpo1vW1ro=
+github.com/libp2p/go-libp2p-blankhost v0.1.4/go.mod h1:oJF0saYsAXQCSfDq254GMNmLNz6ZTHTOvtF4ZydUvwU=
+github.com/libp2p/go-libp2p-blankhost v0.1.6 h1:CkPp1/zaCrCnBo0AdsQA0O1VkUYoUOtyHOnoa8gKIcE=
+github.com/libp2p/go-libp2p-blankhost v0.1.6/go.mod h1:jONCAJqEP+Z8T6EQviGL4JsQcLx1LgTGtVqFNY8EMfQ=
+github.com/libp2p/go-libp2p-circuit v0.1.0/go.mod h1:Ahq4cY3V9VJcHcn1SBXjr78AbFkZeIRmfunbA7pmFh8=
+github.com/libp2p/go-libp2p-circuit v0.1.4/go.mod h1:CY67BrEjKNDhdTk8UgBX1Y/H5c3xkAcs3gnksxY7osU=
+github.com/libp2p/go-libp2p-circuit v0.2.1/go.mod h1:BXPwYDN5A8z4OEY9sOfr2DUQMLQvKt/6oku45YUmjIo=
+github.com/libp2p/go-libp2p-circuit v0.2.2/go.mod h1:nkG3iE01tR3FoQ2nMm06IUrCpCyJp1Eo4A1xYdpjfs4=
+github.com/libp2p/go-libp2p-circuit v0.2.3 h1:3Uw1fPHWrp1tgIhBz0vSOxRUmnKL8L/NGUyEd5WfSGM=
+github.com/libp2p/go-libp2p-circuit v0.2.3/go.mod h1:nkG3iE01tR3FoQ2nMm06IUrCpCyJp1Eo4A1xYdpjfs4=
+github.com/libp2p/go-libp2p-core v0.0.1/go.mod h1:g/VxnTZ/1ygHxH3dKok7Vno1VfpvGcGip57wjTU4fco=
+github.com/libp2p/go-libp2p-core v0.0.2/go.mod h1:9dAcntw/n46XycV4RnlBq3BpgrmyUi9LuoTNdPrbUco=
+github.com/libp2p/go-libp2p-core v0.0.3/go.mod h1:j+YQMNz9WNSkNezXOsahp9kwZBKBvxLpKD316QWSJXE=
+github.com/libp2p/go-libp2p-core v0.0.4/go.mod h1:jyuCQP356gzfCFtRKyvAbNkyeuxb7OlyhWZ3nls5d2I=
+github.com/libp2p/go-libp2p-core v0.2.0/go.mod h1:X0eyB0Gy93v0DZtSYbEM7RnMChm9Uv3j7yRXjO77xSI=
+github.com/libp2p/go-libp2p-core v0.2.2/go.mod h1:8fcwTbsG2B+lTgRJ1ICZtiM5GWCWZVoVrLaDRvIRng0=
+github.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g=
+github.com/libp2p/go-libp2p-core v0.3.0/go.mod h1:ACp3DmS3/N64c2jDzcV429ukDpicbL6+TrrxANBjPGw=
+github.com/libp2p/go-libp2p-core v0.3.1/go.mod h1:thvWy0hvaSBhnVBaW37BvzgVV68OUhgJJLAa6almrII=
+github.com/libp2p/go-libp2p-core v0.4.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0=
+github.com/libp2p/go-libp2p-core v0.5.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0=
+github.com/libp2p/go-libp2p-core v0.5.1/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y=
+github.com/libp2p/go-libp2p-core v0.5.2/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y=
+github.com/libp2p/go-libp2p-core v0.5.3/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y=
+github.com/libp2p/go-libp2p-core v0.5.4/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y=
+github.com/libp2p/go-libp2p-core v0.5.5/go.mod h1:vj3awlOr9+GMZJFH9s4mpt9RHHgGqeHCopzbYKZdRjM=
+github.com/libp2p/go-libp2p-core v0.5.6/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo=
+github.com/libp2p/go-libp2p-core v0.5.7/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo=
+github.com/libp2p/go-libp2p-core v0.6.0 h1:u03qofNYTBN+yVg08PuAKylZogVf0xcTEeM8skGf+ak=
+github.com/libp2p/go-libp2p-core v0.6.0/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo=
+github.com/libp2p/go-libp2p-crypto v0.1.0/go.mod h1:sPUokVISZiy+nNuTTH/TY+leRSxnFj/2GLjtOTW90hI=
+github.com/libp2p/go-libp2p-discovery v0.1.0/go.mod h1:4F/x+aldVHjHDHuX85x1zWoFTGElt8HnoDzwkFZm29g=
+github.com/libp2p/go-libp2p-discovery v0.2.0/go.mod h1:s4VGaxYMbw4+4+tsoQTqh7wfxg97AEdo4GYBt6BadWg=
+github.com/libp2p/go-libp2p-discovery v0.3.0/go.mod h1:o03drFnz9BVAZdzC/QUQ+NeQOu38Fu7LJGEOK2gQltw=
+github.com/libp2p/go-libp2p-discovery v0.4.0 h1:dK78UhopBk48mlHtRCzbdLm3q/81g77FahEBTjcqQT8=
+github.com/libp2p/go-libp2p-discovery v0.4.0/go.mod h1:bZ0aJSrFc/eX2llP0ryhb1kpgkPyTo23SJ5b7UQCMh4=
+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-mplex v0.2.0/go.mod h1:Ejl9IyjvXJ0T9iqUTE1jpYATQ9NM3g+OtR+EMMODbKo=
+github.com/libp2p/go-libp2p-mplex v0.2.1/go.mod h1:SC99Rxs8Vuzrf/6WhmH41kNn13TiYdAWNYHrwImKLnE=
+github.com/libp2p/go-libp2p-mplex v0.2.2/go.mod h1:74S9eum0tVQdAfFiKxAyKzNdSuLqw5oadDq7+L/FELo=
+github.com/libp2p/go-libp2p-mplex v0.2.3 h1:2zijwaJvpdesST2MXpI5w9wWFRgYtMcpRX7rrw0jmOo=
+github.com/libp2p/go-libp2p-mplex v0.2.3/go.mod h1:CK3p2+9qH9x+7ER/gWWDYJ3QW5ZxWDkm+dVvjfuG3ek=
+github.com/libp2p/go-libp2p-nat v0.0.4/go.mod h1:N9Js/zVtAXqaeT99cXgTV9e75KpnWCvVOiGzlcHmBbY=
+github.com/libp2p/go-libp2p-nat v0.0.5/go.mod h1:1qubaE5bTZMJE+E/uu2URroMbzdubFz1ChgiN79yKPE=
+github.com/libp2p/go-libp2p-nat v0.0.6 h1:wMWis3kYynCbHoyKLPBEMu4YRLltbm8Mk08HGSfvTkU=
+github.com/libp2p/go-libp2p-nat v0.0.6/go.mod h1:iV59LVhB3IkFvS6S6sauVTSOrNEANnINbI/fkaLimiw=
+github.com/libp2p/go-libp2p-netutil v0.1.0 h1:zscYDNVEcGxyUpMd0JReUZTrpMfia8PmLKcKF72EAMQ=
+github.com/libp2p/go-libp2p-netutil v0.1.0/go.mod h1:3Qv/aDqtMLTUyQeundkKsA+YCThNdbQD54k3TqjpbFU=
+github.com/libp2p/go-libp2p-noise v0.1.1 h1:vqYQWvnIcHpIoWJKC7Al4D6Hgj0H012TuXRhPwSMGpQ=
+github.com/libp2p/go-libp2p-noise v0.1.1/go.mod h1:QDFLdKX7nluB7DEnlVPbz7xlLHdwHFA9HiohJRr3vwM=
+github.com/libp2p/go-libp2p-peer v0.2.0/go.mod h1:RCffaCvUyW2CJmG2gAWVqwePwW7JMgxjsHm7+J5kjWY=
+github.com/libp2p/go-libp2p-peerstore v0.1.0/go.mod h1:2CeHkQsr8svp4fZ+Oi9ykN1HBb6u0MOvdJ7YIsmcwtY=
+github.com/libp2p/go-libp2p-peerstore v0.1.3/go.mod h1:BJ9sHlm59/80oSkpWgr1MyY1ciXAXV397W6h1GH/uKI=
+github.com/libp2p/go-libp2p-peerstore v0.2.0/go.mod h1:N2l3eVIeAitSg3Pi2ipSrJYnqhVnMNQZo9nkSCuAbnQ=
+github.com/libp2p/go-libp2p-peerstore v0.2.1/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRjwRLBr4TYKfNgrUkOPA=
+github.com/libp2p/go-libp2p-peerstore v0.2.2/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRjwRLBr4TYKfNgrUkOPA=
+github.com/libp2p/go-libp2p-peerstore v0.2.3/go.mod h1:K8ljLdFn590GMttg/luh4caB/3g0vKuY01psze0upRw=
+github.com/libp2p/go-libp2p-peerstore v0.2.4/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s=
+github.com/libp2p/go-libp2p-peerstore v0.2.6 h1:2ACefBX23iMdJU9Ke+dcXt3w86MIryes9v7In4+Qq3U=
+github.com/libp2p/go-libp2p-peerstore v0.2.6/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s=
+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-quic-transport v0.5.0 h1:BUN1lgYNUrtv4WLLQ5rQmC9MCJ6uEXusezGvYRNoJXE=
+github.com/libp2p/go-libp2p-quic-transport v0.5.0/go.mod h1:IEcuC5MLxvZ5KuHKjRu+dr3LjCT1Be3rcD/4d8JrX8M=
+github.com/libp2p/go-libp2p-record v0.1.0/go.mod h1:ujNc8iuE5dlKWVy6wuL6dd58t0n7xI4hAIl8pE6wu5Q=
+github.com/libp2p/go-libp2p-record v0.1.1 h1:ZJK2bHXYUBqObHX+rHLSNrM3M8fmJUlUHrodDPPATmY=
+github.com/libp2p/go-libp2p-record v0.1.1/go.mod h1:VRgKajOyMVgP/F0L5g3kH7SVskp17vFi2xheb5uMJtg=
+github.com/libp2p/go-libp2p-secio v0.1.0/go.mod h1:tMJo2w7h3+wN4pgU2LSYeiKPrfqBgkOsdiKK77hE7c8=
+github.com/libp2p/go-libp2p-secio v0.2.0/go.mod h1:2JdZepB8J5V9mBp79BmwsaPQhRPNN2NrnB2lKQcdy6g=
+github.com/libp2p/go-libp2p-secio v0.2.1/go.mod h1:cWtZpILJqkqrSkiYcDBh5lA3wbT2Q+hz3rJQq3iftD8=
+github.com/libp2p/go-libp2p-secio v0.2.2 h1:rLLPvShPQAcY6eNurKNZq3eZjPWfU9kXF2eI9jIYdrg=
+github.com/libp2p/go-libp2p-secio v0.2.2/go.mod h1:wP3bS+m5AUnFA+OFO7Er03uO1mncHG0uVwGrwvjYlNY=
+github.com/libp2p/go-libp2p-swarm v0.1.0/go.mod h1:wQVsCdjsuZoc730CgOvh5ox6K8evllckjebkdiY5ta4=
+github.com/libp2p/go-libp2p-swarm v0.2.2/go.mod h1:fvmtQ0T1nErXym1/aa1uJEyN7JzaTNyBcHImCxRpPKU=
+github.com/libp2p/go-libp2p-swarm v0.2.3/go.mod h1:P2VO/EpxRyDxtChXz/VPVXyTnszHvokHKRhfkEgFKNM=
+github.com/libp2p/go-libp2p-swarm v0.2.7 h1:4lV/sf7f0NuVqunOpt1I11+Z54+xp+m0eeAvxj/LyRc=
+github.com/libp2p/go-libp2p-swarm v0.2.7/go.mod h1:ZSJ0Q+oq/B1JgfPHJAT2HTall+xYRNYp1xs4S2FBWKA=
+github.com/libp2p/go-libp2p-testing v0.0.2/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E=
+github.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E=
+github.com/libp2p/go-libp2p-testing v0.0.4/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E=
+github.com/libp2p/go-libp2p-testing v0.1.0/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0=
+github.com/libp2p/go-libp2p-testing v0.1.1 h1:U03z3HnGI7Ni8Xx6ONVZvUFOAzWYmolWf5W5jAOPNmU=
+github.com/libp2p/go-libp2p-testing v0.1.1/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0=
+github.com/libp2p/go-libp2p-tls v0.1.3 h1:twKMhMu44jQO+HgQK9X8NHO5HkeJu2QbhLzLJpa8oNM=
+github.com/libp2p/go-libp2p-tls v0.1.3/go.mod h1:wZfuewxOndz5RTnCAxFliGjvYSDA40sKitV4c50uI1M=
+github.com/libp2p/go-libp2p-transport-upgrader v0.1.1/go.mod h1:IEtA6or8JUbsV07qPW4r01GnTenLW4oi3lOPbUMGJJA=
+github.com/libp2p/go-libp2p-transport-upgrader v0.2.0/go.mod h1:mQcrHj4asu6ArfSoMuyojOdjx73Q47cYD7s5+gZOlns=
+github.com/libp2p/go-libp2p-transport-upgrader v0.3.0 h1:q3ULhsknEQ34eVDhv4YwKS8iet69ffs9+Fir6a7weN4=
+github.com/libp2p/go-libp2p-transport-upgrader v0.3.0/go.mod h1:i+SKzbRnvXdVbU3D1dwydnTmKRPXiAR/fyvi1dXuL4o=
+github.com/libp2p/go-libp2p-yamux v0.2.0/go.mod h1:Db2gU+XfLpm6E4rG5uGCFX6uXA8MEXOxFcRoXUODaK8=
+github.com/libp2p/go-libp2p-yamux v0.2.1/go.mod h1:1FBXiHDk1VyRM1C0aez2bCfHQ4vMZKkAQzZbkSQt5fI=
+github.com/libp2p/go-libp2p-yamux v0.2.2/go.mod h1:lIohaR0pT6mOt0AZ0L2dFze9hds9Req3OfS+B+dv4qw=
+github.com/libp2p/go-libp2p-yamux v0.2.5/go.mod h1:Zpgj6arbyQrmZ3wxSZxfBmbdnWtbZ48OpsfmQVTErwA=
+github.com/libp2p/go-libp2p-yamux v0.2.7/go.mod h1:X28ENrBMU/nm4I3Nx4sZ4dgjZ6VhLEn0XhIoZ5viCwU=
+github.com/libp2p/go-libp2p-yamux v0.2.8 h1:0s3ELSLu2O7hWKfX1YjzudBKCP0kZ+m9e2+0veXzkn4=
+github.com/libp2p/go-libp2p-yamux v0.2.8/go.mod h1:/t6tDqeuZf0INZMTgd0WxIRbtK2EzI2h7HbFm9eAKI4=
+github.com/libp2p/go-maddr-filter v0.0.4/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q=
+github.com/libp2p/go-maddr-filter v0.0.5/go.mod h1:Jk+36PMfIqCJhAnaASRH83bdAvfDRp/w6ENFaC9bG+M=
+github.com/libp2p/go-mplex v0.0.3/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTWe7l4Yd0=
+github.com/libp2p/go-mplex v0.1.0/go.mod h1:SXgmdki2kwCUlCCbfGLEgHjC4pFqhTp0ZoV6aiKgxDU=
+github.com/libp2p/go-mplex v0.1.1/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk=
+github.com/libp2p/go-mplex v0.1.2 h1:qOg1s+WdGLlpkrczDqmhYzyk3vCfsQ8+RxRTQjOZWwI=
+github.com/libp2p/go-mplex v0.1.2/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk=
+github.com/libp2p/go-msgio v0.0.2/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ=
+github.com/libp2p/go-msgio v0.0.3/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ=
+github.com/libp2p/go-msgio v0.0.4 h1:agEFehY3zWJFUHK6SEMR7UYmk2z6kC3oeCM7ybLhguA=
+github.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ=
+github.com/libp2p/go-nat v0.0.3/go.mod h1:88nUEt0k0JD45Bk93NIwDqjlhiOwOoV36GchpcVc1yI=
+github.com/libp2p/go-nat v0.0.4/go.mod h1:Nmw50VAvKuk38jUBcmNh6p9lUJLoODbJRvYAa/+KSDo=
+github.com/libp2p/go-nat v0.0.5 h1:qxnwkco8RLKqVh1NmjQ+tJ8p8khNLFxuElYG/TwqW4Q=
+github.com/libp2p/go-nat v0.0.5/go.mod h1:B7NxsVNPZmRLvMOwiEO1scOSyjA56zxYAGv1yQgRkEU=
+github.com/libp2p/go-netroute v0.1.2 h1:UHhB35chwgvcRI392znJA3RCBtZ3MpE3ahNCN5MR4Xg=
+github.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk=
+github.com/libp2p/go-openssl v0.0.2/go.mod h1:v8Zw2ijCSWBQi8Pq5GAixw6DbFfa9u6VIYDXnvOXkc0=
+github.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=
+github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=
+github.com/libp2p/go-openssl v0.0.5 h1:pQkejVhF0xp08D4CQUcw8t+BFJeXowja6RVcb5p++EA=
+github.com/libp2p/go-openssl v0.0.5/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=
+github.com/libp2p/go-reuseport v0.0.1 h1:7PhkfH73VXfPJYKQ6JwS5I/eVcoyYi9IMNGc6FWpFLw=
+github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA=
+github.com/libp2p/go-reuseport-transport v0.0.2/go.mod h1:YkbSDrvjUVDL6b8XqriyA20obEtsW9BLkuOUyQAOCbs=
+github.com/libp2p/go-reuseport-transport v0.0.3 h1:zzOeXnTooCkRvoH+bSXEfXhn76+LAiwoneM0gnXjF2M=
+github.com/libp2p/go-reuseport-transport v0.0.3/go.mod h1:Spv+MPft1exxARzP2Sruj2Wb5JSyHNncjf1Oi2dEbzM=
+github.com/libp2p/go-sockaddr v0.0.2/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k=
+github.com/libp2p/go-sockaddr v0.1.0 h1:Y4s3/jNoryVRKEBrkJ576F17CPOaMIzUeCsg7dlTDj0=
+github.com/libp2p/go-sockaddr v0.1.0/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k=
+github.com/libp2p/go-stream-muxer v0.0.1/go.mod h1:bAo8x7YkSpadMTbtTaxGVHWUQsR/l5MEaHbKaliuT14=
+github.com/libp2p/go-stream-muxer-multistream v0.2.0/go.mod h1:j9eyPol/LLRqT+GPLSxvimPhNph4sfYfMoDPd7HkzIc=
+github.com/libp2p/go-stream-muxer-multistream v0.3.0 h1:TqnSHPJEIqDEO7h1wZZ0p3DXdvDSiLHQidKKUGZtiOY=
+github.com/libp2p/go-stream-muxer-multistream v0.3.0/go.mod h1:yDh8abSIzmZtqtOt64gFJUXEryejzNb0lisTt+fAMJA=
+github.com/libp2p/go-tcp-transport v0.1.0/go.mod h1:oJ8I5VXryj493DEJ7OsBieu8fcg2nHGctwtInJVpipc=
+github.com/libp2p/go-tcp-transport v0.1.1/go.mod h1:3HzGvLbx6etZjnFlERyakbaYPdfjg2pWP97dFZworkY=
+github.com/libp2p/go-tcp-transport v0.2.0 h1:YoThc549fzmNJIh7XjHVtMIFaEDRtIrtWciG5LyYAPo=
+github.com/libp2p/go-tcp-transport v0.2.0/go.mod h1:vX2U0CnWimU4h0SGSEsg++AzvBcroCGYw28kh94oLe0=
+github.com/libp2p/go-testutil v0.1.0/go.mod h1:81b2n5HypcVyrCg/MJx4Wgfp/VHojytjVe/gLzZ2Ehc=
+github.com/libp2p/go-ws-transport v0.1.0/go.mod h1:rjw1MG1LU9YDC6gzmwObkPd/Sqwhw7yT74kj3raBFuo=
+github.com/libp2p/go-ws-transport v0.2.0/go.mod h1:9BHJz/4Q5A9ludYWKoGCFC5gUElzlHoKzu0yY9p/klM=
+github.com/libp2p/go-ws-transport v0.3.0/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1fiZuEaBYusP5zsk=
+github.com/libp2p/go-ws-transport v0.3.1 h1:ZX5rWB8nhRRJVaPO6tmkGI/Xx8XNboYX20PW5hXIscw=
+github.com/libp2p/go-ws-transport v0.3.1/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1fiZuEaBYusP5zsk=
+github.com/libp2p/go-yamux v1.2.2/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
+github.com/libp2p/go-yamux v1.2.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
+github.com/libp2p/go-yamux v1.3.0/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
+github.com/libp2p/go-yamux v1.3.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
+github.com/libp2p/go-yamux v1.3.5/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
+github.com/libp2p/go-yamux v1.3.7 h1:v40A1eSPJDIZwz2AvrV3cxpTZEGDP11QJbukmEhYyQI=
+github.com/libp2p/go-yamux v1.3.7/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE=
+github.com/lucas-clemente/quic-go v0.16.0 h1:jJw36wfzGJhmOhAOaOC2lS36WgeqXQszH47A7spo1LI=
+github.com/lucas-clemente/quic-go v0.16.0/go.mod h1:I0+fcNTdb9eS1ZcjQZbDVPGchJ86chcIxPALn9lEJqE=
+github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGDAUWqna+CNTrI=
+github.com/marten-seemann/qtls v0.9.1 h1:O0YKQxNVPaiFgMng0suWEOY2Sb4LT2sRn9Qimq3Z1IQ=
+github.com/marten-seemann/qtls v0.9.1/go.mod h1:T1MmAdDPyISzxlK6kjRr0pcZFBVd1OZbBb/j3cvzHhk=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
+github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
+github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
+github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/miekg/dns v1.1.28/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
+github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g=
+github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
+github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=
+github.com/minio/sha256-simd v0.0.0-20190328051042-05b4dd3047e5/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=
+github.com/minio/sha256-simd v0.1.0/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=
+github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
+github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=
+github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
+github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
+github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
+github.com/mr-tron/base58 v1.1.3 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc=
+github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
+github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI=
+github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
+github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4=
+github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM=
+github.com/multiformats/go-multiaddr v0.0.1/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44=
+github.com/multiformats/go-multiaddr v0.0.2/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44=
+github.com/multiformats/go-multiaddr v0.0.4/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44=
+github.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44=
+github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo=
+github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4=
+github.com/multiformats/go-multiaddr v0.2.1/go.mod h1:s/Apk6IyxfvMjDafnhJgJ3/46z7tZ04iMk5wP4QMGGE=
+github.com/multiformats/go-multiaddr v0.2.2 h1:XZLDTszBIJe6m0zF6ITBrEcZR73OPUhCBBS9rYAuUzI=
+github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u0xW5UouOmQQrn6a3Y=
+github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q=
+github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q=
+github.com/multiformats/go-multiaddr-dns v0.2.0 h1:YWJoIDwLePniH7OU5hBnDZV6SWuvJqJ0YtN6pLeH9zA=
+github.com/multiformats/go-multiaddr-dns v0.2.0/go.mod h1:TJ5pr5bBO7Y1B18djPuRsVkduhQH2YqYSbxWJzYGdK0=
+github.com/multiformats/go-multiaddr-fmt v0.0.1/go.mod h1:aBYjqL4T/7j4Qx+R73XSv/8JsgnRFlf0w2KGLCmXl3Q=
+github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E=
+github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo=
+github.com/multiformats/go-multiaddr-net v0.0.1/go.mod h1:nw6HSxNmCIQH27XPGBuX+d1tnvM7ihcFwHMSstNAVUU=
+github.com/multiformats/go-multiaddr-net v0.1.0/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ=
+github.com/multiformats/go-multiaddr-net v0.1.1/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ=
+github.com/multiformats/go-multiaddr-net v0.1.2/go.mod h1:QsWt3XK/3hwvNxZJp92iMQKME1qHfpYmyIjFVsSOY6Y=
+github.com/multiformats/go-multiaddr-net v0.1.3/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA=
+github.com/multiformats/go-multiaddr-net v0.1.4/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA=
+github.com/multiformats/go-multiaddr-net v0.1.5 h1:QoRKvu0xHN1FCFJcMQLbG/yQE2z441L5urvG3+qyz7g=
+github.com/multiformats/go-multiaddr-net v0.1.5/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA=
+github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs=
+github.com/multiformats/go-multibase v0.0.3 h1:l/B6bJDQjvQ5G52jw4QGSYeOTZoAwIO77RblWplfIqk=
+github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc=
+github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U=
+github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po=
+github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
+github.com/multiformats/go-multihash v0.0.10/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
+github.com/multiformats/go-multihash v0.0.13 h1:06x+mk/zj1FoMsgNejLpy6QTvJqlSt/BhLEy87zidlc=
+github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc=
+github.com/multiformats/go-multistream v0.1.0/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg=
+github.com/multiformats/go-multistream v0.1.1 h1:JlAdpIFhBhGRLxe9W6Om0w++Gd6KMWoFPZL/dEnm9nI=
+github.com/multiformats/go-multistream v0.1.1/go.mod h1:KmHZ40hzVxiaiwlj3MEbYgK9JFk2/9UktWZAF54Du38=
+github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
+github.com/multiformats/go-varint v0.0.2/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
+github.com/multiformats/go-varint v0.0.5 h1:XVZwSo04Cs3j/jS0uAEPpT3JY6DzMcVLLoWOSnCxOjg=
+github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
+github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
+github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
+github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ=
+github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
+github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
+github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg=
+github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
+github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
+github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/polydawn/refmt v0.0.0-20190221155625-df39d6c2d992/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o=
+github.com/polydawn/refmt v0.0.0-20190408063855-01bf1e26dd14/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o=
+github.com/polydawn/refmt v0.0.0-20190807091052-3d65705ee9f1/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o=
+github.com/polydawn/refmt v0.0.0-20190809202753-05966cbd336a h1:hjZfReYVLbqFkAtr2us7vdy04YWz3LVAirzP7reh8+M=
+github.com/polydawn/refmt v0.0.0-20190809202753-05966cbd336a/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o=
+github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
+github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
+github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
+github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
+github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
+github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ=
+github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
+github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
+github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
+github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
+github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
+github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
+github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
+github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
+github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
+github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
+github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
+github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
+github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
+github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
+github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
+github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
+github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
+github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
+github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
+github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
+github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
+github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
+github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w=
+github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
+github.com/smartystreets/goconvey v0.0.0-20190222223459-a17d461953aa/go.mod h1:2RVY1rIf+2J2o/IM9+vPq9RzmHDSseB7FoXiSNIUsoU=
+github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8=
+github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
+github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY=
+github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
+github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
+github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0=
+github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU=
+github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
+github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+github.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+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/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
+github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
+github.com/testground/sdk-go v0.2.7-0.20201112151952-8ee00c80c3ec h1:ZigYjS91IfPRStWGEZuI8/QDes9vPKpwnmLmc3AVQns=
+github.com/testground/sdk-go v0.2.7-0.20201112151952-8ee00c80c3ec/go.mod h1:Q4dnWsUBH+dZ1u7aEGDBHWGUaLfhitjUq3UJQqxeTmk=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
+github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
+github.com/warpfork/go-wish v0.0.0-20180510122957-5ad1f5abf436/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw=
+github.com/warpfork/go-wish v0.0.0-20190328234359-8b3e70f8e830/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw=
+github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a h1:G++j5e0OC488te356JvdhaM8YS6nMsjLAYF7JxCv07w=
+github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw=
+github.com/whyrusleeping/cbor-gen v0.0.0-20200123233031-1cdf64d27158/go.mod h1:Xj/M2wWU+QdTdRbu/L/1dIZY8/Wb2K9pAhtroQuxJJI=
+github.com/whyrusleeping/cbor-gen v0.0.0-20200402171437-3d27c146c105 h1:Sh6UG5dW5xW8Ek2CtRGq4ipdEvvx9hOyBJjEGyTYDl0=
+github.com/whyrusleeping/cbor-gen v0.0.0-20200402171437-3d27c146c105/go.mod h1:Xj/M2wWU+QdTdRbu/L/1dIZY8/Wb2K9pAhtroQuxJJI=
+github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f h1:jQa4QT2UP9WYv2nzyawpKMOCl+Z/jW7djv2/J50lj9E=
+github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f/go.mod h1:p9UJB6dDgdPgMJZs7UjUOdulKyRr9fqkS+6JKAInPy8=
+github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc=
+github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM=
+github.com/whyrusleeping/go-logging v0.0.1/go.mod h1:lDPYj54zutzG1XYfHAhcc7oNXEburHQBn+Iqd4yS4vE=
+github.com/whyrusleeping/go-notifier v0.0.0-20170827234753-097c5d47330f/go.mod h1:cZNvX9cFybI01GriPRMXDtczuvUhgbcYr9iCGaNlRv8=
+github.com/whyrusleeping/mafmt v1.2.8/go.mod h1:faQJFPbLSxzD9xpA02ttW/tS9vZykNvXwGvqIpk20FA=
+github.com/whyrusleeping/mdns v0.0.0-20180901202407-ef14215e6b30/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4=
+github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4=
+github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 h1:E9S12nwJwEOXe2d6gT6qxdvqMnNq+VnSsKPgm2ZZNds=
+github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI=
+github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
+go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
+go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA=
+go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8=
+go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
+go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/goleak v1.0.0 h1:qsup4IcBdlmsnGfqyLl4Ntn3C2XCCuKAE7DwHpScyUo=
+go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
+go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
+go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
+go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
+go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
+go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
+go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
+go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
+golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
+golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5 h1:Q7tZBpemrlsc2I7IyODzhtallWRSm4Q0d09pL6XbQtU=
+golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190302025703-b6889370fb10/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190524122548-abf6ff778158/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190526052359-791d8a0f4d09/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo=
+golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=
+golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200827010519-17fd2f27a9e3 h1:r3P/5xOq/dK1991B65Oy6E1fRF/2d/fSYZJ/fXGVfJc=
+golang.org/x/tools v0.0.0-20200827010519-17fd2f27a9e3/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
+google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
+google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
+google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
+google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
+google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/src-d/go-cli.v0 v0.0.0-20181105080154-d492247bbc0d/go.mod h1:z+K8VcOYVYcSwSjGebuDL6176A1XskgbtNl64NSg+n8=
+gopkg.in/src-d/go-log.v1 v1.0.1/go.mod h1:GN34hKP0g305ysm2/hctJ0Y8nWP3zxXXJ8GFabTyABE=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
+gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
+honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
+honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
+sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
diff --git a/testplans/graphsync/main.go b/testplans/graphsync/main.go
new file mode 100644
index 000000000..1567d0290
--- /dev/null
+++ b/testplans/graphsync/main.go
@@ -0,0 +1,388 @@
+package main
+
+import (
+ "context"
+ "crypto/rand"
+ "fmt"
+ "io"
+ goruntime "runtime"
+ "strings"
+ "time"
+
+ "github.com/dustin/go-humanize"
+ allselector "github.com/hannahhoward/all-selector"
+ "github.com/ipfs/go-blockservice"
+ "github.com/ipfs/go-cid"
+ ds "github.com/ipfs/go-datastore"
+ dss "github.com/ipfs/go-datastore/sync"
+ "github.com/ipfs/go-graphsync/storeutil"
+ blockstore "github.com/ipfs/go-ipfs-blockstore"
+ chunk "github.com/ipfs/go-ipfs-chunker"
+ offline "github.com/ipfs/go-ipfs-exchange-offline"
+ files "github.com/ipfs/go-ipfs-files"
+ format "github.com/ipfs/go-ipld-format"
+ "github.com/ipfs/go-merkledag"
+ "github.com/ipfs/go-unixfs/importer/balanced"
+ ihelper "github.com/ipfs/go-unixfs/importer/helpers"
+ cidlink "github.com/ipld/go-ipld-prime/linking/cid"
+ "github.com/libp2p/go-libp2p-core/metrics"
+ "github.com/testground/sdk-go/network"
+ "golang.org/x/sync/errgroup"
+
+ gs "github.com/ipfs/go-graphsync"
+ gsi "github.com/ipfs/go-graphsync/impl"
+ gsnet "github.com/ipfs/go-graphsync/network"
+
+ "github.com/libp2p/go-libp2p"
+ "github.com/libp2p/go-libp2p-core/host"
+ "github.com/libp2p/go-libp2p-core/peer"
+ noise "github.com/libp2p/go-libp2p-noise"
+ secio "github.com/libp2p/go-libp2p-secio"
+ tls "github.com/libp2p/go-libp2p-tls"
+
+ "github.com/testground/sdk-go/run"
+ "github.com/testground/sdk-go/runtime"
+ "github.com/testground/sdk-go/sync"
+)
+
+var testcases = map[string]interface{}{
+ "stress": run.InitializedTestCaseFn(runStress),
+}
+
+func main() {
+ run.InvokeMap(testcases)
+}
+
+type networkParams struct {
+ latency time.Duration
+ bandwidth uint64
+}
+
+func (p networkParams) String() string {
+ return fmt.Sprintf("", p.latency, p.bandwidth)
+}
+
+func runStress(runenv *runtime.RunEnv, initCtx *run.InitContext) error {
+ var (
+ size = runenv.SizeParam("size")
+ concurrency = runenv.IntParam("concurrency")
+
+ networkParams = parseNetworkConfig(runenv)
+ )
+ runenv.RecordMessage("started test instance")
+ runenv.RecordMessage("network params: %v", networkParams)
+
+ ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute)
+ defer cancel()
+
+ initCtx.MustWaitAllInstancesInitialized(ctx)
+
+ host, peers, _ := makeHost(ctx, runenv, initCtx)
+ defer host.Close()
+
+ var (
+ // make datastore, blockstore, dag service, graphsync
+ bs = blockstore.NewBlockstore(dss.MutexWrap(ds.NewMapDatastore()))
+ dagsrv = merkledag.NewDAGService(blockservice.New(bs, offline.Exchange(bs)))
+ gsync = gsi.New(ctx,
+ gsnet.NewFromLibp2pHost(host),
+ storeutil.LoaderForBlockstore(bs),
+ storeutil.StorerForBlockstore(bs),
+ )
+ )
+
+ defer initCtx.SyncClient.MustSignalAndWait(ctx, "done", runenv.TestInstanceCount)
+
+ switch runenv.TestGroupID {
+ case "providers":
+ if runenv.TestGroupInstanceCount > 1 {
+ panic("test case only supports one provider")
+ }
+
+ runenv.RecordMessage("we are the provider")
+ defer runenv.RecordMessage("done provider")
+
+ gsync.RegisterIncomingRequestHook(func(p peer.ID, request gs.RequestData, hookActions gs.IncomingRequestHookActions) {
+ hookActions.ValidateRequest()
+ })
+
+ return runProvider(ctx, runenv, initCtx, dagsrv, size, networkParams, concurrency)
+
+ case "requestors":
+ runenv.RecordMessage("we are the requestor")
+ defer runenv.RecordMessage("done requestor")
+
+ p := *peers[0]
+ if err := host.Connect(ctx, p); err != nil {
+ return err
+ }
+ runenv.RecordMessage("done dialling provider")
+ return runRequestor(ctx, runenv, initCtx, gsync, p, dagsrv, networkParams, concurrency, size)
+
+ default:
+ panic("unsupported group ID")
+ }
+}
+
+func parseNetworkConfig(runenv *runtime.RunEnv) []networkParams {
+ var (
+ bandwidths = runenv.SizeArrayParam("bandwidths")
+ latencies []time.Duration
+ )
+
+ lats := runenv.StringArrayParam("latencies")
+ for _, l := range lats {
+ d, err := time.ParseDuration(l)
+ if err != nil {
+ panic(err)
+ }
+ latencies = append(latencies, d)
+ }
+
+ // prepend bandwidth=0 and latency=0 zero values; the first iteration will
+ // be a control iteration. The sidecar interprets zero values as no
+ // limitation on that attribute.
+ bandwidths = append([]uint64{0}, bandwidths...)
+ latencies = append([]time.Duration{0}, latencies...)
+
+ var ret []networkParams
+ for _, bandwidth := range bandwidths {
+ for _, latency := range latencies {
+ ret = append(ret, networkParams{
+ latency: latency,
+ bandwidth: bandwidth,
+ })
+ }
+ }
+ return ret
+}
+
+func runRequestor(ctx context.Context, runenv *runtime.RunEnv, initCtx *run.InitContext, gsync gs.GraphExchange, p peer.AddrInfo, dagsrv format.DAGService, networkParams []networkParams, concurrency int, size uint64) error {
+ var (
+ cids []cid.Cid
+ // create a selector for the whole UnixFS dag
+ sel = allselector.AllSelector
+ )
+
+ for round, np := range networkParams {
+ var (
+ topicCid = sync.NewTopic(fmt.Sprintf("cid-%d", round), []cid.Cid{})
+ stateNext = sync.State(fmt.Sprintf("next-%d", round))
+ stateNet = sync.State(fmt.Sprintf("network-configured-%d", round))
+ )
+
+ // wait for all instances to be ready for the next state.
+ initCtx.SyncClient.MustSignalAndWait(ctx, stateNext, runenv.TestInstanceCount)
+
+ // clean up previous CIDs to attempt to free memory
+ // TODO does this work?
+ _ = dagsrv.RemoveMany(ctx, cids)
+
+ runenv.RecordMessage("===== ROUND %d: latency=%s, bandwidth=%d =====", round, np.latency, np.bandwidth)
+
+ sctx, scancel := context.WithCancel(ctx)
+ cidCh := make(chan []cid.Cid, 1)
+ initCtx.SyncClient.MustSubscribe(sctx, topicCid, cidCh)
+ cids = <-cidCh
+ scancel()
+
+ // run GC to get accurate-ish stats.
+ goruntime.GC()
+ goruntime.GC()
+
+ <-initCtx.SyncClient.MustBarrier(ctx, stateNet, 1).C
+
+ errgrp, grpctx := errgroup.WithContext(ctx)
+ for _, c := range cids {
+ c := c // capture
+ np := np // capture
+
+ errgrp.Go(func() error {
+ // make a go-ipld-prime link for the root UnixFS node
+ clink := cidlink.Link{Cid: c}
+
+ // execute the traversal.
+ runenv.RecordMessage("\t>>> requesting CID %s", c)
+
+ start := time.Now()
+ _, errCh := gsync.Request(grpctx, p.ID, clink, sel)
+ for err := range errCh {
+ return err
+ }
+ dur := time.Since(start)
+
+ runenv.RecordMessage("\t<<< request complete with no errors")
+ runenv.RecordMessage("***** ROUND %d observed duration (lat=%s,bw=%d): %s", round, np.latency, np.bandwidth, dur)
+
+ measurement := fmt.Sprintf("duration.sec,lat=%s,bw=%s,concurrency=%d,size=%s", np.latency, humanize.IBytes(np.bandwidth), concurrency, humanize.Bytes(size))
+ measurement = strings.Replace(measurement, " ", "", -1)
+ runenv.R().RecordPoint(measurement, float64(dur)/float64(time.Second))
+
+ // verify that we have the CID now.
+ if node, err := dagsrv.Get(grpctx, c); err != nil {
+ return err
+ } else if node == nil {
+ return fmt.Errorf("finished graphsync request, but CID not in store")
+ }
+
+ return nil
+ })
+ }
+
+ if err := errgrp.Wait(); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func runProvider(ctx context.Context, runenv *runtime.RunEnv, initCtx *run.InitContext, dagsrv format.DAGService, size uint64, networkParams []networkParams, concurrency int) error {
+ var (
+ cids []cid.Cid
+ bufferedDS = format.NewBufferedDAG(ctx, dagsrv)
+ )
+
+ for round, np := range networkParams {
+ var (
+ topicCid = sync.NewTopic(fmt.Sprintf("cid-%d", round), []cid.Cid{})
+ stateNext = sync.State(fmt.Sprintf("next-%d", round))
+ stateNet = sync.State(fmt.Sprintf("network-configured-%d", round))
+ )
+
+ // wait for all instances to be ready for the next state.
+ initCtx.SyncClient.MustSignalAndWait(ctx, stateNext, runenv.TestInstanceCount)
+
+ // remove the previous CIDs from the dag service; hopefully this
+ // will delete them from the store and free up memory.
+ for _, c := range cids {
+ _ = dagsrv.Remove(ctx, c)
+ }
+ cids = cids[:0]
+
+ runenv.RecordMessage("===== ROUND %d: latency=%s, bandwidth=%d =====", round, np.latency, np.bandwidth)
+
+ // generate as many random files as the concurrency level.
+ for i := 0; i < concurrency; i++ {
+ // file with random data
+ file := files.NewReaderFile(io.LimitReader(rand.Reader, int64(size)))
+
+ const unixfsChunkSize uint64 = 1 << 20
+ const unixfsLinksPerLevel = 1024
+
+ params := ihelper.DagBuilderParams{
+ Maxlinks: unixfsLinksPerLevel,
+ RawLeaves: true,
+ CidBuilder: nil,
+ Dagserv: bufferedDS,
+ }
+
+ db, err := params.New(chunk.NewSizeSplitter(file, int64(unixfsChunkSize)))
+ if err != nil {
+ return fmt.Errorf("unable to setup dag builder: %w", err)
+ }
+
+ node, err := balanced.Layout(db)
+ if err != nil {
+ return fmt.Errorf("unable to create unix fs node: %w", err)
+ }
+
+ cids = append(cids, node.Cid())
+ }
+
+ if err := bufferedDS.Commit(); err != nil {
+ return fmt.Errorf("unable to commit unix fs node: %w", err)
+ }
+
+ // run GC to get accurate-ish stats.
+ goruntime.GC()
+ goruntime.GC()
+
+ runenv.RecordMessage("\tCIDs are: %v", cids)
+ initCtx.SyncClient.MustPublish(ctx, topicCid, cids)
+
+ runenv.RecordMessage("\tconfiguring network for round %d", round)
+ initCtx.NetClient.MustConfigureNetwork(ctx, &network.Config{
+ Network: "default",
+ Enable: true,
+ Default: network.LinkShape{
+ Latency: np.latency,
+ Bandwidth: np.bandwidth * 8, // bps
+ },
+ CallbackState: stateNet,
+ CallbackTarget: 1,
+ })
+ runenv.RecordMessage("\tnetwork configured for round %d", round)
+ }
+
+ return nil
+}
+
+func makeHost(ctx context.Context, runenv *runtime.RunEnv, initCtx *run.InitContext) (host.Host, []*peer.AddrInfo, *metrics.BandwidthCounter) {
+ secureChannel := runenv.StringParam("secure_channel")
+
+ var security libp2p.Option
+ switch secureChannel {
+ case "noise":
+ security = libp2p.Security(noise.ID, noise.New)
+ case "secio":
+ security = libp2p.Security(secio.ID, secio.New)
+ case "tls":
+ security = libp2p.Security(tls.ID, tls.New)
+ }
+
+ // ☎️ Let's construct the libp2p node.
+ ip := initCtx.NetClient.MustGetDataNetworkIP()
+ listenAddr := fmt.Sprintf("/ip4/%s/tcp/0", ip)
+ bwcounter := metrics.NewBandwidthCounter()
+ host, err := libp2p.New(ctx,
+ security,
+ libp2p.ListenAddrStrings(listenAddr),
+ libp2p.BandwidthReporter(bwcounter),
+ )
+ if err != nil {
+ panic(fmt.Sprintf("failed to instantiate libp2p instance: %s", err))
+ }
+
+ // Record our listen addrs.
+ runenv.RecordMessage("my listen addrs: %v", host.Addrs())
+
+ // Obtain our own address info, and use the sync service to publish it to a
+ // 'peersTopic' topic, where others will read from.
+ var (
+ id = host.ID()
+ ai = &peer.AddrInfo{ID: id, Addrs: host.Addrs()}
+
+ // the peers topic where all instances will advertise their AddrInfo.
+ peersTopic = sync.NewTopic("peers", new(peer.AddrInfo))
+
+ // initialize a slice to store the AddrInfos of all other peers in the run.
+ peers = make([]*peer.AddrInfo, 0, runenv.TestInstanceCount-1)
+ )
+
+ // Publish our own.
+ initCtx.SyncClient.MustPublish(ctx, peersTopic, ai)
+
+ // Now subscribe to the peers topic and consume all addresses, storing them
+ // in the peers slice.
+ peersCh := make(chan *peer.AddrInfo)
+ sctx, scancel := context.WithCancel(ctx)
+ defer scancel()
+
+ sub := initCtx.SyncClient.MustSubscribe(sctx, peersTopic, peersCh)
+
+ // Receive the expected number of AddrInfos.
+ for len(peers) < cap(peers) {
+ select {
+ case ai := <-peersCh:
+ if ai.ID == id {
+ continue // skip over ourselves.
+ }
+ peers = append(peers, ai)
+ case err := <-sub.Done():
+ panic(err)
+ }
+ }
+
+ return host, peers, bwcounter
+}
diff --git a/testplans/graphsync/manifest.toml b/testplans/graphsync/manifest.toml
new file mode 100644
index 000000000..87803474d
--- /dev/null
+++ b/testplans/graphsync/manifest.toml
@@ -0,0 +1,24 @@
+name = "graphsync"
+
+[builders]
+"docker:go" = { enabled = true, enable_go_build_cache = true }
+"exec:go" = { enabled = true }
+
+[runners]
+"local:docker" = { enabled = true }
+"local:exec" = { enabled = true }
+"cluster:k8s" = { enabled = true }
+
+[global.build_config]
+ enable_go_build_cache = true
+
+[[testcases]]
+name = "stress"
+instances = { min = 2, max = 10000, default = 2 }
+
+ [testcases.params]
+ size = { type = "int", desc = "size of file to transfer, in human-friendly form", default = "1MiB" }
+ secure_channel = { type = "enum", desc = "secure channel used", values = ["secio", "noise", "tls"], default = "noise" }
+ latencies = { type = "string", desc = "latencies to try with; comma-separated list of durations", default = '["100ms", "200ms", "300ms"]' }
+ bandwidths = { type = "string", desc = "bandwidths (egress bytes/s) to try with; comma-separated list of humanized sizes", default = '["10M", "1M", "512kb"]' }
+ concurrency = { type = "int", desc = "concurrency level", default = "1" }
\ No newline at end of file
diff --git a/testplans/lotus-soup/.gitignore b/testplans/lotus-soup/.gitignore
new file mode 100644
index 000000000..001a5837b
--- /dev/null
+++ b/testplans/lotus-soup/.gitignore
@@ -0,0 +1 @@
+lotus-soup
diff --git a/testplans/lotus-soup/_compositions/baseline-docker-5-1.toml b/testplans/lotus-soup/_compositions/baseline-docker-5-1.toml
new file mode 100644
index 000000000..001a02434
--- /dev/null
+++ b/testplans/lotus-soup/_compositions/baseline-docker-5-1.toml
@@ -0,0 +1,55 @@
+[metadata]
+ name = "lotus-soup"
+ author = ""
+
+[global]
+ plan = "lotus-soup"
+ case = "deals-e2e"
+ total_instances = 7
+ builder = "docker:go"
+ runner = "local:docker"
+
+[global.build]
+ selectors = ["testground"]
+
+[global.run_config]
+ exposed_ports = { pprof = "6060", node_rpc = "1234", miner_rpc = "2345" }
+
+[global.build_config]
+ enable_go_build_cache = true
+
+[global.run.test_params]
+ clients = "5"
+ miners = "1"
+ genesis_timestamp_offset = "0"
+ balance = "20000000" # These balances will work for maximum 100 nodes, as TotalFilecoin is 2B
+ sectors = "5"
+ random_beacon_type = "mock"
+ mining_mode = "natural"
+
+[[groups]]
+ id = "bootstrapper"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "bootstrapper"
+
+[[groups]]
+ id = "miners"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "miner"
+
+[[groups]]
+ id = "clients"
+ [groups.instances]
+ count = 5
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "client"
diff --git a/testplans/lotus-soup/_compositions/baseline-k8s-10-3.toml b/testplans/lotus-soup/_compositions/baseline-k8s-10-3.toml
new file mode 100644
index 000000000..4afd3375e
--- /dev/null
+++ b/testplans/lotus-soup/_compositions/baseline-k8s-10-3.toml
@@ -0,0 +1,80 @@
+[metadata]
+ name = "lotus-soup"
+ author = ""
+
+[global]
+ plan = "lotus-soup"
+ case = "deals-e2e"
+ total_instances = 14
+ builder = "docker:go"
+ runner = "cluster:k8s"
+
+[global.build]
+ selectors = ["testground"]
+
+[global.run_config]
+ exposed_ports = { pprof = "6060", node_rpc = "1234", miner_rpc = "2345" }
+
+[global.build_config]
+ push_registry=true
+ go_proxy_mode="remote"
+ go_proxy_url="http://localhost:8081"
+ registry_type="aws"
+
+[global.run.test_params]
+ clients = "10"
+ miners = "3"
+ genesis_timestamp_offset = "0"
+ balance = "20000000" # These balances will work for maximum 100 nodes, as TotalFilecoin is 2B
+ random_beacon_type = "mock"
+ mining_mode = "natural"
+
+[[groups]]
+ id = "bootstrapper"
+ [groups.resources]
+ memory = "512Mi"
+ cpu = "1000m"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "bootstrapper"
+
+[[groups]]
+ id = "miners-weak"
+ [groups.resources]
+ memory = "8192Mi"
+ cpu = "1000m"
+ [groups.instances]
+ count = 2
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "miner"
+ sectors = "8"
+
+[[groups]]
+ id = "miners-strong"
+ [groups.resources]
+ memory = "8192Mi"
+ cpu = "1000m"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "miner"
+ sectors = "24"
+
+[[groups]]
+ id = "clients"
+ [groups.resources]
+ memory = "1024Mi"
+ cpu = "1000m"
+ [groups.instances]
+ count = 10
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "client"
diff --git a/testplans/lotus-soup/_compositions/baseline-k8s-3-1.toml b/testplans/lotus-soup/_compositions/baseline-k8s-3-1.toml
new file mode 100644
index 000000000..18ce024bb
--- /dev/null
+++ b/testplans/lotus-soup/_compositions/baseline-k8s-3-1.toml
@@ -0,0 +1,67 @@
+[metadata]
+ name = "lotus-soup"
+ author = ""
+
+[global]
+ plan = "lotus-soup"
+ case = "deals-e2e"
+ total_instances = 5
+ builder = "docker:go"
+ runner = "cluster:k8s"
+
+[global.build]
+ selectors = ["testground"]
+
+[global.run_config]
+ exposed_ports = { pprof = "6060", node_rpc = "1234", miner_rpc = "2345" }
+
+[global.build_config]
+ push_registry=true
+ go_proxy_mode="remote"
+ go_proxy_url="http://localhost:8081"
+ registry_type="aws"
+
+[global.run.test_params]
+ clients = "3"
+ miners = "1"
+ genesis_timestamp_offset = "0"
+ balance = "20000000" # These balances will work for maximum 100 nodes, as TotalFilecoin is 2B
+ sectors = "10"
+ random_beacon_type = "mock"
+ mining_mode = "natural"
+
+[[groups]]
+ id = "bootstrapper"
+ [groups.resources]
+ memory = "512Mi"
+ cpu = "1000m"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "bootstrapper"
+
+[[groups]]
+ id = "miners"
+ [groups.resources]
+ memory = "4096Mi"
+ cpu = "1000m"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "miner"
+
+[[groups]]
+ id = "clients"
+ [groups.resources]
+ memory = "1024Mi"
+ cpu = "1000m"
+ [groups.instances]
+ count = 3
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "client"
diff --git a/testplans/lotus-soup/_compositions/baseline-k8s-3-2.toml b/testplans/lotus-soup/_compositions/baseline-k8s-3-2.toml
new file mode 100644
index 000000000..98d581c9d
--- /dev/null
+++ b/testplans/lotus-soup/_compositions/baseline-k8s-3-2.toml
@@ -0,0 +1,67 @@
+[metadata]
+ name = "lotus-soup"
+ author = ""
+
+[global]
+ plan = "lotus-soup"
+ case = "deals-e2e"
+ total_instances = 6
+ builder = "docker:go"
+ runner = "cluster:k8s"
+
+[global.build]
+ selectors = ["testground"]
+
+[global.run_config]
+ exposed_ports = { pprof = "6060", node_rpc = "1234", miner_rpc = "2345" }
+
+[global.build_config]
+ push_registry=true
+ go_proxy_mode="remote"
+ go_proxy_url="http://localhost:8081"
+ registry_type="aws"
+
+[global.run.test_params]
+ clients = "3"
+ miners = "2"
+ genesis_timestamp_offset = "0"
+ balance = "20000000" # These balances will work for maximum 100 nodes, as TotalFilecoin is 2B
+ sectors = "10"
+ random_beacon_type = "mock"
+ mining_mode = "natural"
+
+[[groups]]
+ id = "bootstrapper"
+ [groups.resources]
+ memory = "512Mi"
+ cpu = "1000m"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "bootstrapper"
+
+[[groups]]
+ id = "miners"
+ [groups.resources]
+ memory = "4096Mi"
+ cpu = "1000m"
+ [groups.instances]
+ count = 2
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "miner"
+
+[[groups]]
+ id = "clients"
+ [groups.resources]
+ memory = "1024Mi"
+ cpu = "1000m"
+ [groups.instances]
+ count = 3
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "client"
diff --git a/testplans/lotus-soup/_compositions/baseline.toml b/testplans/lotus-soup/_compositions/baseline.toml
new file mode 100644
index 000000000..be035b6eb
--- /dev/null
+++ b/testplans/lotus-soup/_compositions/baseline.toml
@@ -0,0 +1,55 @@
+[metadata]
+ name = "lotus-soup"
+ author = ""
+
+[global]
+ plan = "lotus-soup"
+ case = "deals-e2e"
+ total_instances = 6
+ builder = "exec:go"
+ runner = "local:exec"
+
+[global.build]
+ selectors = ["testground"]
+
+[global.run_config]
+ exposed_ports = { pprof = "6060", node_rpc = "1234", miner_rpc = "2345" }
+
+[global.build_config]
+ enable_go_build_cache = true
+
+[global.run.test_params]
+ clients = "3"
+ miners = "2"
+ genesis_timestamp_offset = "0"
+ balance = "20000000.5" # These balances will work for maximum 100 nodes, as TotalFilecoin is 2B
+ sectors = "10"
+ random_beacon_type = "mock"
+ mining_mode = "natural"
+
+[[groups]]
+ id = "bootstrapper"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "bootstrapper"
+
+[[groups]]
+ id = "miners"
+ [groups.instances]
+ count = 2
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "miner"
+
+[[groups]]
+ id = "clients"
+ [groups.instances]
+ count = 3
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "client"
diff --git a/testplans/lotus-soup/_compositions/deals-stress-concurrent-natural-k8s.toml b/testplans/lotus-soup/_compositions/deals-stress-concurrent-natural-k8s.toml
new file mode 100644
index 000000000..18f14407f
--- /dev/null
+++ b/testplans/lotus-soup/_compositions/deals-stress-concurrent-natural-k8s.toml
@@ -0,0 +1,69 @@
+[metadata]
+ name = "lotus-soup"
+ author = ""
+
+[global]
+ plan = "lotus-soup"
+ case = "deals-stress"
+ total_instances = 6
+ builder = "docker:go"
+ runner = "cluster:k8s"
+
+[global.build]
+ selectors = ["testground"]
+
+[global.run_config]
+ exposed_ports = { pprof = "6060", node_rpc = "1234", miner_rpc = "2345" }
+
+[global.build_config]
+ push_registry=true
+ go_proxy_mode="remote"
+ go_proxy_url="http://localhost:8081"
+ registry_type="aws"
+
+[global.run.test_params]
+ clients = "3"
+ miners = "2"
+ genesis_timestamp_offset = "0"
+ balance = "90000000" # These balances will work for maximum 100 nodes, as TotalFilecoin is 2B
+ sectors = "10"
+ random_beacon_type = "mock"
+ mining_mode = "natural"
+
+[[groups]]
+ id = "bootstrapper"
+ [groups.resources]
+ memory = "512Mi"
+ cpu = "100m"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "bootstrapper"
+
+[[groups]]
+ id = "miners"
+ [groups.resources]
+ memory = "14000Mi"
+ cpu = "1000m"
+ [groups.instances]
+ count = 2
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "miner"
+
+[[groups]]
+ id = "clients"
+ [groups.resources]
+ memory = "2048Mi"
+ cpu = "100m"
+ [groups.instances]
+ count = 3
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "client"
+ deals = "3"
+ deal_mode = "concurrent"
diff --git a/testplans/lotus-soup/_compositions/deals-stress-concurrent-natural.toml b/testplans/lotus-soup/_compositions/deals-stress-concurrent-natural.toml
new file mode 100644
index 000000000..cc3ab45fc
--- /dev/null
+++ b/testplans/lotus-soup/_compositions/deals-stress-concurrent-natural.toml
@@ -0,0 +1,57 @@
+[metadata]
+ name = "lotus-soup"
+ author = ""
+
+[global]
+ plan = "lotus-soup"
+ case = "deals-stress"
+ total_instances = 6
+ builder = "docker:go"
+ runner = "local:docker"
+
+[global.build]
+ selectors = ["testground"]
+
+[global.run_config]
+ exposed_ports = { pprof = "6060", node_rpc = "1234", miner_rpc = "2345" }
+
+[global.build_config]
+ enable_go_build_cache = true
+
+[global.run.test_params]
+ clients = "3"
+ miners = "2"
+ genesis_timestamp_offset = "0"
+ balance = "20000000" # These balances will work for maximum 100 nodes, as TotalFilecoin is 2B
+ sectors = "1000"
+ random_beacon_type = "mock"
+
+[[groups]]
+ id = "bootstrapper"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "bootstrapper"
+
+[[groups]]
+ id = "miners"
+ [groups.instances]
+ count = 2
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "miner"
+ mining_mode = "natural"
+
+[[groups]]
+ id = "clients"
+ [groups.instances]
+ count = 3
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "client"
+ deals = "300"
+ deal_mode = "concurrent"
diff --git a/testplans/lotus-soup/_compositions/deals-stress-concurrent.toml b/testplans/lotus-soup/_compositions/deals-stress-concurrent.toml
new file mode 100644
index 000000000..bec530c42
--- /dev/null
+++ b/testplans/lotus-soup/_compositions/deals-stress-concurrent.toml
@@ -0,0 +1,56 @@
+[metadata]
+ name = "lotus-soup"
+ author = ""
+
+[global]
+ plan = "lotus-soup"
+ case = "deals-stress"
+ total_instances = 6
+ builder = "docker:go"
+ runner = "local:docker"
+
+[global.build]
+ selectors = ["testground"]
+
+[global.run_config]
+ exposed_ports = { pprof = "6060", node_rpc = "1234", miner_rpc = "2345" }
+
+[global.build_config]
+ enable_go_build_cache = true
+
+[global.run.test_params]
+ clients = "3"
+ miners = "2"
+ genesis_timestamp_offset = "100000"
+ balance = "20000000" # These balances will work for maximum 100 nodes, as TotalFilecoin is 2B
+ sectors = "1000"
+ random_beacon_type = "mock"
+
+[[groups]]
+ id = "bootstrapper"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "bootstrapper"
+
+[[groups]]
+ id = "miners"
+ [groups.instances]
+ count = 2
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "miner"
+
+[[groups]]
+ id = "clients"
+ [groups.instances]
+ count = 3
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "client"
+ deals = "300"
+ deal_mode = "concurrent"
diff --git a/testplans/lotus-soup/_compositions/deals-stress-serial-natural.toml b/testplans/lotus-soup/_compositions/deals-stress-serial-natural.toml
new file mode 100644
index 000000000..c5611a3e8
--- /dev/null
+++ b/testplans/lotus-soup/_compositions/deals-stress-serial-natural.toml
@@ -0,0 +1,57 @@
+[metadata]
+ name = "lotus-soup"
+ author = ""
+
+[global]
+ plan = "lotus-soup"
+ case = "deals-stress"
+ total_instances = 6
+ builder = "docker:go"
+ runner = "local:docker"
+
+[global.build]
+ selectors = ["testground"]
+
+[global.run_config]
+ exposed_ports = { pprof = "6060", node_rpc = "1234", miner_rpc = "2345" }
+
+[global.build_config]
+ enable_go_build_cache = true
+
+[global.run.test_params]
+ clients = "3"
+ miners = "2"
+ genesis_timestamp_offset = "0"
+ balance = "20000000" # These balances will work for maximum 100 nodes, as TotalFilecoin is 2B
+ sectors = "1000"
+ random_beacon_type = "mock"
+
+[[groups]]
+ id = "bootstrapper"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "bootstrapper"
+
+[[groups]]
+ id = "miners"
+ [groups.instances]
+ count = 2
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "miner"
+ mining_mode = "natural"
+
+[[groups]]
+ id = "clients"
+ [groups.instances]
+ count = 3
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "client"
+ deals = "300"
+ deal_mode = "serial"
diff --git a/testplans/lotus-soup/_compositions/deals-stress-serial.toml b/testplans/lotus-soup/_compositions/deals-stress-serial.toml
new file mode 100644
index 000000000..5317fecb9
--- /dev/null
+++ b/testplans/lotus-soup/_compositions/deals-stress-serial.toml
@@ -0,0 +1,56 @@
+[metadata]
+ name = "lotus-soup"
+ author = ""
+
+[global]
+ plan = "lotus-soup"
+ case = "deals-stress"
+ total_instances = 6
+ builder = "docker:go"
+ runner = "local:docker"
+
+[global.build]
+ selectors = ["testground"]
+
+[global.run_config]
+ exposed_ports = { pprof = "6060", node_rpc = "1234", miner_rpc = "2345" }
+
+[global.build_config]
+ enable_go_build_cache = true
+
+[global.run.test_params]
+ clients = "3"
+ miners = "2"
+ genesis_timestamp_offset = "100000"
+ balance = "20000000" # These balances will work for maximum 100 nodes, as TotalFilecoin is 2B
+ sectors = "1000"
+ random_beacon_type = "mock"
+
+[[groups]]
+ id = "bootstrapper"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "bootstrapper"
+
+[[groups]]
+ id = "miners"
+ [groups.instances]
+ count = 2
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "miner"
+
+[[groups]]
+ id = "clients"
+ [groups.instances]
+ count = 3
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "client"
+ deals = "300"
+ deal_mode = "serial"
diff --git a/testplans/lotus-soup/_compositions/drand-halt.toml b/testplans/lotus-soup/_compositions/drand-halt.toml
new file mode 100644
index 000000000..ce2610dc1
--- /dev/null
+++ b/testplans/lotus-soup/_compositions/drand-halt.toml
@@ -0,0 +1,79 @@
+[metadata]
+ name = "lotus-soup"
+ author = ""
+
+[global]
+ plan = "lotus-soup"
+ case = "drand-halting"
+ total_instances = 6
+ builder = "docker:go"
+ runner = "local:docker"
+
+[global.build]
+ selectors = ["testground"]
+
+[global.run_config]
+ exposed_ports = { pprof = "6060", node_rpc = "1234", miner_rpc = "2345" }
+
+[global.build_config]
+ enable_go_build_cache = true
+
+[global.run.test_params]
+ clients = "1"
+ miners = "1"
+ balance = "20000000" # These balances will work for maximum 100 nodes, as TotalFilecoin is 2B
+ sectors = "10"
+ random_beacon_type = "local-drand"
+ genesis_timestamp_offset = "0"
+# mining_mode = "natural"
+
+[[groups]]
+ id = "bootstrapper"
+ [groups.resources]
+ memory = "120Mi"
+ cpu = "10m"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "bootstrapper"
+
+
+[[groups]]
+ id = "miners"
+ [groups.resources]
+ memory = "120Mi"
+ cpu = "10m"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "miner"
+
+
+[[groups]]
+ id = "clients"
+ [groups.resources]
+ memory = "120Mi"
+ cpu = "10m"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "client"
+
+
+[[groups]]
+ id = "drand"
+ [groups.instances]
+ count = 3
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "drand"
+ drand_period = "1s"
+ drand_log_level = "none"
+ suspend_events = "wait 20s -> halt -> wait 1m -> resume -> wait 2s -> halt -> wait 1m -> resume"
diff --git a/testplans/lotus-soup/_compositions/drand-outage-k8s.toml b/testplans/lotus-soup/_compositions/drand-outage-k8s.toml
new file mode 100644
index 000000000..0588adb0b
--- /dev/null
+++ b/testplans/lotus-soup/_compositions/drand-outage-k8s.toml
@@ -0,0 +1,71 @@
+[metadata]
+ name = "lotus-soup"
+ author = ""
+
+[global]
+ plan = "lotus-soup"
+ case = "drand-outage"
+ total_instances = 7
+ builder = "docker:go"
+ runner = "cluster:k8s"
+
+[global.build]
+ selectors = ["testground"]
+
+[global.run_config]
+ exposed_ports = { pprof = "6060", node_rpc = "1234", miner_rpc = "2345" }
+
+[global.build_config]
+ push_registry=true
+ go_proxy_mode="remote"
+ go_proxy_url="http://localhost:8081"
+ registry_type="aws"
+
+[global.run.test_params]
+ clients = "0"
+ miners = "3"
+ balance = "20000000" # These balances will work for maximum 100 nodes, as TotalFilecoin is 2B
+ sectors = "100"
+ random_beacon_type = "local-drand"
+ genesis_timestamp_offset = "0"
+ mining_mode = "natural"
+
+[[groups]]
+ id = "bootstrapper"
+ [groups.resources]
+ memory = "1024Mi"
+ cpu = "10m"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "bootstrapper"
+
+[[groups]]
+ id = "miners"
+ [groups.resources]
+ memory = "1024Mi"
+ cpu = "10m"
+ [groups.instances]
+ count = 3
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "miner"
+
+[[groups]]
+ id = "drand"
+ [groups.resources]
+ memory = "1024Mi"
+ cpu = "10m"
+ [groups.instances]
+ count = 3
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "drand"
+ drand_period = "30s"
+ drand_catchup_period = "10s"
+ drand_log_level = "debug"
+ suspend_events = "wait 5m -> halt -> wait 45m -> resume -> wait 15m -> halt -> wait 5m -> resume"
diff --git a/testplans/lotus-soup/_compositions/drand-outage-local.toml b/testplans/lotus-soup/_compositions/drand-outage-local.toml
new file mode 100644
index 000000000..dad81a203
--- /dev/null
+++ b/testplans/lotus-soup/_compositions/drand-outage-local.toml
@@ -0,0 +1,59 @@
+[metadata]
+ name = "lotus-soup"
+ author = ""
+
+[global]
+ plan = "lotus-soup"
+ case = "drand-outage"
+ total_instances = 7
+ builder = "docker:go"
+ runner = "local:docker"
+
+[global.build]
+ selectors = ["testground"]
+
+[global.run_config]
+ exposed_ports = { pprof = "6060", node_rpc = "1234", miner_rpc = "2345" }
+
+[global.build_config]
+ enable_go_build_cache = true
+
+[global.run.test_params]
+ clients = "0"
+ miners = "3"
+ balance = "20000000" # These balances will work for maximum 100 nodes, as TotalFilecoin is 2B
+ sectors = "10"
+ random_beacon_type = "local-drand"
+ genesis_timestamp_offset = "0"
+ mining_mode = "natural"
+
+[[groups]]
+ id = "bootstrapper"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "bootstrapper"
+
+[[groups]]
+ id = "miners"
+ [groups.instances]
+ count = 3
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "miner"
+
+[[groups]]
+ id = "drand"
+ [groups.instances]
+ count = 3
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "drand"
+ drand_period = "30s"
+ drand_catchup_period = "10s"
+ drand_log_level = "debug"
+ suspend_events = "wait 3m -> halt -> wait 3m -> resume -> wait 3m -> halt -> wait 3m -> resume"
diff --git a/testplans/lotus-soup/_compositions/fast-k8s-3-1.toml b/testplans/lotus-soup/_compositions/fast-k8s-3-1.toml
new file mode 100644
index 000000000..d77bfbc3e
--- /dev/null
+++ b/testplans/lotus-soup/_compositions/fast-k8s-3-1.toml
@@ -0,0 +1,68 @@
+[metadata]
+ name = "lotus-soup"
+ author = ""
+
+[global]
+ plan = "lotus-soup"
+ case = "deals-e2e"
+ total_instances = 5
+ builder = "docker:go"
+ runner = "cluster:k8s"
+
+[global.build]
+ selectors = ["testground"]
+
+[global.run_config]
+ exposed_ports = { pprof = "6060", node_rpc = "1234", miner_rpc = "2345" }
+
+[global.build_config]
+ push_registry=true
+ go_proxy_mode="remote"
+ go_proxy_url="http://localhost:8081"
+ registry_type="aws"
+
+[global.run.test_params]
+ clients = "3"
+ miners = "1"
+ fast_retrieval = "true"
+ genesis_timestamp_offset = "0"
+ balance = "20000000" # These balances will work for maximum 100 nodes, as TotalFilecoin is 2B
+ sectors = "10"
+ random_beacon_type = "mock"
+ mining_mode = "natural"
+
+[[groups]]
+ id = "bootstrapper"
+ [groups.resources]
+ memory = "512Mi"
+ cpu = "1000m"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "bootstrapper"
+
+[[groups]]
+ id = "miners"
+ [groups.resources]
+ memory = "4096Mi"
+ cpu = "1000m"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "miner"
+
+[[groups]]
+ id = "clients"
+ [groups.resources]
+ memory = "1024Mi"
+ cpu = "1000m"
+ [groups.instances]
+ count = 3
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "client"
diff --git a/testplans/lotus-soup/_compositions/local-drand.toml b/testplans/lotus-soup/_compositions/local-drand.toml
new file mode 100644
index 000000000..e942ed072
--- /dev/null
+++ b/testplans/lotus-soup/_compositions/local-drand.toml
@@ -0,0 +1,72 @@
+[metadata]
+ name = "lotus-soup"
+ author = ""
+
+[global]
+ plan = "lotus-soup"
+ case = "deals-e2e"
+ total_instances = 6
+ builder = "docker:go"
+ runner = "local:docker"
+
+[global.build]
+ selectors = ["testground"]
+
+[global.run_config]
+ exposed_ports = { pprof = "6060", node_rpc = "1234", miner_rpc = "2345" }
+
+[global.build_config]
+ enable_go_build_cache = true
+
+[global.run.test_params]
+ clients = "1"
+ miners = "1"
+ balance = "20000000" # These balances will work for maximum 100 nodes, as TotalFilecoin is 2B
+ sectors = "10"
+ random_beacon_type = "local-drand"
+ genesis_timestamp_offset = "0"
+
+[[groups]]
+ id = "bootstrapper"
+ [groups.resources]
+ memory = "120Mi"
+ cpu = "10m"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "bootstrapper"
+
+[[groups]]
+ id = "miners"
+ [groups.resources]
+ memory = "120Mi"
+ cpu = "10m"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "miner"
+
+[[groups]]
+ id = "clients"
+ [groups.resources]
+ memory = "120Mi"
+ cpu = "10m"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "client"
+
+[[groups]]
+ id = "drand"
+ [groups.instances]
+ count = 3
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "drand"
diff --git a/testplans/lotus-soup/_compositions/natural.toml b/testplans/lotus-soup/_compositions/natural.toml
new file mode 100644
index 000000000..bfef6b844
--- /dev/null
+++ b/testplans/lotus-soup/_compositions/natural.toml
@@ -0,0 +1,55 @@
+[metadata]
+ name = "lotus-soup"
+ author = ""
+
+[global]
+ plan = "lotus-soup"
+ case = "deals-e2e"
+ total_instances = 6
+ builder = "docker:go"
+ runner = "local:docker"
+
+[global.build]
+ selectors = ["testground"]
+
+[global.run_config]
+ exposed_ports = { pprof = "6060", node_rpc = "1234", miner_rpc = "2345" }
+
+[global.build_config]
+ enable_go_build_cache = true
+
+[global.run.test_params]
+ clients = "3"
+ miners = "2"
+ genesis_timestamp_offset = "100000"
+ balance = "20000000" # These balances will work for maximum 100 nodes, as TotalFilecoin is 2B
+ sectors = "10"
+ random_beacon_type = "mock"
+
+[[groups]]
+ id = "bootstrapper"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "bootstrapper"
+
+[[groups]]
+ id = "miners"
+ [groups.instances]
+ count = 2
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "miner"
+ mining_mode = "natural"
+
+[[groups]]
+ id = "clients"
+ [groups.instances]
+ count = 3
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "client"
diff --git a/testplans/lotus-soup/_compositions/net-chaos/latency.toml b/testplans/lotus-soup/_compositions/net-chaos/latency.toml
new file mode 100644
index 000000000..fb5f5f544
--- /dev/null
+++ b/testplans/lotus-soup/_compositions/net-chaos/latency.toml
@@ -0,0 +1,57 @@
+[metadata]
+ name = "lotus-soup"
+ author = ""
+
+[global]
+ plan = "lotus-soup"
+ case = "deals-e2e"
+ total_instances = 7
+ builder = "docker:go"
+ runner = "local:docker"
+
+[global.build]
+ selectors = ["testground"]
+
+[global.run_config]
+ exposed_ports = { pprof = "6060", node_rpc = "1234", miner_rpc = "2345" }
+
+[global.build_config]
+ enable_go_build_cache = true
+
+[global.run.test_params]
+ clients = "5"
+ miners = "1"
+ genesis_timestamp_offset = "0"
+ balance = "20000000" # These balances will work for maximum 100 nodes, as TotalFilecoin is 2B
+ sectors = "5"
+ random_beacon_type = "mock"
+ mining_mode = "natural"
+
+[[groups]]
+ id = "bootstrapper"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "bootstrapper"
+
+[[groups]]
+ id = "miners"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "miner"
+ latency_range = '["20ms", "300ms"]'
+
+[[groups]]
+ id = "clients"
+ [groups.instances]
+ count = 5
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "client"
+ latency_range = '["100ms", "1500ms"]'
diff --git a/testplans/lotus-soup/_compositions/paych-stress-k8s.toml b/testplans/lotus-soup/_compositions/paych-stress-k8s.toml
new file mode 100644
index 000000000..cf98960b7
--- /dev/null
+++ b/testplans/lotus-soup/_compositions/paych-stress-k8s.toml
@@ -0,0 +1,62 @@
+[metadata]
+ name = "lotus-soup"
+ author = "raulk"
+
+[global]
+ plan = "lotus-soup"
+ case = "paych-stress"
+ total_instances = 5 # 2 clients + 2 miners + 1 bootstrapper
+ builder = "docker:go"
+ runner = "cluster:k8s"
+
+[global.build]
+ selectors = ["testground"]
+
+[global.run_config]
+ exposed_ports = { pprof = "6060", node_rpc = "1234", miner_rpc = "2345" }
+
+[global.build_config]
+ push_registry=true
+ go_proxy_mode="remote"
+ go_proxy_url="http://localhost:8081"
+ registry_type="aws"
+
+[global.run.test_params]
+ clients = "2"
+ miners = "2"
+ genesis_timestamp_offset = "0"
+ balance = "100" ## be careful, this is in FIL.
+ sectors = "10"
+ random_beacon_type = "mock"
+ mining_mode = "natural"
+ # number of lanes to send vouchers on
+ lane_count = "8"
+ # number of vouchers on each lane
+ vouchers_per_lane = "3"
+ # amount to increase voucher by each time (per lane)
+ increments = "3" ## in FIL
+
+[[groups]]
+ id = "bootstrapper"
+ instances = { count = 1 }
+ [groups.run.test_params]
+ role = "bootstrapper"
+
+[[groups]]
+ id = "miners"
+ instances = { count = 2 }
+ [groups.run.test_params]
+ role = "miner"
+ [groups.resources]
+ memory = "2048Mi"
+ cpu = "100m"
+
+[[groups]]
+ id = "clients"
+ # the first client will be on the receiving end; all others will be on the sending end.
+ instances = { count = 2 }
+ [groups.run.test_params]
+ role = "client"
+ [groups.resources]
+ memory = "1024Mi"
+ cpu = "100m"
diff --git a/testplans/lotus-soup/_compositions/paych-stress.toml b/testplans/lotus-soup/_compositions/paych-stress.toml
new file mode 100644
index 000000000..b42721a7b
--- /dev/null
+++ b/testplans/lotus-soup/_compositions/paych-stress.toml
@@ -0,0 +1,53 @@
+[metadata]
+ name = "lotus-soup"
+ author = "raulk"
+
+[global]
+ plan = "lotus-soup"
+ case = "paych-stress"
+ total_instances = 5 # 2 clients + 2 miners + 1 bootstrapper
+ builder = "exec:go"
+ runner = "local:exec"
+
+[global.build]
+ selectors = ["testground"]
+
+[global.run_config]
+ exposed_ports = { pprof = "6060", node_rpc = "1234", miner_rpc = "2345" }
+
+[global.build_config]
+ enable_go_build_cache = true
+
+[global.run.test_params]
+ clients = "2"
+ miners = "2"
+ genesis_timestamp_offset = "0"
+ balance = "100" ## be careful, this is in FIL.
+ sectors = "10"
+ random_beacon_type = "mock"
+ mining_mode = "natural"
+ # number of lanes to send vouchers on
+ lane_count = "8"
+ # number of vouchers on each lane
+ vouchers_per_lane = "3"
+ # amount to increase voucher by each time (per lane)
+ increments = "3" ## in FIL
+
+[[groups]]
+ id = "bootstrapper"
+ instances = { count = 1 }
+ [groups.run.test_params]
+ role = "bootstrapper"
+
+[[groups]]
+ id = "miners"
+ instances = { count = 2 }
+ [groups.run.test_params]
+ role = "miner"
+
+[[groups]]
+ id = "clients"
+ # the first client will be on the receiving end; all others will be on the sending end.
+ instances = { count = 2 }
+ [groups.run.test_params]
+ role = "client"
diff --git a/testplans/lotus-soup/_compositions/pubsub-tracer.toml b/testplans/lotus-soup/_compositions/pubsub-tracer.toml
new file mode 100644
index 000000000..db9f111d1
--- /dev/null
+++ b/testplans/lotus-soup/_compositions/pubsub-tracer.toml
@@ -0,0 +1,64 @@
+[metadata]
+ name = "lotus-soup"
+ author = ""
+
+[global]
+ plan = "lotus-soup"
+ case = "deals-e2e"
+ total_instances = 7
+ builder = "docker:go"
+ runner = "local:docker"
+
+[global.build]
+ selectors = ["testground"]
+
+[global.run_config]
+ exposed_ports = { pprof = "6060", node_rpc = "1234", miner_rpc = "2345" }
+
+[global.build_config]
+ enable_go_build_cache = true
+
+[global.run.test_params]
+ clients = "3"
+ miners = "2"
+ genesis_timestamp_offset = "100000"
+ balance = "20000000" # These balances will work for maximum 100 nodes, as TotalFilecoin is 2B
+ sectors = "10"
+ random_beacon_type = "mock"
+ enable_pubsub_tracer = "true"
+
+[[groups]]
+ id = "pubsub-tracer"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "pubsub-tracer"
+
+[[groups]]
+ id = "bootstrapper"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "bootstrapper"
+
+[[groups]]
+ id = "miners"
+ [groups.instances]
+ count = 2
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "miner"
+
+[[groups]]
+ id = "clients"
+ [groups.instances]
+ count = 3
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "client"
diff --git a/testplans/lotus-soup/_compositions/recovery-exec.toml b/testplans/lotus-soup/_compositions/recovery-exec.toml
new file mode 100644
index 000000000..8e9ef9d6e
--- /dev/null
+++ b/testplans/lotus-soup/_compositions/recovery-exec.toml
@@ -0,0 +1,80 @@
+[metadata]
+ name = "lotus-soup"
+ author = ""
+
+[global]
+ plan = "lotus-soup"
+ case = "recovery-failed-windowed-post"
+ total_instances = 7
+ builder = "exec:go"
+ runner = "local:exec"
+
+[global.build]
+ selectors = ["testground"]
+
+[global.run_config]
+ exposed_ports = { pprof = "6060", node_rpc = "1234", miner_rpc = "2345" }
+
+[global.build_config]
+ push_registry=true
+ go_proxy_mode="remote"
+ go_proxy_url="http://localhost:8081"
+ registry_type="aws"
+
+[global.run.test_params]
+ clients = "3"
+ miners = "3"
+ genesis_timestamp_offset = "0"
+ balance = "20000000"
+
+[[groups]]
+ id = "bootstrapper"
+ [groups.resources]
+ memory = "512Mi"
+ cpu = "1000m"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "bootstrapper"
+
+[[groups]]
+ id = "miners"
+ [groups.resources]
+ memory = "4096Mi"
+ cpu = "1000m"
+ [groups.instances]
+ count = 2
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "miner"
+ sectors = "10"
+ mining_mode = "natural"
+
+[[groups]]
+ id = "miners-biserk"
+ [groups.resources]
+ memory = "4096Mi"
+ cpu = "1000m"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "miner-biserk"
+ sectors = "5"
+ mining_mode = "natural"
+
+[[groups]]
+ id = "clients"
+ [groups.resources]
+ memory = "1024Mi"
+ cpu = "1000m"
+ [groups.instances]
+ count = 3
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "client"
diff --git a/testplans/lotus-soup/_compositions/recovery-k8s.toml b/testplans/lotus-soup/_compositions/recovery-k8s.toml
new file mode 100644
index 000000000..5b7037e01
--- /dev/null
+++ b/testplans/lotus-soup/_compositions/recovery-k8s.toml
@@ -0,0 +1,95 @@
+[metadata]
+ name = "lotus-soup"
+ author = ""
+
+[global]
+ plan = "lotus-soup"
+ case = "recovery-failed-windowed-post"
+ total_instances = 9
+ builder = "docker:go"
+ runner = "cluster:k8s"
+
+[global.build]
+ selectors = ["testground"]
+
+[global.run_config]
+ exposed_ports = { pprof = "6060", node_rpc = "1234", miner_rpc = "2345" }
+ keep_service=true
+
+[global.build_config]
+ push_registry=true
+ go_proxy_mode="remote"
+ go_proxy_url="http://localhost:8081"
+ registry_type="aws"
+
+[global.run.test_params]
+ clients = "4"
+ miners = "4"
+ genesis_timestamp_offset = "0"
+ balance = "20000000"
+
+[[groups]]
+ id = "bootstrapper"
+ [groups.resources]
+ memory = "512Mi"
+ cpu = "1000m"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "bootstrapper"
+
+[[groups]]
+ id = "miners"
+ [groups.resources]
+ memory = "4096Mi"
+ cpu = "1000m"
+ [groups.instances]
+ count = 2
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "miner"
+ sectors = "10"
+ mining_mode = "natural"
+
+[[groups]]
+ id = "miners-full-slash"
+ [groups.resources]
+ memory = "4096Mi"
+ cpu = "1000m"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "miner-full-slash"
+ sectors = "10"
+ mining_mode = "natural"
+
+[[groups]]
+ id = "miners-partial-slash"
+ [groups.resources]
+ memory = "4096Mi"
+ cpu = "1000m"
+ [groups.instances]
+ count = 1
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "miner-partial-slash"
+ sectors = "10"
+ mining_mode = "natural"
+
+[[groups]]
+ id = "clients"
+ [groups.resources]
+ memory = "1024Mi"
+ cpu = "1000m"
+ [groups.instances]
+ count = 4
+ percentage = 0.0
+ [groups.run]
+ [groups.run.test_params]
+ role = "client"
diff --git a/testplans/lotus-soup/deals_e2e.go b/testplans/lotus-soup/deals_e2e.go
new file mode 100644
index 000000000..f052fd3bd
--- /dev/null
+++ b/testplans/lotus-soup/deals_e2e.go
@@ -0,0 +1,176 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "io/ioutil"
+ "math/rand"
+ "os"
+ "time"
+
+ "github.com/filecoin-project/go-address"
+ "github.com/filecoin-project/go-state-types/big"
+ "github.com/filecoin-project/lotus/api"
+ "github.com/testground/sdk-go/sync"
+
+ mbig "math/big"
+
+ "github.com/filecoin-project/lotus/build"
+
+ "github.com/filecoin-project/oni/lotus-soup/testkit"
+)
+
+// This is the baseline test; Filecoin 101.
+//
+// A network with a bootstrapper, a number of miners, and a number of clients/full nodes
+// is constructed and connected through the bootstrapper.
+// Some funds are allocated to each node and a number of sectors are presealed in the genesis block.
+//
+// The test plan:
+// One or more clients store content to one or more miners, testing storage deals.
+// The plan ensures that the storage deals hit the blockchain and measure the time it took.
+// Verification: one or more clients retrieve and verify the hashes of stored content.
+// The plan ensures that all (previously) published content can be correctly retrieved
+// and measures the time it took.
+//
+// Preparation of the genesis block: this is the responsibility of the bootstrapper.
+// In order to compute the genesis block, we need to collect identities and presealed
+// sectors from each node.
+// Then we create a genesis block that allocates some funds to each node and collects
+// the presealed sectors.
+func dealsE2E(t *testkit.TestEnvironment) error {
+ // Dispatch/forward non-client roles to defaults.
+ if t.Role != "client" {
+ return testkit.HandleDefaultRole(t)
+ }
+
+ // This is a client role
+ fastRetrieval := t.BooleanParam("fast_retrieval")
+ t.RecordMessage("running client, with fast retrieval set to: %v", fastRetrieval)
+
+ cl, err := testkit.PrepareClient(t)
+ if err != nil {
+ return err
+ }
+
+ ctx := context.Background()
+ client := cl.FullApi
+
+ // select a random miner
+ minerAddr := cl.MinerAddrs[rand.Intn(len(cl.MinerAddrs))]
+ if err := client.NetConnect(ctx, minerAddr.MinerNetAddrs); err != nil {
+ return err
+ }
+ t.D().Counter(fmt.Sprintf("send-data-to,miner=%s", minerAddr.MinerActorAddr)).Inc(1)
+
+ t.RecordMessage("selected %s as the miner", minerAddr.MinerActorAddr)
+
+ if fastRetrieval {
+ err = initPaymentChannel(t, ctx, cl, minerAddr)
+ if err != nil {
+ return err
+ }
+ }
+
+ time.Sleep(12 * time.Second)
+
+ // generate 1600 bytes of random data
+ data := make([]byte, 1600)
+ rand.New(rand.NewSource(time.Now().UnixNano())).Read(data)
+
+ file, err := ioutil.TempFile("/tmp", "data")
+ if err != nil {
+ return err
+ }
+ defer os.Remove(file.Name())
+
+ _, err = file.Write(data)
+ if err != nil {
+ return err
+ }
+
+ fcid, err := client.ClientImport(ctx, api.FileRef{Path: file.Name(), IsCAR: false})
+ if err != nil {
+ return err
+ }
+ t.RecordMessage("file cid: %s", fcid)
+
+ // start deal
+ t1 := time.Now()
+ deal := testkit.StartDeal(ctx, minerAddr.MinerActorAddr, client, fcid.Root, fastRetrieval)
+ t.RecordMessage("started deal: %s", deal)
+
+ // TODO: this sleep is only necessary because deals don't immediately get logged in the dealstore, we should fix this
+ time.Sleep(2 * time.Second)
+
+ t.RecordMessage("waiting for deal to be sealed")
+ testkit.WaitDealSealed(t, ctx, client, deal)
+ t.D().ResettingHistogram("deal.sealed").Update(int64(time.Since(t1)))
+
+ // wait for all client deals to be sealed before trying to retrieve
+ t.SyncClient.MustSignalAndWait(ctx, sync.State("done-sealing"), t.IntParam("clients"))
+
+ carExport := true
+
+ t.RecordMessage("trying to retrieve %s", fcid)
+ t1 = time.Now()
+ _ = testkit.RetrieveData(t, ctx, client, fcid.Root, nil, carExport, data)
+ t.D().ResettingHistogram("deal.retrieved").Update(int64(time.Since(t1)))
+
+ t.SyncClient.MustSignalEntry(ctx, testkit.StateStopMining)
+
+ time.Sleep(10 * time.Second) // wait for metrics to be emitted
+
+ // TODO broadcast published content CIDs to other clients
+ // TODO select a random piece of content published by some other client and retrieve it
+
+ t.SyncClient.MustSignalAndWait(ctx, testkit.StateDone, t.TestInstanceCount)
+ return nil
+}
+
+// filToAttoFil converts a fractional filecoin value into AttoFIL, rounding if necessary
+func filToAttoFil(f float64) big.Int {
+ a := mbig.NewFloat(f)
+ a.Mul(a, mbig.NewFloat(float64(build.FilecoinPrecision)))
+ i, _ := a.Int(nil)
+ return big.Int{Int: i}
+}
+
+func initPaymentChannel(t *testkit.TestEnvironment, ctx context.Context, cl *testkit.LotusClient, minerAddr testkit.MinerAddressesMsg) error {
+ recv := minerAddr
+ balance := filToAttoFil(10)
+ t.RecordMessage("my balance: %d", balance)
+ t.RecordMessage("creating payment channel; from=%s, to=%s, funds=%d", cl.Wallet.Address, recv.WalletAddr, balance)
+
+ channel, err := cl.FullApi.PaychGet(ctx, cl.Wallet.Address, recv.WalletAddr, balance)
+ if err != nil {
+ return fmt.Errorf("failed to create payment channel: %w", err)
+ }
+
+ if addr := channel.Channel; addr != address.Undef {
+ return fmt.Errorf("expected an Undef channel address, got: %s", addr)
+ }
+
+ t.RecordMessage("payment channel created; msg_cid=%s", channel.WaitSentinel)
+ t.RecordMessage("waiting for payment channel message to appear on chain")
+
+ // wait for the channel creation message to appear on chain.
+ _, err = cl.FullApi.StateWaitMsg(ctx, channel.WaitSentinel, 2)
+ if err != nil {
+ return fmt.Errorf("failed while waiting for payment channel creation msg to appear on chain: %w", err)
+ }
+
+ // need to wait so that the channel is tracked.
+ // the full API waits for build.MessageConfidence (=1 in tests) before tracking the channel.
+ // we wait for 2 confirmations, so we have the assurance the channel is tracked.
+
+ t.RecordMessage("reloading paych; now it should have an address")
+ channel, err = cl.FullApi.PaychGet(ctx, cl.Wallet.Address, recv.WalletAddr, big.Zero())
+ if err != nil {
+ return fmt.Errorf("failed to reload payment channel: %w", err)
+ }
+
+ t.RecordMessage("channel address: %s", channel.Channel)
+
+ return nil
+}
diff --git a/testplans/lotus-soup/deals_stress.go b/testplans/lotus-soup/deals_stress.go
new file mode 100644
index 000000000..616c7c8d9
--- /dev/null
+++ b/testplans/lotus-soup/deals_stress.go
@@ -0,0 +1,147 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "io/ioutil"
+ "math/rand"
+ "os"
+ "sync"
+ "time"
+
+ "github.com/filecoin-project/lotus/api"
+ "github.com/ipfs/go-cid"
+
+ "github.com/filecoin-project/oni/lotus-soup/testkit"
+)
+
+func dealsStress(t *testkit.TestEnvironment) error {
+ // Dispatch/forward non-client roles to defaults.
+ if t.Role != "client" {
+ return testkit.HandleDefaultRole(t)
+ }
+
+ t.RecordMessage("running client")
+
+ cl, err := testkit.PrepareClient(t)
+ if err != nil {
+ return err
+ }
+
+ ctx := context.Background()
+ client := cl.FullApi
+
+ // select a random miner
+ minerAddr := cl.MinerAddrs[rand.Intn(len(cl.MinerAddrs))]
+ if err := client.NetConnect(ctx, minerAddr.MinerNetAddrs); err != nil {
+ return err
+ }
+
+ t.RecordMessage("selected %s as the miner", minerAddr.MinerActorAddr)
+
+ time.Sleep(12 * time.Second)
+
+ // prepare a number of concurrent data points
+ deals := t.IntParam("deals")
+ data := make([][]byte, 0, deals)
+ files := make([]*os.File, 0, deals)
+ cids := make([]cid.Cid, 0, deals)
+ rng := rand.NewSource(time.Now().UnixNano())
+
+ for i := 0; i < deals; i++ {
+ dealData := make([]byte, 1600)
+ rand.New(rng).Read(dealData)
+
+ dealFile, err := ioutil.TempFile("/tmp", "data")
+ if err != nil {
+ return err
+ }
+ defer os.Remove(dealFile.Name())
+
+ _, err = dealFile.Write(dealData)
+ if err != nil {
+ return err
+ }
+
+ dealCid, err := client.ClientImport(ctx, api.FileRef{Path: dealFile.Name(), IsCAR: false})
+ if err != nil {
+ return err
+ }
+
+ t.RecordMessage("deal %d file cid: %s", i, dealCid)
+
+ data = append(data, dealData)
+ files = append(files, dealFile)
+ cids = append(cids, dealCid.Root)
+ }
+
+ concurrentDeals := true
+ if t.StringParam("deal_mode") == "serial" {
+ concurrentDeals = false
+ }
+
+ // this to avoid failure to get block
+ time.Sleep(2 * time.Second)
+
+ t.RecordMessage("starting storage deals")
+ if concurrentDeals {
+
+ var wg1 sync.WaitGroup
+ for i := 0; i < deals; i++ {
+ wg1.Add(1)
+ go func(i int) {
+ defer wg1.Done()
+ t1 := time.Now()
+ deal := testkit.StartDeal(ctx, minerAddr.MinerActorAddr, client, cids[i], false)
+ t.RecordMessage("started storage deal %d -> %s", i, deal)
+ time.Sleep(2 * time.Second)
+ t.RecordMessage("waiting for deal %d to be sealed", i)
+ testkit.WaitDealSealed(t, ctx, client, deal)
+ t.D().ResettingHistogram(fmt.Sprintf("deal.sealed,miner=%s", minerAddr.MinerActorAddr)).Update(int64(time.Since(t1)))
+ }(i)
+ }
+ t.RecordMessage("waiting for all deals to be sealed")
+ wg1.Wait()
+ t.RecordMessage("all deals sealed; starting retrieval")
+
+ var wg2 sync.WaitGroup
+ for i := 0; i < deals; i++ {
+ wg2.Add(1)
+ go func(i int) {
+ defer wg2.Done()
+ t.RecordMessage("retrieving data for deal %d", i)
+ t1 := time.Now()
+ _ = testkit.RetrieveData(t, ctx, client, cids[i], nil, true, data[i])
+
+ t.RecordMessage("retrieved data for deal %d", i)
+ t.D().ResettingHistogram("deal.retrieved").Update(int64(time.Since(t1)))
+ }(i)
+ }
+ t.RecordMessage("waiting for all retrieval deals to complete")
+ wg2.Wait()
+ t.RecordMessage("all retrieval deals successful")
+
+ } else {
+
+ for i := 0; i < deals; i++ {
+ deal := testkit.StartDeal(ctx, minerAddr.MinerActorAddr, client, cids[i], false)
+ t.RecordMessage("started storage deal %d -> %s", i, deal)
+ time.Sleep(2 * time.Second)
+ t.RecordMessage("waiting for deal %d to be sealed", i)
+ testkit.WaitDealSealed(t, ctx, client, deal)
+ }
+
+ for i := 0; i < deals; i++ {
+ t.RecordMessage("retrieving data for deal %d", i)
+ _ = testkit.RetrieveData(t, ctx, client, cids[i], nil, true, data[i])
+ t.RecordMessage("retrieved data for deal %d", i)
+ }
+ }
+
+ t.SyncClient.MustSignalEntry(ctx, testkit.StateStopMining)
+ t.SyncClient.MustSignalAndWait(ctx, testkit.StateDone, t.TestInstanceCount)
+
+ time.Sleep(15 * time.Second) // wait for metrics to be emitted
+
+ return nil
+}
diff --git a/testplans/lotus-soup/env-ci.toml b/testplans/lotus-soup/env-ci.toml
new file mode 100644
index 000000000..bd651c465
--- /dev/null
+++ b/testplans/lotus-soup/env-ci.toml
@@ -0,0 +1 @@
+[client]
diff --git a/testplans/lotus-soup/go.mod b/testplans/lotus-soup/go.mod
new file mode 100644
index 000000000..4b260cd20
--- /dev/null
+++ b/testplans/lotus-soup/go.mod
@@ -0,0 +1,47 @@
+module github.com/filecoin-project/lotus/testplans/lotus-soup
+
+go 1.14
+
+require (
+ contrib.go.opencensus.io/exporter/prometheus v0.1.0
+ github.com/codeskyblue/go-sh v0.0.0-20200712050446-30169cf553fe
+ github.com/davecgh/go-spew v1.1.1
+ github.com/drand/drand v1.2.1
+ github.com/filecoin-project/go-address v0.0.5-0.20201103152444-f2023ef3f5bb
+ github.com/filecoin-project/go-fil-markets v1.0.6
+ github.com/filecoin-project/go-jsonrpc v0.1.2-0.20201008195726-68c6a2704e49
+ github.com/filecoin-project/go-state-types v0.0.0-20201102161440-c8033295a1fc
+ github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b
+ github.com/filecoin-project/lotus v0.9.2-0.20201012041700-a2e0832a12f2
+ github.com/filecoin-project/oni/lotus-soup v0.0.0-20201016183302-a8430088b2b8
+ github.com/filecoin-project/specs-actors v0.9.13
+ github.com/google/uuid v1.1.2
+ github.com/gorilla/mux v1.7.4
+ github.com/hashicorp/go-multierror v1.1.0
+ github.com/ipfs/go-cid v0.0.7
+ github.com/ipfs/go-datastore v0.4.5
+ github.com/ipfs/go-ipfs-files v0.0.8
+ github.com/ipfs/go-ipld-format v0.2.0
+ github.com/ipfs/go-log/v2 v2.1.2-0.20200626104915-0016c0b4b3e4
+ github.com/ipfs/go-merkledag v0.3.2
+ github.com/ipfs/go-unixfs v0.2.4
+ github.com/ipld/go-car v0.1.1-0.20201119040415-11b6074b6d4d
+ github.com/kpacha/opencensus-influxdb v0.0.0-20181102202715-663e2683a27c
+ github.com/libp2p/go-libp2p v0.12.0
+ github.com/libp2p/go-libp2p-core v0.7.0
+ github.com/libp2p/go-libp2p-pubsub-tracer v0.0.0-20200626141350-e730b32bf1e6
+ github.com/multiformats/go-multiaddr v0.3.1
+ github.com/multiformats/go-multiaddr-net v0.2.0
+ github.com/testground/sdk-go v0.2.6-0.20201016180515-1e40e1b0ec3a
+ go.opencensus.io v0.22.4
+ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
+)
+
+// This will work in all build modes: docker:go, exec:go, and local go build.
+// On docker:go and exec:go, it maps to /extra/filecoin-ffi, as it's picked up
+// as an "extra source" in the manifest.
+replace github.com/filecoin-project/filecoin-ffi => ../../extern/filecoin-ffi
+
+replace github.com/supranational/blst => ../../extern/blst
+
+replace github.com/filecoin-project/lotus => ../../
diff --git a/testplans/lotus-soup/go.sum b/testplans/lotus-soup/go.sum
new file mode 100644
index 000000000..ca19673cb
--- /dev/null
+++ b/testplans/lotus-soup/go.sum
@@ -0,0 +1,2078 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
+cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
+cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg=
+cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
+cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
+cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
+cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
+cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
+cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw=
+cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
+cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
+cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
+cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o=
+cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
+cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
+cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
+cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
+collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE=
+contrib.go.opencensus.io/exporter/jaeger v0.1.0/go.mod h1:VYianECmuFPwU37O699Vc1GOcy+y8kOsfaxHRImmjbA=
+contrib.go.opencensus.io/exporter/prometheus v0.1.0 h1:SByaIoWwNgMdPSgl5sMqM2KDE5H/ukPWBRo314xiDvg=
+contrib.go.opencensus.io/exporter/prometheus v0.1.0/go.mod h1:cGFniUXGZlKRjzOyuZJ6mgB+PgBcCIa79kEKR8YCW+A=
+dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
+dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
+dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
+git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
+github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
+github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
+github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
+github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
+github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM=
+github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
+github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
+github.com/GeertJohan/go.rice v1.0.0 h1:KkI6O9uMaQU3VEKaj01ulavtF7o1fWT7+pk/4voiMLQ=
+github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
+github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee h1:8doiS7ib3zi6/K172oDhSKU0dJ/miJramo9NITOMyZQ=
+github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee/go.mod h1:W0GbEAA4uFNYOGG2cJpmFJ04E6SD1NLELPYZB57/7AY=
+github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
+github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y=
+github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
+github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
+github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
+github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
+github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
+github.com/Stebalien/go-bitfield v0.0.0-20180330043415-076a62f9ce6e/go.mod h1:3oM7gXIttpYDAJXpVNnSCiUMYBLIZ6cb1t+Ip982MRo=
+github.com/Stebalien/go-bitfield v0.0.1 h1:X3kbSSPUaJK60wV2hjOPZwmpljr6VGCqdq4cBLhbQBo=
+github.com/Stebalien/go-bitfield v0.0.1/go.mod h1:GNjFpasyUVkHMsfEOk8EFLJ9syQ6SI+XWrX9Wf2XH0s=
+github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
+github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
+github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
+github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
+github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
+github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
+github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E=
+github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
+github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
+github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
+github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0=
+github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
+github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
+github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
+github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
+github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
+github.com/avast/retry-go v2.6.0+incompatible h1:FelcMrm7Bxacr1/RM8+/eqkDkmVN7tjlsy51dOzB3LI=
+github.com/avast/retry-go v2.6.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
+github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
+github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
+github.com/aws/aws-sdk-go v1.32.11/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
+github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
+github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg=
+github.com/benbjohnson/clock v1.0.1/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
+github.com/benbjohnson/clock v1.0.2/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
+github.com/benbjohnson/clock v1.0.3 h1:vkLuvpK4fmtSCuo60+yC63p7y0BmQ8gm5ZXGuBCJyXg=
+github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c=
+github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
+github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
+github.com/briandowns/spinner v1.11.1/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ=
+github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8=
+github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
+github.com/btcsuite/btcd v0.0.0-20190605094302-a0d1e3e36d50/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
+github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
+github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
+github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
+github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
+github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
+github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
+github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
+github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
+github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
+github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
+github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
+github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129 h1:gfAMKE626QEuKG3si0pdTRcr/YEbBoxY+3GOH3gWvl4=
+github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129/go.mod h1:u9UyCz2eTrSGy6fbupqJ54eY5c4IC8gREQ1053dK12U=
+github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
+github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34=
+github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
+github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
+github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
+github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
+github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
+github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
+github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
+github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
+github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
+github.com/cockroachdb/pebble v0.0.0-20200916222308-4e219a90ba5b/go.mod h1:hU7vhtrqonEphNF+xt8/lHdaBprxmV1h8BOGrd9XwmQ=
+github.com/cockroachdb/pebble v0.0.0-20201001221639-879f3bfeef07/go.mod h1:hU7vhtrqonEphNF+xt8/lHdaBprxmV1h8BOGrd9XwmQ=
+github.com/cockroachdb/redact v0.0.0-20200622112456-cd282804bbd3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
+github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w=
+github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
+github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q=
+github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
+github.com/codeskyblue/go-sh v0.0.0-20200712050446-30169cf553fe h1:69JI97HlzP+PH5Mi1thcGlDoBr6PS2Oe+l3mNmAkbs4=
+github.com/codeskyblue/go-sh v0.0.0-20200712050446-30169cf553fe/go.mod h1:VQx0hjo2oUeQkQUET7wRwradO6f+fN5jzXgB/zROxxE=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-semver v0.2.1-0.20180108230905-e214231b295a/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
+github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
+github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
+github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 h1:HVTnpeuvF6Owjd5mniCL8DEXo7uYXdQEmOP4FJbV5tg=
+github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE=
+github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0=
+github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis=
+github.com/daaku/go.zipexe v1.0.0 h1:VSOgZtH418pH9L16hC/JrgSNJbbAL26pj7lmD1+CGdY=
+github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E=
+github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
+github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davidlazar/go-crypto v0.0.0-20170701192655-dcfb0a7ac018/go.mod h1:rQYf4tfk5sSwFsnDg3qYaBxSjsD9S8+59vW0dKUgme4=
+github.com/davidlazar/go-crypto v0.0.0-20190912175916-7055855a373f/go.mod h1:rQYf4tfk5sSwFsnDg3qYaBxSjsD9S8+59vW0dKUgme4=
+github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU=
+github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U=
+github.com/detailyang/go-fallocate v0.0.0-20180908115635-432fa640bd2e h1:lj77EKYUpYXTd8CD/+QMIf8b6OIOTsfEBSXiAzuEHTU=
+github.com/detailyang/go-fallocate v0.0.0-20180908115635-432fa640bd2e/go.mod h1:3ZQK6DMPSz/QZ73jlWxBtUhNA8xZx7LzUFSq/OfP8vk=
+github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ=
+github.com/dgraph-io/badger v1.6.0-rc1/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
+github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
+github.com/dgraph-io/badger v1.6.1 h1:w9pSFNSdq/JPM1N12Fz/F/bzo993Is1W+Q7HjPzi7yg=
+github.com/dgraph-io/badger v1.6.1/go.mod h1:FRmFw3uxvcpa8zG3Rxs0th+hCLIuaQg8HlNV5bjgnuU=
+github.com/dgraph-io/badger/v2 v2.0.3/go.mod h1:3KY8+bsP8wI0OEnQJAKpd4wIJW/Mm32yw2j/9FUVnIM=
+github.com/dgraph-io/badger/v2 v2.2007.2 h1:EjjK0KqwaFMlPin1ajhP943VPENHJdEz1KLIegjaI3k=
+github.com/dgraph-io/badger/v2 v2.2007.2/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE=
+github.com/dgraph-io/ristretto v0.0.2-0.20200115201040-8f368f2f2ab3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
+github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
+github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de h1:t0UHb5vdojIDUqktM6+xJAfScFBsVpXZmqC9dsgJmeA=
+github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ=
+github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
+github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
+github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
+github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
+github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/drand/bls12-381 v0.3.2 h1:RImU8Wckmx8XQx1tp1q04OV73J9Tj6mmpQLYDP7V1XE=
+github.com/drand/bls12-381 v0.3.2/go.mod h1:dtcLgPtYT38L3NO6mPDYH0nbpc5tjPassDqiniuAt4Y=
+github.com/drand/drand v1.1.2-0.20200905144319-79c957281b32 h1:sU+51aQRaDxg0KnjQg19KuYRIxDBEUHffBAICSnBys8=
+github.com/drand/drand v1.1.2-0.20200905144319-79c957281b32/go.mod h1:0sQEVg+ngs1jaDPVIiEgY0lbENWJPaUlWxGHEaSmKVM=
+github.com/drand/drand v1.2.1 h1:KB7z+69YbnQ5z22AH/LMi0ObDR8DzYmrkS6vZXTR9jI=
+github.com/drand/drand v1.2.1/go.mod h1:j0P7RGmVaY7E/OuO2yQOcQj7OgeZCuhgu2gdv0JAm+g=
+github.com/drand/kyber v1.0.1-0.20200110225416-8de27ed8c0e2/go.mod h1:UpXoA0Upd1N9l4TvRPHr1qAUBBERj6JQ/mnKI3BPEmw=
+github.com/drand/kyber v1.0.2/go.mod h1:x6KOpK7avKj0GJ4emhXFP5n7M7W7ChAPmnQh/OL6vRw=
+github.com/drand/kyber v1.1.2 h1:faemqlaFyLrbBSjZGRzzu5SG/do+uTYpHlnrJIHbAhQ=
+github.com/drand/kyber v1.1.2/go.mod h1:x6KOpK7avKj0GJ4emhXFP5n7M7W7ChAPmnQh/OL6vRw=
+github.com/drand/kyber v1.1.4 h1:YvKM03QWGvLrdTnYmxxP5iURAX+Gdb6qRDUOgg8i60Q=
+github.com/drand/kyber v1.1.4/go.mod h1:9+IgTq7kadePhZg7eRwSD7+bA+bmvqRK+8DtmoV5a3U=
+github.com/drand/kyber-bls12381 v0.1.0 h1:/P4C65VnyEwxzR5ZYYVMNzY1If+aYBrdUU5ukwh7LQw=
+github.com/drand/kyber-bls12381 v0.1.0/go.mod h1:N1emiHpm+jj7kMlxEbu3MUyOiooTgNySln564cgD9mk=
+github.com/drand/kyber-bls12381 v0.2.0/go.mod h1:zQip/bHdeEB6HFZSU3v+d3cQE0GaBVQw9aR2E7AdoeI=
+github.com/drand/kyber-bls12381 v0.2.1 h1:/d5/YAdaCmHpYjF1NZevOEcKGaq6LBbyvkCTIdGqDjs=
+github.com/drand/kyber-bls12381 v0.2.1/go.mod h1:JwWn4nHO9Mp4F5qCie5sVIPQZ0X6cw8XAeMRvc/GXBE=
+github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
+github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
+github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
+github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts=
+github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
+github.com/elastic/go-sysinfo v1.3.0 h1:eb2XFGTMlSwG/yyU9Y8jVAYLIzU2sFzWXwo2gmetyrE=
+github.com/elastic/go-sysinfo v1.3.0/go.mod h1:i1ZYdU10oLNfRzq4vq62BEwD2fH8KaWh6eh0ikPT9F0=
+github.com/elastic/go-windows v1.0.0 h1:qLURgZFkkrYyTTkvYpsZIgf83AUsdIHfvlJaqaZ7aSY=
+github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU=
+github.com/ema/qdisc v0.0.0-20190904071900-b82c76788043/go.mod h1:ix4kG2zvdUd8kEKSW0ZTr1XLks0epFpI4j745DXxlNE=
+github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 h1:BBso6MBKW8ncyZLv37o+KNyy0HrrHgfnOaGQC2qvN+A=
+github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5/go.mod h1:JpoxHjuQauoxiFMl1ie8Xc/7TfLuMZ5eOCONd1sUBHg=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/fatih/color v1.8.0/go.mod h1:3l45GVGkyrnYNl9HoIjnp2NnNWvh6hLAqD8yTfGjnw8=
+github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
+github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
+github.com/fd/go-nat v1.0.0/go.mod h1:BTBu/CKvMmOMUPkKVef1pngt2WFH/lg7E6yQnulfp6E=
+github.com/filecoin-project/go-address v0.0.3/go.mod h1:jr8JxKsYx+lQlQZmF5i2U0Z+cGQ59wMIps/8YW/lDj8=
+github.com/filecoin-project/go-address v0.0.4 h1:gSNMv0qWwH16fGQs7ycOUrDjY6YCSsgLUl0I0KLjo8w=
+github.com/filecoin-project/go-address v0.0.4/go.mod h1:jr8JxKsYx+lQlQZmF5i2U0Z+cGQ59wMIps/8YW/lDj8=
+github.com/filecoin-project/go-address v0.0.5-0.20201103152444-f2023ef3f5bb h1:Cbu7YYsXHtVlPEJ+eqbBx2S3ElmWCB0NjpGPYvvvCrA=
+github.com/filecoin-project/go-address v0.0.5-0.20201103152444-f2023ef3f5bb/go.mod h1:jr8JxKsYx+lQlQZmF5i2U0Z+cGQ59wMIps/8YW/lDj8=
+github.com/filecoin-project/go-amt-ipld/v2 v2.1.0/go.mod h1:nfFPoGyX0CU9SkXX8EoCcSuHN1XcbN0c6KBh7yvP5fs=
+github.com/filecoin-project/go-amt-ipld/v2 v2.1.1-0.20201006184820-924ee87a1349 h1:pIuR0dnMD0i+as8wNnjjHyQrnhP5O5bmba/lmgQeRgU=
+github.com/filecoin-project/go-amt-ipld/v2 v2.1.1-0.20201006184820-924ee87a1349/go.mod h1:vgmwKBkx+ca5OIeEvstiQgzAZnb7R6QaqE1oEDSqa6g=
+github.com/filecoin-project/go-bitfield v0.2.0/go.mod h1:CNl9WG8hgR5mttCnUErjcQjGvuiZjRqK9rHVBsQF4oM=
+github.com/filecoin-project/go-bitfield v0.2.1 h1:S6Uuqcspqu81sWJ0He4OAfFLm1tSwPdVjtKTkl5m/xQ=
+github.com/filecoin-project/go-bitfield v0.2.1/go.mod h1:CNl9WG8hgR5mttCnUErjcQjGvuiZjRqK9rHVBsQF4oM=
+github.com/filecoin-project/go-bitfield v0.2.3-0.20201110211213-fe2c1862e816 h1:RMdzMqe3mu2Z/3N3b9UEfkbGZxukstmZgNC024ybWhA=
+github.com/filecoin-project/go-bitfield v0.2.3-0.20201110211213-fe2c1862e816/go.mod h1:CNl9WG8hgR5mttCnUErjcQjGvuiZjRqK9rHVBsQF4oM=
+github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2 h1:av5fw6wmm58FYMgJeoB/lK9XXrgdugYiTqkdxjTy9k8=
+github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2/go.mod h1:pqTiPHobNkOVM5thSRsHYjyQfq7O5QSCMhvuu9JoDlg=
+github.com/filecoin-project/go-commp-utils v0.0.0-20201119054358-b88f7a96a434 h1:0kHszkYP3hgApcjl5x4rpwONhN9+j7XDobf6at5XfHs=
+github.com/filecoin-project/go-commp-utils v0.0.0-20201119054358-b88f7a96a434/go.mod h1:6s95K91mCyHY51RPWECZieD3SGWTqIFLf1mPOes9l5U=
+github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 h1:2pMXdBnCiXjfCYx/hLqFxccPoqsSveQFxVLvNxy9bus=
+github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03/go.mod h1:+viYnvGtUTgJRdy6oaeF4MTFKAfatX071MPDPBL11EQ=
+github.com/filecoin-project/go-data-transfer v0.6.7 h1:Kacr5qz2YWtd3sensU6aXFtES7joeapVDeXApeUD35I=
+github.com/filecoin-project/go-data-transfer v0.6.7/go.mod h1:C++k1U6+jMQODOaen5OPDo9XQbth9Yq3ie94vNjBJbk=
+github.com/filecoin-project/go-data-transfer v1.0.1/go.mod h1:UxvfUAY9v3ub0a21BSK9u3pB2aq30Y0KMsG+w9/ysyo=
+github.com/filecoin-project/go-data-transfer v1.2.0 h1:LM+K+J+y9t8e3gYskJHWDlyHJsF6aaxoHOP+HIiVE1U=
+github.com/filecoin-project/go-data-transfer v1.2.0/go.mod h1:ZAH51JZFR8NZC4FPiDPG+swjgui0q6zTMJbztc6pHhY=
+github.com/filecoin-project/go-ds-versioning v0.1.0 h1:y/X6UksYTsK8TLCI7rttCKEvl8btmWxyFMEeeWGUxIQ=
+github.com/filecoin-project/go-ds-versioning v0.1.0/go.mod h1:mp16rb4i2QPmxBnmanUx8i/XANp+PFCCJWiAb+VW4/s=
+github.com/filecoin-project/go-fil-commcid v0.0.0-20200716160307-8f644712406f h1:GxJzR3oRIMTPtpZ0b7QF8FKPK6/iPAc7trhlL5k/g+s=
+github.com/filecoin-project/go-fil-commcid v0.0.0-20200716160307-8f644712406f/go.mod h1:Eaox7Hvus1JgPrL5+M3+h7aSPHc0cVqpSxA+TxIEpZQ=
+github.com/filecoin-project/go-fil-commcid v0.0.0-20201016201715-d41df56b4f6a h1:hyJ+pUm/4U4RdEZBlg6k8Ma4rDiuvqyGpoICXAxwsTg=
+github.com/filecoin-project/go-fil-commcid v0.0.0-20201016201715-d41df56b4f6a/go.mod h1:Eaox7Hvus1JgPrL5+M3+h7aSPHc0cVqpSxA+TxIEpZQ=
+github.com/filecoin-project/go-fil-markets v0.7.1 h1:e0NlpSnaeGyDUhCOzevjcxkSA54kt9BzlXpLRgduUFI=
+github.com/filecoin-project/go-fil-markets v0.7.1/go.mod h1:5Pt4DXQqUoUrp9QzlSdlYTpItXxwAtqKrxRWQ6hAOqk=
+github.com/filecoin-project/go-fil-markets v1.0.5-0.20201113164554-c5eba40d5335/go.mod h1:AJySOJC00JRWEZzRG2KsfUnqEf5ITXxeX09BE9N4f9c=
+github.com/filecoin-project/go-fil-markets v1.0.6 h1:JTZBMKJ19YpK/vQfE0rHvjELy83NeGor5d4dBnoIlK0=
+github.com/filecoin-project/go-fil-markets v1.0.6/go.mod h1:iVYc+VrHIP15F5COkHNM6ndTwKSJ7qPrHSKCfFUnve4=
+github.com/filecoin-project/go-hamt-ipld v0.1.5 h1:uoXrKbCQZ49OHpsTCkrThPNelC4W3LPEk0OrS/ytIBM=
+github.com/filecoin-project/go-hamt-ipld v0.1.5/go.mod h1:6Is+ONR5Cd5R6XZoCse1CWaXZc0Hdb/JeX+EQCQzX24=
+github.com/filecoin-project/go-hamt-ipld/v2 v2.0.0 h1:b3UDemBYN2HNfk3KOXNuxgTTxlWi3xVvbQP0IT38fvM=
+github.com/filecoin-project/go-hamt-ipld/v2 v2.0.0/go.mod h1:7aWZdaQ1b16BVoQUYR+eEvrDCGJoPLxFpDynFjYfBjI=
+github.com/filecoin-project/go-jsonrpc v0.1.2-0.20201008195726-68c6a2704e49 h1:FSY245KeXFCUgyfFEu+bhrZNk8BGGJyfpSmQl2aiPU8=
+github.com/filecoin-project/go-jsonrpc v0.1.2-0.20201008195726-68c6a2704e49/go.mod h1:XBBpuKIMaXIIzeqzO1iucq4GvbF8CxmXRFoezRh+Cx4=
+github.com/filecoin-project/go-multistore v0.0.3 h1:vaRBY4YiA2UZFPK57RNuewypB8u0DzzQwqsL0XarpnI=
+github.com/filecoin-project/go-multistore v0.0.3/go.mod h1:kaNqCC4IhU4B1uyr7YWFHd23TL4KM32aChS0jNkyUvQ=
+github.com/filecoin-project/go-padreader v0.0.0-20200903213702-ed5fae088b20 h1:+/4aUeUoKr6AKfPE3mBhXA5spIV6UcKdTYDPNU2Tdmg=
+github.com/filecoin-project/go-padreader v0.0.0-20200903213702-ed5fae088b20/go.mod h1:mPn+LRRd5gEKNAtc+r3ScpW2JRU/pj4NBKdADYWHiak=
+github.com/filecoin-project/go-paramfetch v0.0.2-0.20200701152213-3e0f0afdc261 h1:A256QonvzRaknIIAuWhe/M2dpV2otzs3NBhi5TWa/UA=
+github.com/filecoin-project/go-paramfetch v0.0.2-0.20200701152213-3e0f0afdc261/go.mod h1:fZzmf4tftbwf9S37XRifoJlz7nCjRdIrMGLR07dKLCc=
+github.com/filecoin-project/go-state-types v0.0.0-20200903145444-247639ffa6ad/go.mod h1:IQ0MBPnonv35CJHtWSN3YY1Hz2gkPru1Q9qoaYLxx9I=
+github.com/filecoin-project/go-state-types v0.0.0-20200904021452-1883f36ca2f4/go.mod h1:IQ0MBPnonv35CJHtWSN3YY1Hz2gkPru1Q9qoaYLxx9I=
+github.com/filecoin-project/go-state-types v0.0.0-20200905071437-95828685f9df/go.mod h1:IQ0MBPnonv35CJHtWSN3YY1Hz2gkPru1Q9qoaYLxx9I=
+github.com/filecoin-project/go-state-types v0.0.0-20200928172055-2df22083d8ab/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g=
+github.com/filecoin-project/go-state-types v0.0.0-20201102161440-c8033295a1fc h1:+hbMY4Pcx2oizrfH08VWXwrj5mU8aJT6g0UNxGHFCGU=
+github.com/filecoin-project/go-state-types v0.0.0-20201102161440-c8033295a1fc/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g=
+github.com/filecoin-project/go-statemachine v0.0.0-20200714194326-a77c3ae20989/go.mod h1:FGwQgZAt2Gh5mjlwJUlVB62JeYdo+if0xWxSEfBD9ig=
+github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe h1:dF8u+LEWeIcTcfUcCf3WFVlc81Fr2JKg8zPzIbBDKDw=
+github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe/go.mod h1:FGwQgZAt2Gh5mjlwJUlVB62JeYdo+if0xWxSEfBD9ig=
+github.com/filecoin-project/go-statestore v0.1.0 h1:t56reH59843TwXHkMcwyuayStBIiWBRilQjQ+5IiwdQ=
+github.com/filecoin-project/go-statestore v0.1.0/go.mod h1:LFc9hD+fRxPqiHiaqUEZOinUJB4WARkRfNl10O7kTnI=
+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.9.2-0.20201012041700-a2e0832a12f2 h1:LS2lchwWKv3vnyrOtlvYISl1NIcDpL9baemE1T0tDS4=
+github.com/filecoin-project/lotus v0.9.2-0.20201012041700-a2e0832a12f2/go.mod h1:mtewzEjzt/ghDb1lfq9Td2hRY312ySuXJsLdy6DKGME=
+github.com/filecoin-project/oni/lotus-soup v0.0.0-20201016183302-a8430088b2b8 h1:1c12tiuKYXWmZuZusFYOmXIZFewy69mns6Zz03yqjlI=
+github.com/filecoin-project/oni/lotus-soup v0.0.0-20201016183302-a8430088b2b8/go.mod h1:ord2u62C7SvGvbAoImfPISkm6INaA3bnAKSBRGQ4Nj4=
+github.com/filecoin-project/specs-actors v0.9.4/go.mod h1:BStZQzx5x7TmCkLv0Bpa07U6cPKol6fd3w9KjMPZ6Z4=
+github.com/filecoin-project/specs-actors v0.9.7/go.mod h1:wM2z+kwqYgXn5Z7scV1YHLyd1Q1cy0R8HfTIWQ0BFGU=
+github.com/filecoin-project/specs-actors v0.9.12/go.mod h1:TS1AW/7LbG+615j4NsjMK1qlpAwaFsG9w0V2tg2gSao=
+github.com/filecoin-project/specs-actors v0.9.13 h1:rUEOQouefi9fuVY/2HOroROJlZbOzWYXXeIh41KF2M4=
+github.com/filecoin-project/specs-actors v0.9.13/go.mod h1:TS1AW/7LbG+615j4NsjMK1qlpAwaFsG9w0V2tg2gSao=
+github.com/filecoin-project/specs-actors/v2 v2.0.1/go.mod h1:v2NZVYinNIKA9acEMBm5wWXxqv5+frFEbekBFemYghY=
+github.com/filecoin-project/specs-actors/v2 v2.0.3 h1:Niy6xncgi8bI8aBCt1McdZfATBfG4Uxytt8KW4s3bAc=
+github.com/filecoin-project/specs-actors/v2 v2.0.3/go.mod h1:v2NZVYinNIKA9acEMBm5wWXxqv5+frFEbekBFemYghY=
+github.com/filecoin-project/specs-actors/v2 v2.3.2 h1:2Vcf4CGa29kRh4JJ02m+FbvD/p3YNnLGsaHfw7Uj49g=
+github.com/filecoin-project/specs-actors/v2 v2.3.2/go.mod h1:UuJQLoTx/HPvvWeqlIFmC/ywlOLHNe8SNQ3OunFbu2Y=
+github.com/filecoin-project/specs-storage v0.1.1-0.20200907031224-ed2e5cd13796 h1:dJsTPWpG2pcTeojO2pyn0c6l+x/3MZYCBgo/9d11JEk=
+github.com/filecoin-project/specs-storage v0.1.1-0.20200907031224-ed2e5cd13796/go.mod h1:nJRRM7Aa9XVvygr3W9k6xGF46RWzr2zxF/iGoAIfA/g=
+github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506 h1:Ur/l2+6qN+lQiqjozWWc5p9UDaAMDZKTlDS98oRnlIw=
+github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506/go.mod h1:nJRRM7Aa9XVvygr3W9k6xGF46RWzr2zxF/iGoAIfA/g=
+github.com/filecoin-project/test-vectors/schema v0.0.4/go.mod h1:iQ9QXLpYWL3m7warwvK1JC/pTri8mnfEmKygNDqqY6E=
+github.com/filecoin-project/test-vectors/schema v0.0.5/go.mod h1:iQ9QXLpYWL3m7warwvK1JC/pTri8mnfEmKygNDqqY6E=
+github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
+github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6 h1:u/UEqS66A5ckRmS4yNpjmVH56sVtS/RfclBAYocb4as=
+github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6/go.mod h1:1i71OnUq3iUe1ma7Lr6yG6/rjvM3emb6yoL7xLFzcVQ=
+github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
+github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
+github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
+github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
+github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1 h1:EzDjxMg43q1tA2c0MV3tNbaontnHLplHyFF6M5KiVP0=
+github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1/go.mod h1:0eHX/BVySxPc6SE2mZRoppGq7qcEagxdmQnA3dzork8=
+github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
+github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
+github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
+github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
+github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
+github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo=
+github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
+github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
+github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
+github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
+github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4=
+github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
+github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
+github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/godbus/dbus v0.0.0-20190402143921-271e53dc4968/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
+github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
+github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
+github.com/gogo/googleapis v1.4.0 h1:zgVt4UpGxcqVOw97aRGxT4svlcmdK35fynLNctY32zI=
+github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
+github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/gogo/status v1.0.3/go.mod h1:SavQ51ycCLnc7dGyJxp8YAmudx8xqiVrRf+6IXRsugc=
+github.com/gogo/status v1.1.0 h1:+eIkrewn5q6b30y+g/BJINVVdi2xH7je5MPJ3ZPK3JA=
+github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM=
+github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
+github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
+github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
+github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.2-0.20190904063534-ff6b7dc882cf h1:gFVkHXmVAhEbxZVDln5V9GKrLaluNoFHDbrZwAWZgws=
+github.com/golang/snappy v0.0.2-0.20190904063534-ff6b7dc882cf/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM=
+github.com/google/gopacket v1.1.18 h1:lum7VRA9kdlvBi7/v2p7/zcbkduHaCH/SVVyurs7OpY=
+github.com/google/gopacket v1.1.18/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
+github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
+github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
+github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f h1:KMlcu9X58lhTA/KrfX8Bi1LQSO4pzoVjTiL3h4Jk+Zk=
+github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
+github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
+github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
+github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
+github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE=
+github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 h1:0IKlLyQ3Hs9nDaiK5cSHAGmcQEIC8l2Ts1u6x5Dfrqg=
+github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
+github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/grpc-ecosystem/grpc-gateway v1.14.6 h1:8ERzHx8aj1Sc47mu9n/AksaKCSWrMchFtkdrS4BIj5o=
+github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw=
+github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw=
+github.com/gxed/go-shellwords v1.0.3/go.mod h1:N7paucT91ByIjmVJHhvoarjoQnmsi3Jd3vH7VqgtMxQ=
+github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU=
+github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48=
+github.com/gxed/pubsub v0.0.0-20180201040156-26ebdf44f824/go.mod h1:OiEWyHgK+CWrmOlVquHaIK1vhpUJydC9m0Je6mhaiNE=
+github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 h1:BpJ2o0OR5FV7vrkDYfXYVJQeMNWa8RhklZOpW2ITAIQ=
+github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026/go.mod h1:5Scbynm8dF1XAPwIwkGPqzkM/shndPm79Jd1003hTjE=
+github.com/hannahhoward/cbor-gen-for v0.0.0-20200817222906-ea96cece81f1 h1:F9k+7wv5OIk1zcq23QpdiL0hfDuXPjuOmMNaC6fgQ0Q=
+github.com/hannahhoward/cbor-gen-for v0.0.0-20200817222906-ea96cece81f1/go.mod h1:jvfsLIxk0fY/2BKSQ1xf2406AKA5dwMmKKv0ADcOfN8=
+github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e h1:3YKHER4nmd7b5qy5t0GWDTwSn4OyRgfAXSmo6VnryBY=
+github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e/go.mod h1:I8h3MITA53gN9OnWGCgaMa0JWVRdXthWw4M3CPM54OY=
+github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
+github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
+github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
+github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
+github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
+github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
+github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
+github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
+github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
+github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
+github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
+github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
+github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
+github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
+github.com/hodgesds/perf-utils v0.0.8/go.mod h1:F6TfvsbtrF88i++hou29dTXlI2sfsJv+gRZDtmTJkAs=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
+github.com/huin/goupnp v0.0.0-20180415215157-1395d1447324/go.mod h1:MZ2ZmwcBpvOoJ22IJsc7va19ZwoheaBk43rKg12SKag=
+github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo=
+github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc=
+github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o=
+github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/influxdata/flux v0.65.0/go.mod h1:BwN2XG2lMszOoquQaFdPET8FRQfrXiZsWmcMO9rkaVY=
+github.com/influxdata/influxdb v1.8.0 h1:/X+G+i3udzHVxpBMuXdPZcUbkIE0ouT+6U+CzQTsOys=
+github.com/influxdata/influxdb v1.8.0/go.mod h1:SIzcnsjaHRFpmlxpJ4S3NT64qtEKYweNTUMb/vh0OMQ=
+github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
+github.com/influxdata/influxdb1-client v0.0.0-20200515024757-02f0bf5dbca3 h1:k3/6a1Shi7GGCp9QpyYuXsMM6ncTOjCzOE9Fd6CDA+Q=
+github.com/influxdata/influxdb1-client v0.0.0-20200515024757-02f0bf5dbca3/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
+github.com/influxdata/influxql v1.1.0/go.mod h1:KpVI7okXjK6PRi3Z5B+mtKZli+R1DnZgb3N+tzevNgo=
+github.com/influxdata/line-protocol v0.0.0-20180522152040-32c6aa80de5e/go.mod h1:4kt73NQhadE3daL3WhR5EJ/J2ocX0PZzwxQ0gXJ7oFE=
+github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19ybifQhZoQNF5D8=
+github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE=
+github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0=
+github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po=
+github.com/ipfs/bbloom v0.0.1/go.mod h1:oqo8CVWsJFMOZqTglBG4wydCE4IQA/G2/SEofB0rjUI=
+github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
+github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
+github.com/ipfs/go-bitswap v0.0.3/go.mod h1:jadAZYsP/tcRMl47ZhFxhaNuDQoXawT8iHMg+iFoQbg=
+github.com/ipfs/go-bitswap v0.0.9/go.mod h1:kAPf5qgn2W2DrgAcscZ3HrM9qh4pH+X8Fkk3UPrwvis=
+github.com/ipfs/go-bitswap v0.1.0/go.mod h1:FFJEf18E9izuCqUtHxbWEvq+reg7o4CW5wSAE1wsxj0=
+github.com/ipfs/go-bitswap v0.1.2/go.mod h1:qxSWS4NXGs7jQ6zQvoPY3+NmOfHHG47mhkiLzBpJQIs=
+github.com/ipfs/go-bitswap v0.1.8/go.mod h1:TOWoxllhccevbWFUR2N7B1MTSVVge1s6XSMiCSA4MzM=
+github.com/ipfs/go-bitswap v0.2.20 h1:Zfi5jDUoqxDThORUznqdeL77DdGniAzlccNJ4vr+Itc=
+github.com/ipfs/go-bitswap v0.2.20/go.mod h1:C7TwBgHnu89Q8sHsTJP7IhUqF9XYLe71P4tT5adgmYo=
+github.com/ipfs/go-bitswap v0.3.2 h1:TdKx7lpidYe2dMAKfdeNS26y6Pc/AZX/i8doI1GV210=
+github.com/ipfs/go-bitswap v0.3.2/go.mod h1:AyWWfN3moBzQX0banEtfKOfbXb3ZeoOeXnZGNPV9S6w=
+github.com/ipfs/go-block-format v0.0.1/go.mod h1:DK/YYcsSUIVAFNwo/KZCdIIbpN0ROH/baNLgayt4pFc=
+github.com/ipfs/go-block-format v0.0.2 h1:qPDvcP19izTjU8rgo6p7gTXZlkMkF5bz5G3fqIsSCPE=
+github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJgCQF+KIgOPJY=
+github.com/ipfs/go-blockservice v0.0.3/go.mod h1:/NNihwTi6V2Yr6g8wBI+BSwPuURpBRMtYNGrlxZ8KuI=
+github.com/ipfs/go-blockservice v0.0.7/go.mod h1:EOfb9k/Y878ZTRY/CH0x5+ATtaipfbRhbvNSdgc/7So=
+github.com/ipfs/go-blockservice v0.1.0/go.mod h1:hzmMScl1kXHg3M2BjTymbVPjv627N7sYcvYaKbop39M=
+github.com/ipfs/go-blockservice v0.1.3/go.mod h1:OTZhFpkgY48kNzbgyvcexW9cHrpjBYIjSR0KoDOFOLU=
+github.com/ipfs/go-blockservice v0.1.4-0.20200624145336-a978cec6e834 h1:hFJoI1D2a3MqiNkSb4nKwrdkhCngUxUTFNwVwovZX2s=
+github.com/ipfs/go-blockservice v0.1.4-0.20200624145336-a978cec6e834/go.mod h1:OTZhFpkgY48kNzbgyvcexW9cHrpjBYIjSR0KoDOFOLU=
+github.com/ipfs/go-blockservice v0.1.4 h1:Vq+MlsH8000KbbUciRyYMEw/NNP8UAGmcqKi4uWmFGA=
+github.com/ipfs/go-blockservice v0.1.4/go.mod h1:OTZhFpkgY48kNzbgyvcexW9cHrpjBYIjSR0KoDOFOLU=
+github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
+github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
+github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
+github.com/ipfs/go-cid v0.0.4-0.20191112011718-79e75dffeb10/go.mod h1:/BYOuUoxkE+0f6tGzlzMvycuN+5l35VOR4Bpg2sCmds=
+github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M=
+github.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67FexhXog=
+github.com/ipfs/go-cid v0.0.6-0.20200501230655-7c82f3b81c00/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67FexhXog=
+github.com/ipfs/go-cid v0.0.6/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I=
+github.com/ipfs/go-cid v0.0.7 h1:ysQJVJA3fNDF1qigJbsSQOdjhVLsOEoPdh0+R97k3jY=
+github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I=
+github.com/ipfs/go-cidutil v0.0.2 h1:CNOboQf1t7Qp0nuNh8QMmhJs0+Q//bRL1axtCnIB1Yo=
+github.com/ipfs/go-cidutil v0.0.2/go.mod h1:ewllrvrxG6AMYStla3GD7Cqn+XYSLqjK0vc+086tB6s=
+github.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE=
+github.com/ipfs/go-datastore v0.0.5/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE=
+github.com/ipfs/go-datastore v0.1.0/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE=
+github.com/ipfs/go-datastore v0.1.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw=
+github.com/ipfs/go-datastore v0.3.0/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw=
+github.com/ipfs/go-datastore v0.3.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw=
+github.com/ipfs/go-datastore v0.4.0/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA=
+github.com/ipfs/go-datastore v0.4.1/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA=
+github.com/ipfs/go-datastore v0.4.2/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA=
+github.com/ipfs/go-datastore v0.4.4/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA=
+github.com/ipfs/go-datastore v0.4.5 h1:cwOUcGMLdLPWgu3SlrCckCMznaGADbPqE0r8h768/Dg=
+github.com/ipfs/go-datastore v0.4.5/go.mod h1:eXTcaaiN6uOlVCLS9GjJUJtlvJfM3xk23w3fyfrmmJs=
+github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk=
+github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps=
+github.com/ipfs/go-ds-badger v0.0.2/go.mod h1:Y3QpeSFWQf6MopLTiZD+VT6IC1yZqaGmjvRcKeSGij8=
+github.com/ipfs/go-ds-badger v0.0.5/go.mod h1:g5AuuCGmr7efyzQhLL8MzwqcauPojGPUaHzfGTzuE3s=
+github.com/ipfs/go-ds-badger v0.0.7/go.mod h1:qt0/fWzZDoPW6jpQeqUjR5kBfhDNB65jd9YlmAvpQBk=
+github.com/ipfs/go-ds-badger v0.2.1/go.mod h1:Tx7l3aTph3FMFrRS838dcSJh+jjA7cX9DrGVwx/NOwE=
+github.com/ipfs/go-ds-badger v0.2.3 h1:J27YvAcpuA5IvZUbeBxOcQgqnYHUPxoygc6QxxkodZ4=
+github.com/ipfs/go-ds-badger v0.2.3/go.mod h1:pEYw0rgg3FIrywKKnL+Snr+w/LjJZVMTBRn4FS6UHUk=
+github.com/ipfs/go-ds-badger2 v0.1.0/go.mod h1:pbR1p817OZbdId9EvLOhKBgUVTM3BMCSTan78lDDVaw=
+github.com/ipfs/go-ds-badger2 v0.1.1-0.20200708190120-187fc06f714e h1:Xi1nil8K2lBOorBS6Ys7+hmUCzH8fr3U9ipdL/IrcEI=
+github.com/ipfs/go-ds-badger2 v0.1.1-0.20200708190120-187fc06f714e/go.mod h1:lJnws7amT9Ehqzta0gwMrRsURU04caT0iRPr1W8AsOU=
+github.com/ipfs/go-ds-leveldb v0.0.1/go.mod h1:feO8V3kubwsEF22n0YRQCffeb79OOYIykR4L04tMOYc=
+github.com/ipfs/go-ds-leveldb v0.1.0/go.mod h1:hqAW8y4bwX5LWcCtku2rFNX3vjDZCy5LZCg+cSZvYb8=
+github.com/ipfs/go-ds-leveldb v0.4.1/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s=
+github.com/ipfs/go-ds-leveldb v0.4.2 h1:QmQoAJ9WkPMUfBLnu1sBVy0xWWlJPg0m4kRAiJL9iaw=
+github.com/ipfs/go-ds-leveldb v0.4.2/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s=
+github.com/ipfs/go-ds-measure v0.1.0 h1:vE4TyY4aeLeVgnnPBC5QzKIjKrqzha0NCujTfgvVbVQ=
+github.com/ipfs/go-ds-measure v0.1.0/go.mod h1:1nDiFrhLlwArTME1Ees2XaBOl49OoCgd2A3f8EchMSY=
+github.com/ipfs/go-ds-pebble v0.0.2-0.20200921225637-ce220f8ac459/go.mod h1:oh4liWHulKcDKVhCska5NLelE3MatWl+1FwSz3tY91g=
+github.com/ipfs/go-filestore v1.0.0 h1:QR7ekKH+q2AGiWDc7W2Q0qHuYSRZGUJqUn0GsegEPb0=
+github.com/ipfs/go-filestore v1.0.0/go.mod h1:/XOCuNtIe2f1YPbiXdYvD0BKLA0JR1MgPiFOdcuu9SM=
+github.com/ipfs/go-fs-lock v0.0.6 h1:sn3TWwNVQqSeNjlWy6zQ1uUGAZrV3hPOyEA6y1/N2a0=
+github.com/ipfs/go-fs-lock v0.0.6/go.mod h1:OTR+Rj9sHiRubJh3dRhD15Juhd/+w6VPOY28L7zESmM=
+github.com/ipfs/go-graphsync v0.1.0/go.mod h1:jMXfqIEDFukLPZHqDPp8tJMbHO9Rmeb9CEGevngQbmE=
+github.com/ipfs/go-graphsync v0.2.1 h1:MdehhqBSuTI2LARfKLkpYnt0mUrqHs/mtuDnESXHBfU=
+github.com/ipfs/go-graphsync v0.2.1/go.mod h1:gEBvJUNelzMkaRPJTpg/jaKN4AQW/7wDWu0K92D8o10=
+github.com/ipfs/go-graphsync v0.4.2/go.mod h1:/VmbZTUdUMTbNkgzAiCEucIIAU3BkLE2cZrDCVUhyi0=
+github.com/ipfs/go-graphsync v0.4.3/go.mod h1:mPOwDYv128gf8gxPFgXnz4fNrSYPsWyqisJ7ych+XDY=
+github.com/ipfs/go-graphsync v0.5.0 h1:iaByvxq88Ys1KcaQzTS1wmRhNsNEo3SaUiSGqTSbGmM=
+github.com/ipfs/go-graphsync v0.5.0/go.mod h1:e2ZxnClqBBYAtd901g9vXMJzS47labjAtOzsWtOzKNk=
+github.com/ipfs/go-hamt-ipld v0.1.1/go.mod h1:1EZCr2v0jlCnhpa+aZ0JZYp8Tt2w16+JJOAVz17YcDk=
+github.com/ipfs/go-ipfs-blockstore v0.0.1/go.mod h1:d3WClOmRQKFnJ0Jz/jj/zmksX0ma1gROTlovZKBmN08=
+github.com/ipfs/go-ipfs-blockstore v0.1.0/go.mod h1:5aD0AvHPi7mZc6Ci1WCAhiBQu2IsfTduLl+422H6Rqw=
+github.com/ipfs/go-ipfs-blockstore v0.1.4/go.mod h1:Jxm3XMVjh6R17WvxFEiyKBLUGr86HgIYJW/D/MwqeYQ=
+github.com/ipfs/go-ipfs-blockstore v1.0.0/go.mod h1:knLVdhVU9L7CC4T+T4nvGdeUIPAXlnd9zmXfp+9MIjU=
+github.com/ipfs/go-ipfs-blockstore v1.0.1 h1:fnuVj4XdZp4yExhd0CnUwAiMNJHiPnfInhiuwz4lW1w=
+github.com/ipfs/go-ipfs-blockstore v1.0.1/go.mod h1:MGNZlHNEnR4KGgPHM3/k8lBySIOK2Ve+0KjZubKlaOE=
+github.com/ipfs/go-ipfs-blockstore v1.0.3 h1:RDhK6fdg5YsonkpMuMpdvk/pRtOQlrIRIybuQfkvB2M=
+github.com/ipfs/go-ipfs-blockstore v1.0.3/go.mod h1:MGNZlHNEnR4KGgPHM3/k8lBySIOK2Ve+0KjZubKlaOE=
+github.com/ipfs/go-ipfs-blocksutil v0.0.1 h1:Eh/H4pc1hsvhzsQoMEP3Bke/aW5P5rVM1IWFJMcGIPQ=
+github.com/ipfs/go-ipfs-blocksutil v0.0.1/go.mod h1:Yq4M86uIOmxmGPUHv/uI7uKqZNtLb449gwKqXjIsnRk=
+github.com/ipfs/go-ipfs-chunker v0.0.1/go.mod h1:tWewYK0we3+rMbOh7pPFGDyypCtvGcBFymgY4rSDLAw=
+github.com/ipfs/go-ipfs-chunker v0.0.5 h1:ojCf7HV/m+uS2vhUGWcogIIxiO5ubl5O57Q7NapWLY8=
+github.com/ipfs/go-ipfs-chunker v0.0.5/go.mod h1:jhgdF8vxRHycr00k13FM8Y0E+6BoalYeobXmUyTreP8=
+github.com/ipfs/go-ipfs-cmds v0.1.0 h1:0CEde9EcxByej8+L6d1PST57J4ambRPyCTjLG5Ymou8=
+github.com/ipfs/go-ipfs-cmds v0.1.0/go.mod h1:TiK4e7/V31tuEb8YWDF8lN3qrnDH+BS7ZqWIeYJlAs8=
+github.com/ipfs/go-ipfs-config v0.0.11 h1:5/4nas2CQXiKr2/MLxU24GDGTBvtstQIQezuk7ltOQQ=
+github.com/ipfs/go-ipfs-config v0.0.11/go.mod h1:wveA8UT5ywN26oKStByzmz1CO6cXwLKKM6Jn/Hfw08I=
+github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=
+github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ=
+github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=
+github.com/ipfs/go-ipfs-ds-help v0.0.1/go.mod h1:gtP9xRaZXqIQRh1HRpp595KbBEdgqWFxefeVKOV8sxo=
+github.com/ipfs/go-ipfs-ds-help v0.1.1/go.mod h1:SbBafGJuGsPI/QL3j9Fc5YPLeAu+SzOkI0gFwAg+mOs=
+github.com/ipfs/go-ipfs-ds-help v1.0.0 h1:bEQ8hMGs80h0sR8O4tfDgV6B01aaF9qeTrujrTLYV3g=
+github.com/ipfs/go-ipfs-ds-help v1.0.0/go.mod h1:ujAbkeIgkKAWtxxNkoZHWLCyk5JpPoKnGyCcsoF6ueE=
+github.com/ipfs/go-ipfs-exchange-interface v0.0.1 h1:LJXIo9W7CAmugqI+uofioIpRb6rY30GUu7G6LUfpMvM=
+github.com/ipfs/go-ipfs-exchange-interface v0.0.1/go.mod h1:c8MwfHjtQjPoDyiy9cFquVtVHkO9b9Ob3FG91qJnWCM=
+github.com/ipfs/go-ipfs-exchange-offline v0.0.1 h1:P56jYKZF7lDDOLx5SotVh5KFxoY6C81I1NSHW1FxGew=
+github.com/ipfs/go-ipfs-exchange-offline v0.0.1/go.mod h1:WhHSFCVYX36H/anEKQboAzpUws3x7UeEGkzQc3iNkM0=
+github.com/ipfs/go-ipfs-files v0.0.2/go.mod h1:INEFm0LL2LWXBhNJ2PMIIb2w45hpXgPjNoE7yA8Y1d4=
+github.com/ipfs/go-ipfs-files v0.0.3/go.mod h1:INEFm0LL2LWXBhNJ2PMIIb2w45hpXgPjNoE7yA8Y1d4=
+github.com/ipfs/go-ipfs-files v0.0.4/go.mod h1:INEFm0LL2LWXBhNJ2PMIIb2w45hpXgPjNoE7yA8Y1d4=
+github.com/ipfs/go-ipfs-files v0.0.8 h1:8o0oFJkJ8UkO/ABl8T6ac6tKF3+NIpj67aAB6ZpusRg=
+github.com/ipfs/go-ipfs-files v0.0.8/go.mod h1:wiN/jSG8FKyk7N0WyctKSvq3ljIa2NNTiZB55kpTdOs=
+github.com/ipfs/go-ipfs-flags v0.0.1/go.mod h1:RnXBb9WV53GSfTrSDVK61NLTFKvWc60n+K9EgCDh+rA=
+github.com/ipfs/go-ipfs-http-client v0.0.5 h1:niW5M0qqa0O/VRCAzr3f5Y7i3MjTpf0lhpkisjRtHR8=
+github.com/ipfs/go-ipfs-http-client v0.0.5/go.mod h1:8EKP9RGUrUex4Ff86WhnKU7seEBOtjdgXlY9XHYvYMw=
+github.com/ipfs/go-ipfs-posinfo v0.0.1 h1:Esoxj+1JgSjX0+ylc0hUmJCOv6V2vFoZiETLR6OtpRs=
+github.com/ipfs/go-ipfs-posinfo v0.0.1/go.mod h1:SwyeVP+jCwiDu0C313l/8jg6ZxM0qqtlt2a0vILTc1A=
+github.com/ipfs/go-ipfs-pq v0.0.1/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY=
+github.com/ipfs/go-ipfs-pq v0.0.2 h1:e1vOOW6MuOwG2lqxcLA+wEn93i/9laCY8sXAw76jFOY=
+github.com/ipfs/go-ipfs-pq v0.0.2/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY=
+github.com/ipfs/go-ipfs-routing v0.0.1/go.mod h1:k76lf20iKFxQTjcJokbPM9iBXVXVZhcOwc360N4nuKs=
+github.com/ipfs/go-ipfs-routing v0.1.0 h1:gAJTT1cEeeLj6/DlLX6t+NxD9fQe2ymTO6qWRDI/HQQ=
+github.com/ipfs/go-ipfs-routing v0.1.0/go.mod h1:hYoUkJLyAUKhF58tysKpids8RNDPO42BVMgK5dNsoqY=
+github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc=
+github.com/ipfs/go-ipfs-util v0.0.2 h1:59Sswnk1MFaiq+VcaknX7aYEyGyGDAA73ilhEK2POp8=
+github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ=
+github.com/ipfs/go-ipld-cbor v0.0.1/go.mod h1:RXHr8s4k0NE0TKhnrxqZC9M888QfsBN9rhS5NjfKzY8=
+github.com/ipfs/go-ipld-cbor v0.0.2/go.mod h1:wTBtrQZA3SoFKMVkp6cn6HMRteIB1VsmHA0AQFOn7Nc=
+github.com/ipfs/go-ipld-cbor v0.0.3/go.mod h1:wTBtrQZA3SoFKMVkp6cn6HMRteIB1VsmHA0AQFOn7Nc=
+github.com/ipfs/go-ipld-cbor v0.0.4/go.mod h1:BkCduEx3XBCO6t2Sfo5BaHzuok7hbhdMm9Oh8B2Ftq4=
+github.com/ipfs/go-ipld-cbor v0.0.5-0.20200204214505-252690b78669/go.mod h1:BkCduEx3XBCO6t2Sfo5BaHzuok7hbhdMm9Oh8B2Ftq4=
+github.com/ipfs/go-ipld-cbor v0.0.5-0.20200428170625-a0bd04d3cbdf h1:PRCy+w3GocY77CBEwTprp6hn7PLiEU1YToKe7B+1FVk=
+github.com/ipfs/go-ipld-cbor v0.0.5-0.20200428170625-a0bd04d3cbdf/go.mod h1:BkCduEx3XBCO6t2Sfo5BaHzuok7hbhdMm9Oh8B2Ftq4=
+github.com/ipfs/go-ipld-cbor v0.0.5 h1:ovz4CHKogtG2KB/h1zUp5U0c/IzZrL435rCh5+K/5G8=
+github.com/ipfs/go-ipld-cbor v0.0.5/go.mod h1:BkCduEx3XBCO6t2Sfo5BaHzuok7hbhdMm9Oh8B2Ftq4=
+github.com/ipfs/go-ipld-format v0.0.1/go.mod h1:kyJtbkDALmFHv3QR6et67i35QzO3S0dCDnkOJhcZkms=
+github.com/ipfs/go-ipld-format v0.0.2/go.mod h1:4B6+FM2u9OJ9zCV+kSbgFAZlOrv1Hqbf0INGQgiKf9k=
+github.com/ipfs/go-ipld-format v0.2.0 h1:xGlJKkArkmBvowr+GMCX0FEZtkro71K1AwiKnL37mwA=
+github.com/ipfs/go-ipld-format v0.2.0/go.mod h1:3l3C1uKoadTPbeNfrDi+xMInYKlx2Cvg1BuydPSdzQs=
+github.com/ipfs/go-ipns v0.0.2 h1:oq4ErrV4hNQ2Eim257RTYRgfOSV/s8BDaf9iIl4NwFs=
+github.com/ipfs/go-ipns v0.0.2/go.mod h1:WChil4e0/m9cIINWLxZe1Jtf77oz5L05rO2ei/uKJ5U=
+github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM=
+github.com/ipfs/go-log v1.0.0/go.mod h1:JO7RzlMK6rA+CIxFMLOuB6Wf5b81GDiKElL7UPSIKjA=
+github.com/ipfs/go-log v1.0.1/go.mod h1:HuWlQttfN6FWNHRhlY5yMk/lW7evQC0HHGOxEwMRR8I=
+github.com/ipfs/go-log v1.0.2/go.mod h1:1MNjMxe0u6xvJZgeqbJ8vdo2TKaGwZ1a0Bpza+sr2Sk=
+github.com/ipfs/go-log v1.0.3/go.mod h1:OsLySYkwIbiSUR/yBTdv1qPtcE4FW3WPWk/ewz9Ru+A=
+github.com/ipfs/go-log v1.0.4 h1:6nLQdX4W8P9yZZFH7mO+X/PzjN8Laozm/lMJ6esdgzY=
+github.com/ipfs/go-log v1.0.4/go.mod h1:oDCg2FkjogeFOhqqb+N39l2RpTNPL6F/StPkB3kPgcs=
+github.com/ipfs/go-log/v2 v2.0.1/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0=
+github.com/ipfs/go-log/v2 v2.0.2/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0=
+github.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0=
+github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw=
+github.com/ipfs/go-log/v2 v2.0.8/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw=
+github.com/ipfs/go-log/v2 v2.1.1/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM=
+github.com/ipfs/go-log/v2 v2.1.2-0.20200626104915-0016c0b4b3e4 h1:3bijxqzQ1O9yg7gd7Aqk80oaEvsJ+uXw0zSvi2qR3Jw=
+github.com/ipfs/go-log/v2 v2.1.2-0.20200626104915-0016c0b4b3e4/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM=
+github.com/ipfs/go-merkledag v0.0.3/go.mod h1:Oc5kIXLHokkE1hWGMBHw+oxehkAaTOqtEb7Zbh6BhLA=
+github.com/ipfs/go-merkledag v0.0.6/go.mod h1:QYPdnlvkOg7GnQRofu9XZimC5ZW5Wi3bKys/4GQQfto=
+github.com/ipfs/go-merkledag v0.2.3/go.mod h1:SQiXrtSts3KGNmgOzMICy5c0POOpUNQLvB3ClKnBAlk=
+github.com/ipfs/go-merkledag v0.3.1/go.mod h1:fvkZNNZixVW6cKSZ/JfLlON5OlgTXNdRLz0p6QG/I2M=
+github.com/ipfs/go-merkledag v0.3.2 h1:MRqj40QkrWkvPswXs4EfSslhZ4RVPRbxwX11js0t1xY=
+github.com/ipfs/go-merkledag v0.3.2/go.mod h1:fvkZNNZixVW6cKSZ/JfLlON5OlgTXNdRLz0p6QG/I2M=
+github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fGD6n0jO4kdg=
+github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY=
+github.com/ipfs/go-metrics-prometheus v0.0.2/go.mod h1:ELLU99AQQNi+zX6GCGm2lAgnzdSH3u5UVlCdqSXnEks=
+github.com/ipfs/go-path v0.0.3/go.mod h1:zIRQUez3LuQIU25zFjC2hpBTHimWx7VK5bjZgRLbbdo=
+github.com/ipfs/go-path v0.0.7 h1:H06hKMquQ0aYtHiHryOMLpQC1qC3QwXwkahcEVD51Ho=
+github.com/ipfs/go-path v0.0.7/go.mod h1:6KTKmeRnBXgqrTvzFrPV3CamxcgvXX/4z79tfAd2Sno=
+github.com/ipfs/go-peertaskqueue v0.0.4/go.mod h1:03H8fhyeMfKNFWqzYEVyMbcPUeYrqP1MX6Kd+aN+rMQ=
+github.com/ipfs/go-peertaskqueue v0.1.0/go.mod h1:Jmk3IyCcfl1W3jTW3YpghSwSEC6IJ3Vzz/jUmWw8Z0U=
+github.com/ipfs/go-peertaskqueue v0.1.1/go.mod h1:Jmk3IyCcfl1W3jTW3YpghSwSEC6IJ3Vzz/jUmWw8Z0U=
+github.com/ipfs/go-peertaskqueue v0.2.0 h1:2cSr7exUGKYyDeUyQ7P/nHPs9P7Ht/B+ROrpN1EJOjc=
+github.com/ipfs/go-peertaskqueue v0.2.0/go.mod h1:5/eNrBEbtSKWCG+kQK8K8fGNixoYUnr+P7jivavs9lY=
+github.com/ipfs/go-todocounter v0.0.1/go.mod h1:l5aErvQc8qKE2r7NDMjmq5UNAvuZy0rC8BHOplkWvZ4=
+github.com/ipfs/go-unixfs v0.0.4/go.mod h1:eIo/p9ADu/MFOuyxzwU+Th8D6xoxU//r590vUpWyfz8=
+github.com/ipfs/go-unixfs v0.2.1/go.mod h1:IwAAgul1UQIcNZzKPYZWOCijryFBeCV79cNubPzol+k=
+github.com/ipfs/go-unixfs v0.2.4 h1:6NwppOXefWIyysZ4LR/qUBPvXd5//8J3jiMdvpbw6Lo=
+github.com/ipfs/go-unixfs v0.2.4/go.mod h1:SUdisfUjNoSDzzhGVxvCL9QO/nKdwXdr+gbMUdqcbYw=
+github.com/ipfs/go-verifcid v0.0.1 h1:m2HI7zIuR5TFyQ1b79Da5N9dnnCP1vcu2QqawmWlK2E=
+github.com/ipfs/go-verifcid v0.0.1/go.mod h1:5Hrva5KBeIog4A+UpqlaIU+DEstipcJYQQZc0g37pY0=
+github.com/ipfs/interface-go-ipfs-core v0.2.3 h1:E6uQ+1fJjkxJWlL9lAE72a5FWeyeeNL3GitLy8+jq3Y=
+github.com/ipfs/interface-go-ipfs-core v0.2.3/go.mod h1:Tihp8zxGpUeE3Tokr94L6zWZZdkRQvG5TL6i9MuNE+s=
+github.com/ipfs/iptb v1.4.0 h1:YFYTrCkLMRwk/35IMyC6+yjoQSHTEcNcefBStLJzgvo=
+github.com/ipfs/iptb v1.4.0/go.mod h1:1rzHpCYtNp87/+hTxG5TfCVn/yMY3dKnLn8tBiMfdmg=
+github.com/ipfs/iptb-plugins v0.2.1 h1:au4HWn9/pRPbkxA08pDx2oRAs4cnbgQWgV0teYXuuGA=
+github.com/ipfs/iptb-plugins v0.2.1/go.mod h1:QXMbtIWZ+jRsW8a4h13qAKU7jcM7qaittO8wOsTP0Rs=
+github.com/ipld/go-car v0.1.1-0.20200923150018-8cdef32e2da4 h1:6phjU3kXvCEWOZpu+Ob0w6DzgPFZmDLgLPxJhD8RxEY=
+github.com/ipld/go-car v0.1.1-0.20200923150018-8cdef32e2da4/go.mod h1:xrMEcuSq+D1vEwl+YAXsg/JfA98XGpXDwnkIL4Aimqw=
+github.com/ipld/go-car v0.1.1-0.20201119040415-11b6074b6d4d h1:iphSzTuPqyDgH7WUVZsdqUnQNzYgIblsVr1zhVNA33U=
+github.com/ipld/go-car v0.1.1-0.20201119040415-11b6074b6d4d/go.mod h1:2Gys8L8MJ6zkh1gktTSXreY63t4UbyvNp5JaudTyxHQ=
+github.com/ipld/go-ipld-prime v0.0.2-0.20200428162820-8b59dc292b8e/go.mod h1:uVIwe/u0H4VdKv3kaN1ck7uCb6yD9cFLS9/ELyXbsw8=
+github.com/ipld/go-ipld-prime v0.5.1-0.20200828233916-988837377a7f h1:XpOuNQ5GbXxUcSukbQcW9jkE7REpaFGJU2/T00fo9kA=
+github.com/ipld/go-ipld-prime v0.5.1-0.20200828233916-988837377a7f/go.mod h1:0xEgdD6MKbZ1vF0GC+YcR/C4SQCAlRuOjIJ2i0HxqzM=
+github.com/ipld/go-ipld-prime v0.5.1-0.20201021195245-109253e8a018 h1:RbRHv8epkmvBYA5cGfz68GUSbOgx5j/7ObLIl4Rsif0=
+github.com/ipld/go-ipld-prime v0.5.1-0.20201021195245-109253e8a018/go.mod h1:0xEgdD6MKbZ1vF0GC+YcR/C4SQCAlRuOjIJ2i0HxqzM=
+github.com/ipld/go-ipld-prime-proto v0.0.0-20200428191222-c1ffdadc01e1/go.mod h1:OAV6xBmuTLsPZ+epzKkPB1e25FHk/vCtyatkdHcArLs=
+github.com/ipld/go-ipld-prime-proto v0.0.0-20200922192210-9a2bfd4440a6 h1:6Mq+tZGSEMEoJJ1NbJRhddeelkXZcU8yfH/ZRYUo/Es=
+github.com/ipld/go-ipld-prime-proto v0.0.0-20200922192210-9a2bfd4440a6/go.mod h1:3pHYooM9Ea65jewRwrb2u5uHZCNkNTe9ABsVB+SrkH0=
+github.com/ipld/go-ipld-prime-proto v0.1.0 h1:j7gjqrfwbT4+gXpHwEx5iMssma3mnctC7YaCimsFP70=
+github.com/ipld/go-ipld-prime-proto v0.1.0/go.mod h1:11zp8f3sHVgIqtb/c9Kr5ZGqpnCLF1IVTNOez9TopzE=
+github.com/ipsn/go-secp256k1 v0.0.0-20180726113642-9d62b9f0bc52 h1:QG4CGBqCeuBo6aZlGAamSkxWdgWfZGeE49eUOWJPA4c=
+github.com/ipsn/go-secp256k1 v0.0.0-20180726113642-9d62b9f0bc52/go.mod h1:fdg+/X9Gg4AsAIzWpEHwnqd+QY3b7lajxyjE1m4hkq4=
+github.com/jackpal/gateway v1.0.4/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA=
+github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA=
+github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
+github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
+github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
+github.com/jbenet/go-cienv v0.0.0-20150120210510-1bb1476777ec/go.mod h1:rGaEvXB4uRSZMmzKNLoXvTu1sfx+1kv/DojUlPrSZGs=
+github.com/jbenet/go-cienv v0.1.0 h1:Vc/s0QbQtoxX8MwwSLWWh+xNNZvM3Lw7NsTcHrvvhMc=
+github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA=
+github.com/jbenet/go-random v0.0.0-20190219211222-123a90aedc0c h1:uUx61FiAa1GI6ZmVd2wf2vULeQZIKG66eybjNXKYCz4=
+github.com/jbenet/go-random v0.0.0-20190219211222-123a90aedc0c/go.mod h1:sdx1xVM9UuLw1tXnhJWN3piypTUO3vCIHYmG15KE/dU=
+github.com/jbenet/go-temp-err-catcher v0.0.0-20150120210811-aac704a3f4f2/go.mod h1:8GXXJV31xl8whumTzdZsTt3RnUIiPqzkyf7mxToRCMs=
+github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk=
+github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk=
+github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY=
+github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4=
+github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o=
+github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4=
+github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
+github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
+github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
+github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
+github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4=
+github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak=
+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=
+github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
+github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o=
+github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
+github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
+github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0=
+github.com/kabukky/httpscerts v0.0.0-20150320125433-617593d7dcb3 h1:Iy7Ifq2ysilWU4QlCx/97OoI4xT1IV7i8byT/EyIT/M=
+github.com/kabukky/httpscerts v0.0.0-20150320125433-617593d7dcb3/go.mod h1:BYpt4ufZiIGv2nXn4gMxnfKV306n3mWXgNu/d2TqdTU=
+github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0=
+github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
+github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
+github.com/kilic/bls12-381 v0.0.0-20200607163746-32e1441c8a9f h1:qET3Wx0v8tMtoTOQnsJXVvqvCopSf48qobR6tcJuDHo=
+github.com/kilic/bls12-381 v0.0.0-20200607163746-32e1441c8a9f/go.mod h1:XXfR6YFCRSrkEXbNlIyDsgXVNJWVUV30m/ebkVy9n6s=
+github.com/kilic/bls12-381 v0.0.0-20200731194930-64c428e1bff5/go.mod h1:XXfR6YFCRSrkEXbNlIyDsgXVNJWVUV30m/ebkVy9n6s=
+github.com/kilic/bls12-381 v0.0.0-20200820230200-6b2c19996391 h1:51kHw7l/dUDdOdW06AlUGT5jnpj6nqQSILebcsikSjA=
+github.com/kilic/bls12-381 v0.0.0-20200820230200-6b2c19996391/go.mod h1:XXfR6YFCRSrkEXbNlIyDsgXVNJWVUV30m/ebkVy9n6s=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
+github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
+github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
+github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/koron/go-ssdp v0.0.0-20180514024734-4a0ed625a78b/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk=
+github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d h1:68u9r4wEvL3gYg2jvAOgROwZ3H+Y3hIDk4tbbmIjcYQ=
+github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk=
+github.com/kpacha/opencensus-influxdb v0.0.0-20181102202715-663e2683a27c h1:3pM6OrLfkfe0rKZjE6MHdcTaI0ohcHbRUZJeJqkvPb4=
+github.com/kpacha/opencensus-influxdb v0.0.0-20181102202715-663e2683a27c/go.mod h1:ESXZSm2iaF+1P5o6VFEWpeARTQpcil4e1DwumnTopdg=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ=
+github.com/libp2p/go-addr-util v0.0.2 h1:7cWK5cdA5x72jX0g8iLrQWm5TRJZ6CzGdPEhWj7plWU=
+github.com/libp2p/go-addr-util v0.0.2/go.mod h1:Ecd6Fb3yIuLzq4bD7VcywcVSBtefcAwnUISBM3WG15E=
+github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ=
+github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs=
+github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM=
+github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c=
+github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic=
+github.com/libp2p/go-conn-security v0.0.1/go.mod h1:bGmu51N0KU9IEjX7kl2PQjgZa40JQWnayTvNMgD/vyk=
+github.com/libp2p/go-conn-security-multistream v0.0.1/go.mod h1:nc9vud7inQ+d6SO0I/6dSWrdMnHnzZNHeyUQqrAJulE=
+github.com/libp2p/go-conn-security-multistream v0.0.2/go.mod h1:nc9vud7inQ+d6SO0I/6dSWrdMnHnzZNHeyUQqrAJulE=
+github.com/libp2p/go-conn-security-multistream v0.1.0/go.mod h1:aw6eD7LOsHEX7+2hJkDxw1MteijaVcI+/eP2/x3J1xc=
+github.com/libp2p/go-conn-security-multistream v0.2.0 h1:uNiDjS58vrvJTg9jO6bySd1rMKejieG7v45ekqHbZ1M=
+github.com/libp2p/go-conn-security-multistream v0.2.0/go.mod h1:hZN4MjlNetKD3Rq5Jb/P5ohUnFLNzEAR4DLSzpn2QLU=
+github.com/libp2p/go-eventbus v0.0.2/go.mod h1:Hr/yGlwxA/stuLnpMiu82lpNKpvRy3EaJxPu40XYOwk=
+github.com/libp2p/go-eventbus v0.1.0/go.mod h1:vROgu5cs5T7cv7POWlWxBaVLxfSegC5UGQf8A2eEmx4=
+github.com/libp2p/go-eventbus v0.2.1 h1:VanAdErQnpTioN2TowqNcOijf6YwhuODe4pPKSDpxGc=
+github.com/libp2p/go-eventbus v0.2.1/go.mod h1:jc2S4SoEVPP48H9Wpzm5aiGwUCBMfGhVhhBjyhhCJs8=
+github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8=
+github.com/libp2p/go-flow-metrics v0.0.2/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs=
+github.com/libp2p/go-flow-metrics v0.0.3 h1:8tAs/hSdNvUiLgtlSy3mxwxWP4I9y/jlkPFT7epKdeM=
+github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs=
+github.com/libp2p/go-libp2p v0.0.2/go.mod h1:Qu8bWqFXiocPloabFGUcVG4kk94fLvfC8mWTDdFC9wE=
+github.com/libp2p/go-libp2p v0.0.30/go.mod h1:XWT8FGHlhptAv1+3V/+J5mEpzyui/5bvFsNuWYs611A=
+github.com/libp2p/go-libp2p v0.1.0/go.mod h1:6D/2OBauqLUoqcADOJpn9WbKqvaM07tDw68qHM0BxUM=
+github.com/libp2p/go-libp2p v0.1.1/go.mod h1:I00BRo1UuUSdpuc8Q2mN7yDF/oTUTRAX6JWpTiK9Rp8=
+github.com/libp2p/go-libp2p v0.3.1/go.mod h1:e6bwxbdYH1HqWTz8faTChKGR0BjPc8p+6SyP8GTTR7Y=
+github.com/libp2p/go-libp2p v0.4.0/go.mod h1:9EsEIf9p2UDuwtPd0DwJsAl0qXVxgAnuDGRvHbfATfI=
+github.com/libp2p/go-libp2p v0.6.0/go.mod h1:mfKWI7Soz3ABX+XEBR61lGbg+ewyMtJHVt043oWeqwg=
+github.com/libp2p/go-libp2p v0.6.1/go.mod h1:CTFnWXogryAHjXAKEbOf1OWY+VeAP3lDMZkfEI5sT54=
+github.com/libp2p/go-libp2p v0.7.0/go.mod h1:hZJf8txWeCduQRDC/WSqBGMxaTHCOYHt2xSU1ivxn0k=
+github.com/libp2p/go-libp2p v0.7.4/go.mod h1:oXsBlTLF1q7pxr+9w6lqzS1ILpyHsaBPniVO7zIHGMw=
+github.com/libp2p/go-libp2p v0.8.1/go.mod h1:QRNH9pwdbEBpx5DTJYg+qxcVaDMAz3Ee/qDKwXujH5o=
+github.com/libp2p/go-libp2p v0.8.3/go.mod h1:EsH1A+8yoWK+L4iKcbPYu6MPluZ+CHWI9El8cTaefiM=
+github.com/libp2p/go-libp2p v0.9.2/go.mod h1:cunHNLDVus66Ct9iXXcjKRLdmHdFdHVe1TAnbubJQqQ=
+github.com/libp2p/go-libp2p v0.10.0/go.mod h1:yBJNpb+mGJdgrwbKAKrhPU0u3ogyNFTfjJ6bdM+Q/G8=
+github.com/libp2p/go-libp2p v0.11.0 h1:jb5mqdqYEBAybTEhD8io43Cz5LzVKuWxOK7znSN69jE=
+github.com/libp2p/go-libp2p v0.11.0/go.mod h1:3/ogJDXsbbepEfqtZKBR/DedzxJXCeK17t2Z9RE9bEE=
+github.com/libp2p/go-libp2p v0.12.0 h1:+xai9RQnQ9l5elFOKvp5wRyjyWisSwEx+6nU2+onpUA=
+github.com/libp2p/go-libp2p v0.12.0/go.mod h1:FpHZrfC1q7nA8jitvdjKBDF31hguaC676g/nT9PgQM0=
+github.com/libp2p/go-libp2p-asn-util v0.0.0-20200825225859-85005c6cf052 h1:BM7aaOF7RpmNn9+9g6uTjGJ0cTzWr5j9i9IKeun2M8U=
+github.com/libp2p/go-libp2p-asn-util v0.0.0-20200825225859-85005c6cf052/go.mod h1:nRMRTab+kZuk0LnKZpxhOVH/ndsdr2Nr//Zltc/vwgo=
+github.com/libp2p/go-libp2p-autonat v0.0.2/go.mod h1:fs71q5Xk+pdnKU014o2iq1RhMs9/PMaG5zXRFNnIIT4=
+github.com/libp2p/go-libp2p-autonat v0.0.6/go.mod h1:uZneLdOkZHro35xIhpbtTzLlgYturpu4J5+0cZK3MqE=
+github.com/libp2p/go-libp2p-autonat v0.1.0/go.mod h1:1tLf2yXxiE/oKGtDwPYWTSYG3PtvYlJmg7NeVtPRqH8=
+github.com/libp2p/go-libp2p-autonat v0.1.1/go.mod h1:OXqkeGOY2xJVWKAGV2inNF5aKN/djNA3fdpCWloIudE=
+github.com/libp2p/go-libp2p-autonat v0.2.0/go.mod h1:DX+9teU4pEEoZUqR1PiMlqliONQdNbfzE1C718tcViI=
+github.com/libp2p/go-libp2p-autonat v0.2.1/go.mod h1:MWtAhV5Ko1l6QBsHQNSuM6b1sRkXrpk0/LqCr+vCVxI=
+github.com/libp2p/go-libp2p-autonat v0.2.2/go.mod h1:HsM62HkqZmHR2k1xgX34WuWDzk/nBwNHoeyyT4IWV6A=
+github.com/libp2p/go-libp2p-autonat v0.2.3/go.mod h1:2U6bNWCNsAG9LEbwccBDQbjzQ8Krdjge1jLTE9rdoMM=
+github.com/libp2p/go-libp2p-autonat v0.3.2 h1:OhDSwVVaq7liTaRIsFFYvsaPp0pn2yi0WazejZ4DUmo=
+github.com/libp2p/go-libp2p-autonat v0.3.2/go.mod h1:0OzOi1/cVc7UcxfOddemYD5vzEqi4fwRbnZcJGLi68U=
+github.com/libp2p/go-libp2p-autonat v0.4.0 h1:3y8XQbpr+ssX8QfZUHekjHCYK64sj6/4hnf/awD4+Ug=
+github.com/libp2p/go-libp2p-autonat v0.4.0/go.mod h1:YxaJlpr81FhdOv3W3BTconZPfhaYivRdf53g+S2wobk=
+github.com/libp2p/go-libp2p-autonat-svc v0.1.0/go.mod h1:fqi8Obl/z3R4PFVLm8xFtZ6PBL9MlV/xumymRFkKq5A=
+github.com/libp2p/go-libp2p-blankhost v0.0.1/go.mod h1:Ibpbw/7cPPYwFb7PACIWdvxxv0t0XCCI10t7czjAjTc=
+github.com/libp2p/go-libp2p-blankhost v0.1.1/go.mod h1:pf2fvdLJPsC1FsVrNP3DUUvMzUts2dsLLBEpo1vW1ro=
+github.com/libp2p/go-libp2p-blankhost v0.1.3/go.mod h1:KML1//wiKR8vuuJO0y3LUd1uLv+tlkGTAr3jC0S5cLg=
+github.com/libp2p/go-libp2p-blankhost v0.1.4/go.mod h1:oJF0saYsAXQCSfDq254GMNmLNz6ZTHTOvtF4ZydUvwU=
+github.com/libp2p/go-libp2p-blankhost v0.1.6/go.mod h1:jONCAJqEP+Z8T6EQviGL4JsQcLx1LgTGtVqFNY8EMfQ=
+github.com/libp2p/go-libp2p-blankhost v0.2.0 h1:3EsGAi0CBGcZ33GwRuXEYJLLPoVWyXJ1bcJzAJjINkk=
+github.com/libp2p/go-libp2p-blankhost v0.2.0/go.mod h1:eduNKXGTioTuQAUcZ5epXi9vMl+t4d8ugUBRQ4SqaNQ=
+github.com/libp2p/go-libp2p-circuit v0.0.1/go.mod h1:Dqm0s/BiV63j8EEAs8hr1H5HudqvCAeXxDyic59lCwE=
+github.com/libp2p/go-libp2p-circuit v0.0.9/go.mod h1:uU+IBvEQzCu953/ps7bYzC/D/R0Ho2A9LfKVVCatlqU=
+github.com/libp2p/go-libp2p-circuit v0.1.0/go.mod h1:Ahq4cY3V9VJcHcn1SBXjr78AbFkZeIRmfunbA7pmFh8=
+github.com/libp2p/go-libp2p-circuit v0.1.1/go.mod h1:Ahq4cY3V9VJcHcn1SBXjr78AbFkZeIRmfunbA7pmFh8=
+github.com/libp2p/go-libp2p-circuit v0.1.3/go.mod h1:Xqh2TjSy8DD5iV2cCOMzdynd6h8OTBGoV1AWbWor3qM=
+github.com/libp2p/go-libp2p-circuit v0.1.4/go.mod h1:CY67BrEjKNDhdTk8UgBX1Y/H5c3xkAcs3gnksxY7osU=
+github.com/libp2p/go-libp2p-circuit v0.2.1/go.mod h1:BXPwYDN5A8z4OEY9sOfr2DUQMLQvKt/6oku45YUmjIo=
+github.com/libp2p/go-libp2p-circuit v0.2.2/go.mod h1:nkG3iE01tR3FoQ2nMm06IUrCpCyJp1Eo4A1xYdpjfs4=
+github.com/libp2p/go-libp2p-circuit v0.2.3/go.mod h1:nkG3iE01tR3FoQ2nMm06IUrCpCyJp1Eo4A1xYdpjfs4=
+github.com/libp2p/go-libp2p-circuit v0.3.1 h1:69ENDoGnNN45BNDnBd+8SXSetDuw0eJFcGmOvvtOgBw=
+github.com/libp2p/go-libp2p-circuit v0.3.1/go.mod h1:8RMIlivu1+RxhebipJwFDA45DasLx+kkrp4IlJj53F4=
+github.com/libp2p/go-libp2p-circuit v0.4.0 h1:eqQ3sEYkGTtybWgr6JLqJY6QLtPWRErvFjFDfAOO1wc=
+github.com/libp2p/go-libp2p-circuit v0.4.0/go.mod h1:t/ktoFIUzM6uLQ+o1G6NuBl2ANhBKN9Bc8jRIk31MoA=
+github.com/libp2p/go-libp2p-connmgr v0.1.1/go.mod h1:wZxh8veAmU5qdrfJ0ZBLcU8oJe9L82ciVP/fl1VHjXk=
+github.com/libp2p/go-libp2p-connmgr v0.2.3/go.mod h1:Gqjg29zI8CwXX21zRxy6gOg8VYu3zVerJRt2KyktzH4=
+github.com/libp2p/go-libp2p-connmgr v0.2.4 h1:TMS0vc0TCBomtQJyWr7fYxcVYYhx+q/2gF++G5Jkl/w=
+github.com/libp2p/go-libp2p-connmgr v0.2.4/go.mod h1:YV0b/RIm8NGPnnNWM7hG9Q38OeQiQfKhHCCs1++ufn0=
+github.com/libp2p/go-libp2p-core v0.0.1/go.mod h1:g/VxnTZ/1ygHxH3dKok7Vno1VfpvGcGip57wjTU4fco=
+github.com/libp2p/go-libp2p-core v0.0.2/go.mod h1:9dAcntw/n46XycV4RnlBq3BpgrmyUi9LuoTNdPrbUco=
+github.com/libp2p/go-libp2p-core v0.0.3/go.mod h1:j+YQMNz9WNSkNezXOsahp9kwZBKBvxLpKD316QWSJXE=
+github.com/libp2p/go-libp2p-core v0.0.4/go.mod h1:jyuCQP356gzfCFtRKyvAbNkyeuxb7OlyhWZ3nls5d2I=
+github.com/libp2p/go-libp2p-core v0.0.6/go.mod h1:0d9xmaYAVY5qmbp/fcgxHT3ZJsLjYeYPMJAUKpaCHrE=
+github.com/libp2p/go-libp2p-core v0.2.0/go.mod h1:X0eyB0Gy93v0DZtSYbEM7RnMChm9Uv3j7yRXjO77xSI=
+github.com/libp2p/go-libp2p-core v0.2.2/go.mod h1:8fcwTbsG2B+lTgRJ1ICZtiM5GWCWZVoVrLaDRvIRng0=
+github.com/libp2p/go-libp2p-core v0.2.3/go.mod h1:GqhyQqyIAPsxFYXHMjfXgMv03lxsvM0mFzuYA9Ib42A=
+github.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g=
+github.com/libp2p/go-libp2p-core v0.2.5/go.mod h1:6+5zJmKhsf7yHn1RbmYDu08qDUpIUxGdqHuEZckmZOA=
+github.com/libp2p/go-libp2p-core v0.3.0/go.mod h1:ACp3DmS3/N64c2jDzcV429ukDpicbL6+TrrxANBjPGw=
+github.com/libp2p/go-libp2p-core v0.3.1/go.mod h1:thvWy0hvaSBhnVBaW37BvzgVV68OUhgJJLAa6almrII=
+github.com/libp2p/go-libp2p-core v0.4.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0=
+github.com/libp2p/go-libp2p-core v0.5.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0=
+github.com/libp2p/go-libp2p-core v0.5.1/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y=
+github.com/libp2p/go-libp2p-core v0.5.2/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y=
+github.com/libp2p/go-libp2p-core v0.5.3/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y=
+github.com/libp2p/go-libp2p-core v0.5.4/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y=
+github.com/libp2p/go-libp2p-core v0.5.5/go.mod h1:vj3awlOr9+GMZJFH9s4mpt9RHHgGqeHCopzbYKZdRjM=
+github.com/libp2p/go-libp2p-core v0.5.6/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo=
+github.com/libp2p/go-libp2p-core v0.5.7/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo=
+github.com/libp2p/go-libp2p-core v0.6.0/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo=
+github.com/libp2p/go-libp2p-core v0.6.1 h1:XS+Goh+QegCDojUZp00CaPMfiEADCrLjNZskWE7pvqs=
+github.com/libp2p/go-libp2p-core v0.6.1/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8=
+github.com/libp2p/go-libp2p-core v0.7.0 h1:4a0TMjrWNTZlNvcqxZmrMRDi/NQWrhwO2pkTuLSQ/IQ=
+github.com/libp2p/go-libp2p-core v0.7.0/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=
+github.com/libp2p/go-libp2p-discovery v0.0.5/go.mod h1:YtF20GUxjgoKZ4zmXj8j3Nb2TUSBHFlOCetzYdbZL5I=
+github.com/libp2p/go-libp2p-discovery v0.1.0/go.mod h1:4F/x+aldVHjHDHuX85x1zWoFTGElt8HnoDzwkFZm29g=
+github.com/libp2p/go-libp2p-discovery v0.2.0/go.mod h1:s4VGaxYMbw4+4+tsoQTqh7wfxg97AEdo4GYBt6BadWg=
+github.com/libp2p/go-libp2p-discovery v0.3.0/go.mod h1:o03drFnz9BVAZdzC/QUQ+NeQOu38Fu7LJGEOK2gQltw=
+github.com/libp2p/go-libp2p-discovery v0.4.0/go.mod h1:bZ0aJSrFc/eX2llP0ryhb1kpgkPyTo23SJ5b7UQCMh4=
+github.com/libp2p/go-libp2p-discovery v0.5.0 h1:Qfl+e5+lfDgwdrXdu4YNCWyEo3fWuP+WgN9mN0iWviQ=
+github.com/libp2p/go-libp2p-discovery v0.5.0/go.mod h1:+srtPIU9gDaBNu//UHvcdliKBIcr4SfDcm0/PfPJLug=
+github.com/libp2p/go-libp2p-host v0.0.1/go.mod h1:qWd+H1yuU0m5CwzAkvbSjqKairayEHdR5MMl7Cwa7Go=
+github.com/libp2p/go-libp2p-host v0.0.3/go.mod h1:Y/qPyA6C8j2coYyos1dfRm0I8+nvd4TGrDGt4tA7JR8=
+github.com/libp2p/go-libp2p-interface-connmgr v0.0.1/go.mod h1:GarlRLH0LdeWcLnYM/SaBykKFl9U5JFnbBGruAk/D5k=
+github.com/libp2p/go-libp2p-interface-connmgr v0.0.4/go.mod h1:GarlRLH0LdeWcLnYM/SaBykKFl9U5JFnbBGruAk/D5k=
+github.com/libp2p/go-libp2p-interface-connmgr v0.0.5/go.mod h1:GarlRLH0LdeWcLnYM/SaBykKFl9U5JFnbBGruAk/D5k=
+github.com/libp2p/go-libp2p-interface-pnet v0.0.1/go.mod h1:el9jHpQAXK5dnTpKA4yfCNBZXvrzdOU75zz+C6ryp3k=
+github.com/libp2p/go-libp2p-kad-dht v0.2.1/go.mod h1:k7ONOlup7HKzQ68dE6lSnp07cdxdkmnRa+6B4Fh9/w0=
+github.com/libp2p/go-libp2p-kad-dht v0.8.3 h1:ceK5ML6s/I8UAcw6veoNsuEHdHvfo88leU/5uWOIFWs=
+github.com/libp2p/go-libp2p-kad-dht v0.8.3/go.mod h1:HnYYy8taJWESkqiESd1ngb9XX/XGGsMA5G0Vj2HoSh4=
+github.com/libp2p/go-libp2p-kad-dht v0.11.0 h1:ZLhlmDKsFiOkPhTzfEqBrMy/1Tqx+Dk6UgbHM5//IQM=
+github.com/libp2p/go-libp2p-kad-dht v0.11.0/go.mod h1:5ojtR2acDPqh/jXf5orWy8YGb8bHQDS+qeDcoscL/PI=
+github.com/libp2p/go-libp2p-kbucket v0.2.1/go.mod h1:/Rtu8tqbJ4WQ2KTCOMJhggMukOLNLNPY1EtEWWLxUvc=
+github.com/libp2p/go-libp2p-kbucket v0.4.2 h1:wg+VPpCtY61bCasGRexCuXOmEmdKjN+k1w+JtTwu9gA=
+github.com/libp2p/go-libp2p-kbucket v0.4.2/go.mod h1:7sCeZx2GkNK1S6lQnGUW5JYZCFPnXzAZCCBBS70lytY=
+github.com/libp2p/go-libp2p-kbucket v0.4.7 h1:spZAcgxifvFZHBD8tErvppbnNiKA5uokDu3CV7axu70=
+github.com/libp2p/go-libp2p-kbucket v0.4.7/go.mod h1:XyVo99AfQH0foSf176k4jY1xUJ2+jUJIZCSDm7r2YKk=
+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/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=
+github.com/libp2p/go-libp2p-mplex v0.2.1/go.mod h1:SC99Rxs8Vuzrf/6WhmH41kNn13TiYdAWNYHrwImKLnE=
+github.com/libp2p/go-libp2p-mplex v0.2.2/go.mod h1:74S9eum0tVQdAfFiKxAyKzNdSuLqw5oadDq7+L/FELo=
+github.com/libp2p/go-libp2p-mplex v0.2.3/go.mod h1:CK3p2+9qH9x+7ER/gWWDYJ3QW5ZxWDkm+dVvjfuG3ek=
+github.com/libp2p/go-libp2p-mplex v0.2.4 h1:XFFXaN4jhqnIuJVjYOR3k6bnRj0mFfJOlIuDVww+4Zo=
+github.com/libp2p/go-libp2p-mplex v0.2.4/go.mod h1:mI7iOezdWFOisvUwaYd3IDrJ4oVmgoXK8H331ui39CE=
+github.com/libp2p/go-libp2p-mplex v0.3.0 h1:CZyqqKP0BSGQyPLvpRQougbfXaaaJZdGgzhCpJNuNSk=
+github.com/libp2p/go-libp2p-mplex v0.3.0/go.mod h1:l9QWxRbbb5/hQMECEb908GbS9Sm2UAR2KFZKUJEynEs=
+github.com/libp2p/go-libp2p-nat v0.0.2/go.mod h1:QrjXQSD5Dj4IJOdEcjHRkWTSomyxRo6HnUkf/TfQpLQ=
+github.com/libp2p/go-libp2p-nat v0.0.4/go.mod h1:N9Js/zVtAXqaeT99cXgTV9e75KpnWCvVOiGzlcHmBbY=
+github.com/libp2p/go-libp2p-nat v0.0.5/go.mod h1:1qubaE5bTZMJE+E/uu2URroMbzdubFz1ChgiN79yKPE=
+github.com/libp2p/go-libp2p-nat v0.0.6 h1:wMWis3kYynCbHoyKLPBEMu4YRLltbm8Mk08HGSfvTkU=
+github.com/libp2p/go-libp2p-nat v0.0.6/go.mod h1:iV59LVhB3IkFvS6S6sauVTSOrNEANnINbI/fkaLimiw=
+github.com/libp2p/go-libp2p-net v0.0.1/go.mod h1:Yt3zgmlsHOgUWSXmt5V/Jpz9upuJBE8EgNU9DrCcR8c=
+github.com/libp2p/go-libp2p-net v0.0.2/go.mod h1:Yt3zgmlsHOgUWSXmt5V/Jpz9upuJBE8EgNU9DrCcR8c=
+github.com/libp2p/go-libp2p-netutil v0.0.1/go.mod h1:GdusFvujWZI9Vt0X5BKqwWWmZFxecf9Gt03cKxm2f/Q=
+github.com/libp2p/go-libp2p-netutil v0.1.0 h1:zscYDNVEcGxyUpMd0JReUZTrpMfia8PmLKcKF72EAMQ=
+github.com/libp2p/go-libp2p-netutil v0.1.0/go.mod h1:3Qv/aDqtMLTUyQeundkKsA+YCThNdbQD54k3TqjpbFU=
+github.com/libp2p/go-libp2p-noise v0.1.1/go.mod h1:QDFLdKX7nluB7DEnlVPbz7xlLHdwHFA9HiohJRr3vwM=
+github.com/libp2p/go-libp2p-noise v0.1.2 h1:IH9GRihQJTx56obm+GnpdPX4KeVIlvpXrP6xnJ0wxWk=
+github.com/libp2p/go-libp2p-noise v0.1.2/go.mod h1:9B10b7ueo7TIxZHHcjcDCo5Hd6kfKT2m77by82SFRfE=
+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=
+github.com/libp2p/go-libp2p-peerstore v0.1.0/go.mod h1:2CeHkQsr8svp4fZ+Oi9ykN1HBb6u0MOvdJ7YIsmcwtY=
+github.com/libp2p/go-libp2p-peerstore v0.1.3/go.mod h1:BJ9sHlm59/80oSkpWgr1MyY1ciXAXV397W6h1GH/uKI=
+github.com/libp2p/go-libp2p-peerstore v0.1.4/go.mod h1:+4BDbDiiKf4PzpANZDAT+knVdLxvqh7hXOujessqdzs=
+github.com/libp2p/go-libp2p-peerstore v0.2.0/go.mod h1:N2l3eVIeAitSg3Pi2ipSrJYnqhVnMNQZo9nkSCuAbnQ=
+github.com/libp2p/go-libp2p-peerstore v0.2.1/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRjwRLBr4TYKfNgrUkOPA=
+github.com/libp2p/go-libp2p-peerstore v0.2.2/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRjwRLBr4TYKfNgrUkOPA=
+github.com/libp2p/go-libp2p-peerstore v0.2.3/go.mod h1:K8ljLdFn590GMttg/luh4caB/3g0vKuY01psze0upRw=
+github.com/libp2p/go-libp2p-peerstore v0.2.4/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s=
+github.com/libp2p/go-libp2p-peerstore v0.2.6 h1:2ACefBX23iMdJU9Ke+dcXt3w86MIryes9v7In4+Qq3U=
+github.com/libp2p/go-libp2p-peerstore v0.2.6/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s=
+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/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=
+github.com/libp2p/go-libp2p-pubsub v0.3.2/go.mod h1:Uss7/Cfz872KggNb+doCVPHeCDmXB7z500m/R8DaAUk=
+github.com/libp2p/go-libp2p-pubsub v0.3.6 h1:9oO8W7qIWCYQYyz5z8nUsPcb3rrFehBlkbqvbSVjBxY=
+github.com/libp2p/go-libp2p-pubsub v0.3.6/go.mod h1:DTMSVmZZfXodB/pvdTGrY2eHPZ9W2ev7hzTH83OKHrI=
+github.com/libp2p/go-libp2p-pubsub v0.4.0 h1:YNVRyXqBgv9i4RG88jzoTtkSOaSB45CqHkL29NNBZb4=
+github.com/libp2p/go-libp2p-pubsub v0.4.0/go.mod h1:izkeMLvz6Ht8yAISXjx60XUQZMq9ZMe5h2ih4dLIBIQ=
+github.com/libp2p/go-libp2p-pubsub-tracer v0.0.0-20200626141350-e730b32bf1e6 h1:2lH7rMlvDPSvXeOR+g7FE6aqiEwxtpxWKQL8uigk5fQ=
+github.com/libp2p/go-libp2p-pubsub-tracer v0.0.0-20200626141350-e730b32bf1e6/go.mod h1:8ZodgKS4qRLayfw9FDKDd9DX4C16/GMofDxSldG8QPI=
+github.com/libp2p/go-libp2p-quic-transport v0.1.1/go.mod h1:wqG/jzhF3Pu2NrhJEvE+IE0NTHNXslOPn9JQzyCAxzU=
+github.com/libp2p/go-libp2p-quic-transport v0.5.0/go.mod h1:IEcuC5MLxvZ5KuHKjRu+dr3LjCT1Be3rcD/4d8JrX8M=
+github.com/libp2p/go-libp2p-quic-transport v0.8.2 h1:FDaXBCBJ1e5hY6gnWEJ4NbYyLk8eezr4J6AY3q3KqwM=
+github.com/libp2p/go-libp2p-quic-transport v0.8.2/go.mod h1:L+e0q15ZNaYm3seHgbsXjWP8kXLEqz+elLWKk9l8DhM=
+github.com/libp2p/go-libp2p-quic-transport v0.9.0 h1:WPuq5nV/chmIZIzvrkC2ulSdAQ0P0BDvgvAhZFOZ59E=
+github.com/libp2p/go-libp2p-quic-transport v0.9.0/go.mod h1:xyY+IgxL0qsW7Kiutab0+NlxM0/p9yRtrGTYsuMWf70=
+github.com/libp2p/go-libp2p-record v0.0.1/go.mod h1:grzqg263Rug/sRex85QrDOLntdFAymLDLm7lxMgU79Q=
+github.com/libp2p/go-libp2p-record v0.1.0/go.mod h1:ujNc8iuE5dlKWVy6wuL6dd58t0n7xI4hAIl8pE6wu5Q=
+github.com/libp2p/go-libp2p-record v0.1.1/go.mod h1:VRgKajOyMVgP/F0L5g3kH7SVskp17vFi2xheb5uMJtg=
+github.com/libp2p/go-libp2p-record v0.1.2/go.mod h1:pal0eNcT5nqZaTV7UGhqeGqxFgGdsU/9W//C8dqjQDk=
+github.com/libp2p/go-libp2p-record v0.1.3 h1:R27hoScIhQf/A8XJZ8lYpnqh9LatJ5YbHs28kCIfql0=
+github.com/libp2p/go-libp2p-record v0.1.3/go.mod h1:yNUff/adKIfPnYQXgp6FQmNu3gLJ6EMg7+/vv2+9pY4=
+github.com/libp2p/go-libp2p-routing v0.0.1/go.mod h1:N51q3yTr4Zdr7V8Jt2JIktVU+3xBBylx1MZeVA6t1Ys=
+github.com/libp2p/go-libp2p-routing v0.1.0/go.mod h1:zfLhI1RI8RLEzmEaaPwzonRvXeeSHddONWkcTcB54nE=
+github.com/libp2p/go-libp2p-routing-helpers v0.2.3 h1:xY61alxJ6PurSi+MXbywZpelvuU4U4p/gPTxjqCqTzY=
+github.com/libp2p/go-libp2p-routing-helpers v0.2.3/go.mod h1:795bh+9YeoFl99rMASoiVgHdi5bjack0N1+AFAdbvBw=
+github.com/libp2p/go-libp2p-secio v0.0.1/go.mod h1:IdG6iQybdcYmbTzxp4J5dwtUEDTOvZrT0opIDVNPrJs=
+github.com/libp2p/go-libp2p-secio v0.0.3/go.mod h1:hS7HQ00MgLhRO/Wyu1bTX6ctJKhVpm+j2/S2A5UqYb0=
+github.com/libp2p/go-libp2p-secio v0.1.0/go.mod h1:tMJo2w7h3+wN4pgU2LSYeiKPrfqBgkOsdiKK77hE7c8=
+github.com/libp2p/go-libp2p-secio v0.2.0/go.mod h1:2JdZepB8J5V9mBp79BmwsaPQhRPNN2NrnB2lKQcdy6g=
+github.com/libp2p/go-libp2p-secio v0.2.1/go.mod h1:cWtZpILJqkqrSkiYcDBh5lA3wbT2Q+hz3rJQq3iftD8=
+github.com/libp2p/go-libp2p-secio v0.2.2/go.mod h1:wP3bS+m5AUnFA+OFO7Er03uO1mncHG0uVwGrwvjYlNY=
+github.com/libp2p/go-libp2p-swarm v0.0.1/go.mod h1:mh+KZxkbd3lQnveQ3j2q60BM1Cw2mX36XXQqwfPOShs=
+github.com/libp2p/go-libp2p-swarm v0.0.6/go.mod h1:s5GZvzg9xXe8sbeESuFpjt8CJPTCa8mhEusweJqyFy8=
+github.com/libp2p/go-libp2p-swarm v0.1.0/go.mod h1:wQVsCdjsuZoc730CgOvh5ox6K8evllckjebkdiY5ta4=
+github.com/libp2p/go-libp2p-swarm v0.2.1/go.mod h1:x07b4zkMFo2EvgPV2bMTlNmdQc8i+74Jjio7xGvsTgU=
+github.com/libp2p/go-libp2p-swarm v0.2.2/go.mod h1:fvmtQ0T1nErXym1/aa1uJEyN7JzaTNyBcHImCxRpPKU=
+github.com/libp2p/go-libp2p-swarm v0.2.3/go.mod h1:P2VO/EpxRyDxtChXz/VPVXyTnszHvokHKRhfkEgFKNM=
+github.com/libp2p/go-libp2p-swarm v0.2.4/go.mod h1:/xIpHFPPh3wmSthtxdGbkHZ0OET1h/GGZes8Wku/M5Y=
+github.com/libp2p/go-libp2p-swarm v0.2.7/go.mod h1:ZSJ0Q+oq/B1JgfPHJAT2HTall+xYRNYp1xs4S2FBWKA=
+github.com/libp2p/go-libp2p-swarm v0.2.8 h1:cIUUvytBzNQmGSjnXFlI6UpoBGsaud82mJPIJVfkDlg=
+github.com/libp2p/go-libp2p-swarm v0.2.8/go.mod h1:JQKMGSth4SMqonruY0a8yjlPVIkb0mdNSwckW7OYziM=
+github.com/libp2p/go-libp2p-swarm v0.3.0/go.mod h1:hdv95GWCTmzkgeJpP+GK/9D9puJegb7H57B5hWQR5Kk=
+github.com/libp2p/go-libp2p-swarm v0.3.1 h1:UTobu+oQHGdXTOGpZ4RefuVqYoJXcT0EBtSR74m2LkI=
+github.com/libp2p/go-libp2p-swarm v0.3.1/go.mod h1:hdv95GWCTmzkgeJpP+GK/9D9puJegb7H57B5hWQR5Kk=
+github.com/libp2p/go-libp2p-testing v0.0.1/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E=
+github.com/libp2p/go-libp2p-testing v0.0.2/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E=
+github.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E=
+github.com/libp2p/go-libp2p-testing v0.0.4/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E=
+github.com/libp2p/go-libp2p-testing v0.1.0/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0=
+github.com/libp2p/go-libp2p-testing v0.1.1/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0=
+github.com/libp2p/go-libp2p-testing v0.1.2-0.20200422005655-8775583591d8 h1:v4dvk7YEW8buwCdIVWnhpv0Hp/AAJKRWIxBhmLRZrsk=
+github.com/libp2p/go-libp2p-testing v0.1.2-0.20200422005655-8775583591d8/go.mod h1:Qy8sAncLKpwXtS2dSnDOP8ktexIAHKu+J+pnZOFZLTc=
+github.com/libp2p/go-libp2p-testing v0.3.0 h1:ZiBYstPamsi7y6NJZebRudUzsYmVkt998hltyLqf8+g=
+github.com/libp2p/go-libp2p-testing v0.3.0/go.mod h1:efZkql4UZ7OVsEfaxNHZPzIehtsBXMrXnCfJIgDti5g=
+github.com/libp2p/go-libp2p-tls v0.1.3 h1:twKMhMu44jQO+HgQK9X8NHO5HkeJu2QbhLzLJpa8oNM=
+github.com/libp2p/go-libp2p-tls v0.1.3/go.mod h1:wZfuewxOndz5RTnCAxFliGjvYSDA40sKitV4c50uI1M=
+github.com/libp2p/go-libp2p-transport v0.0.1/go.mod h1:UzbUs9X+PHOSw7S3ZmeOxfnwaQY5vGDzZmKPod3N3tk=
+github.com/libp2p/go-libp2p-transport v0.0.4/go.mod h1:StoY3sx6IqsP6XKoabsPnHCwqKXWUMWU7Rfcsubee/A=
+github.com/libp2p/go-libp2p-transport v0.0.5/go.mod h1:StoY3sx6IqsP6XKoabsPnHCwqKXWUMWU7Rfcsubee/A=
+github.com/libp2p/go-libp2p-transport-upgrader v0.0.1/go.mod h1:NJpUAgQab/8K6K0m+JmZCe5RUXG10UMEx4kWe9Ipj5c=
+github.com/libp2p/go-libp2p-transport-upgrader v0.0.4/go.mod h1:RGq+tupk+oj7PzL2kn/m1w6YXxcIAYJYeI90h6BGgUc=
+github.com/libp2p/go-libp2p-transport-upgrader v0.1.1/go.mod h1:IEtA6or8JUbsV07qPW4r01GnTenLW4oi3lOPbUMGJJA=
+github.com/libp2p/go-libp2p-transport-upgrader v0.2.0/go.mod h1:mQcrHj4asu6ArfSoMuyojOdjx73Q47cYD7s5+gZOlns=
+github.com/libp2p/go-libp2p-transport-upgrader v0.3.0 h1:q3ULhsknEQ34eVDhv4YwKS8iet69ffs9+Fir6a7weN4=
+github.com/libp2p/go-libp2p-transport-upgrader v0.3.0/go.mod h1:i+SKzbRnvXdVbU3D1dwydnTmKRPXiAR/fyvi1dXuL4o=
+github.com/libp2p/go-libp2p-yamux v0.1.2/go.mod h1:xUoV/RmYkg6BW/qGxA9XJyg+HzXFYkeXbnhjmnYzKp8=
+github.com/libp2p/go-libp2p-yamux v0.1.3/go.mod h1:VGSQVrqkh6y4nm0189qqxMtvyBft44MOYYPpYKXiVt4=
+github.com/libp2p/go-libp2p-yamux v0.2.0/go.mod h1:Db2gU+XfLpm6E4rG5uGCFX6uXA8MEXOxFcRoXUODaK8=
+github.com/libp2p/go-libp2p-yamux v0.2.1/go.mod h1:1FBXiHDk1VyRM1C0aez2bCfHQ4vMZKkAQzZbkSQt5fI=
+github.com/libp2p/go-libp2p-yamux v0.2.2/go.mod h1:lIohaR0pT6mOt0AZ0L2dFze9hds9Req3OfS+B+dv4qw=
+github.com/libp2p/go-libp2p-yamux v0.2.5/go.mod h1:Zpgj6arbyQrmZ3wxSZxfBmbdnWtbZ48OpsfmQVTErwA=
+github.com/libp2p/go-libp2p-yamux v0.2.7/go.mod h1:X28ENrBMU/nm4I3Nx4sZ4dgjZ6VhLEn0XhIoZ5viCwU=
+github.com/libp2p/go-libp2p-yamux v0.2.8 h1:0s3ELSLu2O7hWKfX1YjzudBKCP0kZ+m9e2+0veXzkn4=
+github.com/libp2p/go-libp2p-yamux v0.2.8/go.mod h1:/t6tDqeuZf0INZMTgd0WxIRbtK2EzI2h7HbFm9eAKI4=
+github.com/libp2p/go-libp2p-yamux v0.4.0/go.mod h1:+DWDjtFMzoAwYLVkNZftoucn7PelNoy5nm3tZ3/Zw30=
+github.com/libp2p/go-libp2p-yamux v0.4.1 h1:TJxRVPY9SjH7TNrNC80l1OJMBiWhs1qpKmeB+1Ug3xU=
+github.com/libp2p/go-libp2p-yamux v0.4.1/go.mod h1:FA/NjRYRVNjqOzpGuGqcruH7jAU2mYIjtKBicVOL3dc=
+github.com/libp2p/go-maddr-filter v0.0.1/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q=
+github.com/libp2p/go-maddr-filter v0.0.4/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q=
+github.com/libp2p/go-maddr-filter v0.0.5/go.mod h1:Jk+36PMfIqCJhAnaASRH83bdAvfDRp/w6ENFaC9bG+M=
+github.com/libp2p/go-maddr-filter v0.1.0 h1:4ACqZKw8AqiuJfwFGq1CYDFugfXTOos+qQ3DETkhtCE=
+github.com/libp2p/go-maddr-filter v0.1.0/go.mod h1:VzZhTXkMucEGGEOSKddrwGiOv0tUhgnKqNEmIAz/bPU=
+github.com/libp2p/go-mplex v0.0.1/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTWe7l4Yd0=
+github.com/libp2p/go-mplex v0.0.3/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTWe7l4Yd0=
+github.com/libp2p/go-mplex v0.0.4/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTWe7l4Yd0=
+github.com/libp2p/go-mplex v0.1.0/go.mod h1:SXgmdki2kwCUlCCbfGLEgHjC4pFqhTp0ZoV6aiKgxDU=
+github.com/libp2p/go-mplex v0.1.1/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk=
+github.com/libp2p/go-mplex v0.1.2 h1:qOg1s+WdGLlpkrczDqmhYzyk3vCfsQ8+RxRTQjOZWwI=
+github.com/libp2p/go-mplex v0.1.2/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk=
+github.com/libp2p/go-mplex v0.2.0 h1:Ov/D+8oBlbRkjBs1R1Iua8hJ8cUfbdiW8EOdZuxcgaI=
+github.com/libp2p/go-mplex v0.2.0/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ=
+github.com/libp2p/go-msgio v0.0.1/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ=
+github.com/libp2p/go-msgio v0.0.2/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ=
+github.com/libp2p/go-msgio v0.0.3/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ=
+github.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ=
+github.com/libp2p/go-msgio v0.0.6 h1:lQ7Uc0kS1wb1EfRxO2Eir/RJoHkHn7t6o+EiwsYIKJA=
+github.com/libp2p/go-msgio v0.0.6/go.mod h1:4ecVB6d9f4BDSL5fqvPiC4A3KivjWn+Venn/1ALLMWA=
+github.com/libp2p/go-nat v0.0.3/go.mod h1:88nUEt0k0JD45Bk93NIwDqjlhiOwOoV36GchpcVc1yI=
+github.com/libp2p/go-nat v0.0.4/go.mod h1:Nmw50VAvKuk38jUBcmNh6p9lUJLoODbJRvYAa/+KSDo=
+github.com/libp2p/go-nat v0.0.5 h1:qxnwkco8RLKqVh1NmjQ+tJ8p8khNLFxuElYG/TwqW4Q=
+github.com/libp2p/go-nat v0.0.5/go.mod h1:B7NxsVNPZmRLvMOwiEO1scOSyjA56zxYAGv1yQgRkEU=
+github.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk=
+github.com/libp2p/go-netroute v0.1.3 h1:1ngWRx61us/EpaKkdqkMjKk/ufr/JlIFYQAxV2XX8Ig=
+github.com/libp2p/go-netroute v0.1.3/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk=
+github.com/libp2p/go-openssl v0.0.2/go.mod h1:v8Zw2ijCSWBQi8Pq5GAixw6DbFfa9u6VIYDXnvOXkc0=
+github.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=
+github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=
+github.com/libp2p/go-openssl v0.0.5/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=
+github.com/libp2p/go-openssl v0.0.7 h1:eCAzdLejcNVBzP/iZM9vqHnQm+XyCEbSSIheIPRGNsw=
+github.com/libp2p/go-openssl v0.0.7/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=
+github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA=
+github.com/libp2p/go-reuseport v0.0.2 h1:XSG94b1FJfGA01BUrT82imejHQyTxO4jEWqheyCXYvU=
+github.com/libp2p/go-reuseport v0.0.2/go.mod h1:SPD+5RwGC7rcnzngoYC86GjPzjSywuQyMVAheVBD9nQ=
+github.com/libp2p/go-reuseport-transport v0.0.1/go.mod h1:YkbSDrvjUVDL6b8XqriyA20obEtsW9BLkuOUyQAOCbs=
+github.com/libp2p/go-reuseport-transport v0.0.2/go.mod h1:YkbSDrvjUVDL6b8XqriyA20obEtsW9BLkuOUyQAOCbs=
+github.com/libp2p/go-reuseport-transport v0.0.3/go.mod h1:Spv+MPft1exxARzP2Sruj2Wb5JSyHNncjf1Oi2dEbzM=
+github.com/libp2p/go-reuseport-transport v0.0.4 h1:OZGz0RB620QDGpv300n1zaOcKGGAoGVf8h9txtt/1uM=
+github.com/libp2p/go-reuseport-transport v0.0.4/go.mod h1:trPa7r/7TJK/d+0hdBLOCGvpQQVOU74OXbNCIMkufGw=
+github.com/libp2p/go-sockaddr v0.0.2/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k=
+github.com/libp2p/go-sockaddr v0.1.0 h1:Y4s3/jNoryVRKEBrkJ576F17CPOaMIzUeCsg7dlTDj0=
+github.com/libp2p/go-sockaddr v0.1.0/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k=
+github.com/libp2p/go-stream-muxer v0.0.1/go.mod h1:bAo8x7YkSpadMTbtTaxGVHWUQsR/l5MEaHbKaliuT14=
+github.com/libp2p/go-stream-muxer v0.1.0/go.mod h1:8JAVsjeRBCWwPoZeH0W1imLOcriqXJyFvB0mR4A04sQ=
+github.com/libp2p/go-stream-muxer-multistream v0.1.1/go.mod h1:zmGdfkQ1AzOECIAcccoL8L//laqawOsO03zX8Sa+eGw=
+github.com/libp2p/go-stream-muxer-multistream v0.2.0/go.mod h1:j9eyPol/LLRqT+GPLSxvimPhNph4sfYfMoDPd7HkzIc=
+github.com/libp2p/go-stream-muxer-multistream v0.3.0 h1:TqnSHPJEIqDEO7h1wZZ0p3DXdvDSiLHQidKKUGZtiOY=
+github.com/libp2p/go-stream-muxer-multistream v0.3.0/go.mod h1:yDh8abSIzmZtqtOt64gFJUXEryejzNb0lisTt+fAMJA=
+github.com/libp2p/go-tcp-transport v0.0.1/go.mod h1:mnjg0o0O5TmXUaUIanYPUqkW4+u6mK0en8rlpA6BBTs=
+github.com/libp2p/go-tcp-transport v0.0.4/go.mod h1:+E8HvC8ezEVOxIo3V5vCK9l1y/19K427vCzQ+xHKH/o=
+github.com/libp2p/go-tcp-transport v0.1.0/go.mod h1:oJ8I5VXryj493DEJ7OsBieu8fcg2nHGctwtInJVpipc=
+github.com/libp2p/go-tcp-transport v0.1.1/go.mod h1:3HzGvLbx6etZjnFlERyakbaYPdfjg2pWP97dFZworkY=
+github.com/libp2p/go-tcp-transport v0.2.0/go.mod h1:vX2U0CnWimU4h0SGSEsg++AzvBcroCGYw28kh94oLe0=
+github.com/libp2p/go-tcp-transport v0.2.1 h1:ExZiVQV+h+qL16fzCWtd1HSzPsqWottJ8KXwWaVi8Ns=
+github.com/libp2p/go-tcp-transport v0.2.1/go.mod h1:zskiJ70MEfWz2MKxvFB/Pv+tPIB1PpPUrHIWQ8aFw7M=
+github.com/libp2p/go-testutil v0.0.1/go.mod h1:iAcJc/DKJQanJ5ws2V+u5ywdL2n12X1WbbEG+Jjy69I=
+github.com/libp2p/go-testutil v0.1.0/go.mod h1:81b2n5HypcVyrCg/MJx4Wgfp/VHojytjVe/gLzZ2Ehc=
+github.com/libp2p/go-ws-transport v0.0.1/go.mod h1:p3bKjDWHEgtuKKj+2OdPYs5dAPIjtpQGHF2tJfGz7Ww=
+github.com/libp2p/go-ws-transport v0.0.5/go.mod h1:Qbl4BxPfXXhhd/o0wcrgoaItHqA9tnZjoFZnxykuaXU=
+github.com/libp2p/go-ws-transport v0.1.0/go.mod h1:rjw1MG1LU9YDC6gzmwObkPd/Sqwhw7yT74kj3raBFuo=
+github.com/libp2p/go-ws-transport v0.1.2/go.mod h1:dsh2Ld8F+XNmzpkaAijmg5Is+e9l6/1tK/6VFOdN69Y=
+github.com/libp2p/go-ws-transport v0.2.0/go.mod h1:9BHJz/4Q5A9ludYWKoGCFC5gUElzlHoKzu0yY9p/klM=
+github.com/libp2p/go-ws-transport v0.3.0/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1fiZuEaBYusP5zsk=
+github.com/libp2p/go-ws-transport v0.3.1 h1:ZX5rWB8nhRRJVaPO6tmkGI/Xx8XNboYX20PW5hXIscw=
+github.com/libp2p/go-ws-transport v0.3.1/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1fiZuEaBYusP5zsk=
+github.com/libp2p/go-yamux v1.2.1/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
+github.com/libp2p/go-yamux v1.2.2/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
+github.com/libp2p/go-yamux v1.2.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
+github.com/libp2p/go-yamux v1.3.0/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
+github.com/libp2p/go-yamux v1.3.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
+github.com/libp2p/go-yamux v1.3.5/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
+github.com/libp2p/go-yamux v1.3.6/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
+github.com/libp2p/go-yamux v1.3.7 h1:v40A1eSPJDIZwz2AvrV3cxpTZEGDP11QJbukmEhYyQI=
+github.com/libp2p/go-yamux v1.3.7/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE=
+github.com/libp2p/go-yamux v1.4.0/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE=
+github.com/libp2p/go-yamux v1.4.1 h1:P1Fe9vF4th5JOxxgQvfbOHkrGqIZniTLf+ddhZp8YTI=
+github.com/libp2p/go-yamux v1.4.1/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE=
+github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
+github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
+github.com/lucas-clemente/quic-go v0.11.2/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw=
+github.com/lucas-clemente/quic-go v0.16.0/go.mod h1:I0+fcNTdb9eS1ZcjQZbDVPGchJ86chcIxPALn9lEJqE=
+github.com/lucas-clemente/quic-go v0.18.1 h1:DMR7guC0NtVS8zNZR3IO7NARZvZygkSC56GGtC6cyys=
+github.com/lucas-clemente/quic-go v0.18.1/go.mod h1:yXttHsSNxQi8AWijC/vLP+OJczXqzHSOcJrM5ITUlCg=
+github.com/lufia/iostat v1.1.0/go.mod h1:rEPNA0xXgjHQjuI5Cy05sLlS2oRcSlWHRLrvh/AQ+Pg=
+github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
+github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGDAUWqna+CNTrI=
+github.com/marten-seemann/qpack v0.2.0/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
+github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
+github.com/marten-seemann/qtls v0.9.1/go.mod h1:T1MmAdDPyISzxlK6kjRr0pcZFBVd1OZbBb/j3cvzHhk=
+github.com/marten-seemann/qtls v0.10.0 h1:ECsuYUKalRL240rRD4Ri33ISb7kAQ3qGDlrrl55b2pc=
+github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs=
+github.com/marten-seemann/qtls-go1-15 v0.1.0 h1:i/YPXVxz8q9umso/5y474CNcHmTpA+5DH+mFPjx6PZg=
+github.com/marten-seemann/qtls-go1-15 v0.1.0/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
+github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
+github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
+github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
+github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
+github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
+github.com/mattn/go-xmlrpc v0.0.3/go.mod h1:mqc2dz7tP5x5BKlCahN/n+hs7OSZKJkS9JsHNBRlrxA=
+github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc=
+github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
+github.com/mdlayher/netlink v0.0.0-20190828143259-340058475d09/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
+github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
+github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
+github.com/mdlayher/wifi v0.0.0-20190303161829-b1436901ddee/go.mod h1:Evt/EIne46u9PtQbeTx2NTcqURpr5K4SvKtGmBuDPN8=
+github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
+github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
+github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/miekg/dns v1.1.4/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/miekg/dns v1.1.28/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
+github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
+github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g=
+github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
+github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=
+github.com/minio/sha256-simd v0.0.0-20190328051042-05b4dd3047e5/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=
+github.com/minio/sha256-simd v0.1.0/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=
+github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
+github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=
+github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
+github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
+github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
+github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
+github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
+github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
+github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
+github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
+github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
+github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
+github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
+github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
+github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI=
+github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
+github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4=
+github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM=
+github.com/multiformats/go-multiaddr v0.0.1/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44=
+github.com/multiformats/go-multiaddr v0.0.2/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44=
+github.com/multiformats/go-multiaddr v0.0.4/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44=
+github.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44=
+github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo=
+github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4=
+github.com/multiformats/go-multiaddr v0.2.1/go.mod h1:s/Apk6IyxfvMjDafnhJgJ3/46z7tZ04iMk5wP4QMGGE=
+github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u0xW5UouOmQQrn6a3Y=
+github.com/multiformats/go-multiaddr v0.3.0/go.mod h1:dF9kph9wfJ+3VLAaeBqo9Of8x4fJxp6ggJGteB8HQTI=
+github.com/multiformats/go-multiaddr v0.3.1 h1:1bxa+W7j9wZKTZREySx1vPMs2TqrYWjVZ7zE6/XLG1I=
+github.com/multiformats/go-multiaddr v0.3.1/go.mod h1:uPbspcUPd5AfaP6ql3ujFY+QWzmBD8uLLL4bXW0XfGc=
+github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q=
+github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q=
+github.com/multiformats/go-multiaddr-dns v0.0.3/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q=
+github.com/multiformats/go-multiaddr-dns v0.1.0/go.mod h1:01k2RAqtoXIuPa3DCavAE9/6jc6nM0H3EgZyfUhN2oY=
+github.com/multiformats/go-multiaddr-dns v0.2.0 h1:YWJoIDwLePniH7OU5hBnDZV6SWuvJqJ0YtN6pLeH9zA=
+github.com/multiformats/go-multiaddr-dns v0.2.0/go.mod h1:TJ5pr5bBO7Y1B18djPuRsVkduhQH2YqYSbxWJzYGdK0=
+github.com/multiformats/go-multiaddr-fmt v0.0.1/go.mod h1:aBYjqL4T/7j4Qx+R73XSv/8JsgnRFlf0w2KGLCmXl3Q=
+github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E=
+github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo=
+github.com/multiformats/go-multiaddr-net v0.0.1/go.mod h1:nw6HSxNmCIQH27XPGBuX+d1tnvM7ihcFwHMSstNAVUU=
+github.com/multiformats/go-multiaddr-net v0.1.0/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ=
+github.com/multiformats/go-multiaddr-net v0.1.1/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ=
+github.com/multiformats/go-multiaddr-net v0.1.2/go.mod h1:QsWt3XK/3hwvNxZJp92iMQKME1qHfpYmyIjFVsSOY6Y=
+github.com/multiformats/go-multiaddr-net v0.1.3/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA=
+github.com/multiformats/go-multiaddr-net v0.1.4/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA=
+github.com/multiformats/go-multiaddr-net v0.1.5/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA=
+github.com/multiformats/go-multiaddr-net v0.2.0 h1:MSXRGN0mFymt6B1yo/6BPnIRpLPEnKgQNvVfCX5VDJk=
+github.com/multiformats/go-multiaddr-net v0.2.0/go.mod h1:gGdH3UXny6U3cKKYCvpXI5rnK7YaOIEOPVDI9tsJbEA=
+github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs=
+github.com/multiformats/go-multibase v0.0.2/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs=
+github.com/multiformats/go-multibase v0.0.3 h1:l/B6bJDQjvQ5G52jw4QGSYeOTZoAwIO77RblWplfIqk=
+github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc=
+github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U=
+github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po=
+github.com/multiformats/go-multihash v0.0.7/go.mod h1:XuKXPp8VHcTygube3OWZC+aZrA+H1IhmjoCDtJc7PXM=
+github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
+github.com/multiformats/go-multihash v0.0.9/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
+github.com/multiformats/go-multihash v0.0.10/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
+github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc=
+github.com/multiformats/go-multihash v0.0.14 h1:QoBceQYQQtNUuf6s7wHxnE2c8bhbMqhfGzNI032se/I=
+github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc=
+github.com/multiformats/go-multistream v0.0.1/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg=
+github.com/multiformats/go-multistream v0.0.4/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg=
+github.com/multiformats/go-multistream v0.1.0/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg=
+github.com/multiformats/go-multistream v0.1.1/go.mod h1:KmHZ40hzVxiaiwlj3MEbYgK9JFk2/9UktWZAF54Du38=
+github.com/multiformats/go-multistream v0.1.2 h1:knyamLYMPFPngQjGQ0lhnlys3jtVR/3xV6TREUJr+fE=
+github.com/multiformats/go-multistream v0.1.2/go.mod h1:5GZPQZbkWOLOn3J2y4Y99vVW7vOfsAflxARk3x14o6k=
+github.com/multiformats/go-multistream v0.2.0 h1:6AuNmQVKUkRnddw2YiDjt5Elit40SFxMJkVnhmETXtU=
+github.com/multiformats/go-multistream v0.2.0/go.mod h1:5GZPQZbkWOLOn3J2y4Y99vVW7vOfsAflxARk3x14o6k=
+github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
+github.com/multiformats/go-varint v0.0.2/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
+github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
+github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY=
+github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
+github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
+github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
+github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
+github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
+github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
+github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/nikkolasg/hexjson v0.0.0-20181101101858-78e39397e00c h1:5bFTChQxSKNwy8ALwOebjekYExl9HTT9urdawqC95tA=
+github.com/nikkolasg/hexjson v0.0.0-20181101101858-78e39397e00c/go.mod h1:7qN3Y0BvzRUf4LofcoJplQL10lsFDb4PYlePTVwrP28=
+github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E=
+github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
+github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
+github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
+github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
+github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
+github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
+github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
+github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
+github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
+github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
+github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
+github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
+github.com/opentracing-contrib/go-grpc v0.0.0-20180928155321-4b5a12d3ff02/go.mod h1:JNdpVEzCpXBgIiv4ds+TzhN1hrtxq6ClLrTlT9OQRSc=
+github.com/opentracing-contrib/go-grpc v0.0.0-20191001143057-db30781987df h1:vdYtBU6zvL7v+Tr+0xFM/qhahw/EvY8DMMunZHKH6eE=
+github.com/opentracing-contrib/go-grpc v0.0.0-20191001143057-db30781987df/go.mod h1:DYR5Eij8rJl8h7gblRrOZ8g0kW1umSpKqYIBTgeDtLo=
+github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
+github.com/opentracing-contrib/go-stdlib v0.0.0-20190519235532-cf7a6c988dc9/go.mod h1:PLldrQSroqzH70Xl+1DQcGnefIbqsKR7UDaiux3zV+w=
+github.com/opentracing-contrib/go-stdlib v1.0.0 h1:TBS7YuVotp8myLon4Pv7BtCBzOTo1DeZCld0Z63mW2w=
+github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU=
+github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
+github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
+github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
+github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
+github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
+github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
+github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
+github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
+github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
+github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE=
+github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
+github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
+github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
+github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
+github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
+github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/polydawn/refmt v0.0.0-20190221155625-df39d6c2d992/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o=
+github.com/polydawn/refmt v0.0.0-20190408063855-01bf1e26dd14/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o=
+github.com/polydawn/refmt v0.0.0-20190807091052-3d65705ee9f1/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o=
+github.com/polydawn/refmt v0.0.0-20190809202753-05966cbd336a h1:hjZfReYVLbqFkAtr2us7vdy04YWz3LVAirzP7reh8+M=
+github.com/polydawn/refmt v0.0.0-20190809202753-05966cbd336a/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o=
+github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
+github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
+github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
+github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
+github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
+github.com/prometheus/client_golang v1.6.0/go.mod h1:ZLOG9ck3JLRdB5MgO8f+lLTe83AXG6ro35rLTxvnIl4=
+github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
+github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
+github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
+github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
+github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
+github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
+github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
+github.com/prometheus/node_exporter v1.0.0-rc.0.0.20200428091818-01054558c289/go.mod h1:FGbBv5OPKjch+jNUJmEQpMZytIdyW0NdBtWFcfSKusc=
+github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
+github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
+github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.1.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
+github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/raulk/clock v1.1.0 h1:dpb29+UKMbLqiU/jqIJptgLR1nn23HLgMY0sTCDza5Y=
+github.com/raulk/clock v1.1.0/go.mod h1:3MpVxdZ/ODBQDxbN+kzshf5OSZwPjtMDx6BBXBmOeY0=
+github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
+github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ=
+github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
+github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
+github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
+github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc=
+github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rs/cors v1.6.0 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI=
+github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
+github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
+github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
+github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
+github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
+github.com/sercand/kuberesolver v2.1.0+incompatible/go.mod h1:lWF3GL0xptCB/vCiJPl/ZshwPsX/n4Y7u0CW9E7aQIQ=
+github.com/sercand/kuberesolver v2.4.0+incompatible h1:WE2OlRf6wjLxHwNkkFLQGaZcVLEXjMjBPjjEU5vksH8=
+github.com/sercand/kuberesolver v2.4.0+incompatible/go.mod h1:lWF3GL0xptCB/vCiJPl/ZshwPsX/n4Y7u0CW9E7aQIQ=
+github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
+github.com/shirou/gopsutil v2.18.12+incompatible h1:1eaJvGomDnH74/5cF4CTmTbLHAriGFsTZppLXDX93OM=
+github.com/shirou/gopsutil v2.18.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
+github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
+github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
+github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
+github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
+github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
+github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
+github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
+github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
+github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
+github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
+github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
+github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
+github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
+github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
+github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
+github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
+github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
+github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
+github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
+github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
+github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
+github.com/siebenmann/go-kstat v0.0.0-20160321171754-d34789b79745/go.mod h1:G81aIFAMS9ECrwBYR9YxhlPjWgrItd+Kje78O6+uqm8=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
+github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
+github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w=
+github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
+github.com/smartystreets/goconvey v0.0.0-20190222223459-a17d461953aa/go.mod h1:2RVY1rIf+2J2o/IM9+vPq9RzmHDSseB7FoXiSNIUsoU=
+github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
+github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY=
+github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
+github.com/soundcloud/go-runit v0.0.0-20150630195641-06ad41a06c4a/go.mod h1:LeFCbQYJ3KJlPs/FvPz2dy1tkpxyeNESVyCNNzRXFR0=
+github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
+github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
+github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0=
+github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU=
+github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
+github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+github.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc=
+github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
+github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
+github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+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/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=
+github.com/testground/sdk-go v0.2.6-0.20201016180515-1e40e1b0ec3a h1:iQDLQpTGtdfatdQtGqQBuoXFrl2AQ0n3Q8mNKkqbmnw=
+github.com/testground/sdk-go v0.2.6-0.20201016180515-1e40e1b0ec3a/go.mod h1:Q4dnWsUBH+dZ1u7aEGDBHWGUaLfhitjUq3UJQqxeTmk=
+github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e/go.mod h1:XDKHRm5ThF8YJjx001LtgelzsoaEcvnA7lVWz9EeX3g=
+github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
+github.com/tj/go-spin v1.1.0 h1:lhdWZsvImxvZ3q1C5OIB7d72DuOwP4O2NdBg9PyzNds=
+github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/uber/jaeger-client-go v2.15.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
+github.com/uber/jaeger-client-go v2.23.1+incompatible h1:uArBYHQR0HqLFFAypI7RsWTzPSj/bDpmZZuQjMLSg1A=
+github.com/uber/jaeger-client-go v2.23.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
+github.com/uber/jaeger-lib v1.5.1-0.20181102163054-1fc5c315e03c/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
+github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/GfSYVCjK7dyaw=
+github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
+github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
+github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
+github.com/urfave/cli/v2 v2.0.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
+github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4=
+github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
+github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
+github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
+github.com/wangjia184/sortedset v0.0.0-20160527075905-f5d03557ba30/go.mod h1:YkocrP2K2tcw938x9gCOmT5G5eCD6jsTz0SZuyAqwIE=
+github.com/warpfork/go-wish v0.0.0-20180510122957-5ad1f5abf436/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw=
+github.com/warpfork/go-wish v0.0.0-20190328234359-8b3e70f8e830/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw=
+github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a h1:G++j5e0OC488te356JvdhaM8YS6nMsjLAYF7JxCv07w=
+github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw=
+github.com/weaveworks/common v0.0.0-20200512154658-384f10054ec5 h1:EYxr08r8x6r/5fLEAMMkida1BVgxVXE4LfZv/XV+znU=
+github.com/weaveworks/common v0.0.0-20200512154658-384f10054ec5/go.mod h1:c98fKi5B9u8OsKGiWHLRKus6ToQ1Tubeow44ECO1uxY=
+github.com/weaveworks/promrus v1.2.0 h1:jOLf6pe6/vss4qGHjXmGz4oDJQA+AOCqEL3FvvZGz7M=
+github.com/weaveworks/promrus v1.2.0/go.mod h1:SaE82+OJ91yqjrE1rsvBWVzNZKcHYFtMUyS1+Ogs/KA=
+github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc/go.mod h1:r45hJU7yEoA81k6MWNhpMj/kms0n14dkzkxYHoB96UM=
+github.com/whyrusleeping/bencher v0.0.0-20190829221104-bb6607aa8bba h1:X4n8JG2e2biEZZXdBKt9HX7DN3bYGFUqljqqy0DqgnY=
+github.com/whyrusleeping/bencher v0.0.0-20190829221104-bb6607aa8bba/go.mod h1:CHQnYnQUEPydYCwuy8lmTHfGmdw9TKrhWV0xLx8l0oM=
+github.com/whyrusleeping/cbor-gen v0.0.0-20191216205031-b047b6acb3c0/go.mod h1:xdlJQaiqipF0HW+Mzpg7XRM3fWbGvfgFlcppuvlkIvY=
+github.com/whyrusleeping/cbor-gen v0.0.0-20200123233031-1cdf64d27158/go.mod h1:Xj/M2wWU+QdTdRbu/L/1dIZY8/Wb2K9pAhtroQuxJJI=
+github.com/whyrusleeping/cbor-gen v0.0.0-20200402171437-3d27c146c105/go.mod h1:Xj/M2wWU+QdTdRbu/L/1dIZY8/Wb2K9pAhtroQuxJJI=
+github.com/whyrusleeping/cbor-gen v0.0.0-20200414195334-429a0b5e922e/go.mod h1:Xj/M2wWU+QdTdRbu/L/1dIZY8/Wb2K9pAhtroQuxJJI=
+github.com/whyrusleeping/cbor-gen v0.0.0-20200504204219-64967432584d/go.mod h1:W5MvapuoHRP8rz4vxjwCK1pDqF1aQcWsV5PZ+AHbqdg=
+github.com/whyrusleeping/cbor-gen v0.0.0-20200710004633-5379fc63235d/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ=
+github.com/whyrusleeping/cbor-gen v0.0.0-20200715143311-227fab5a2377/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ=
+github.com/whyrusleeping/cbor-gen v0.0.0-20200723185710-6a3894a6352b/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ=
+github.com/whyrusleeping/cbor-gen v0.0.0-20200806213330-63aa96ca5488/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ=
+github.com/whyrusleeping/cbor-gen v0.0.0-20200810223238-211df3b9e24c/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ=
+github.com/whyrusleeping/cbor-gen v0.0.0-20200812213548-958ddffe352c/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ=
+github.com/whyrusleeping/cbor-gen v0.0.0-20200826160007-0b9f6c5fb163 h1:TtcUeY2XZSriVWR1pXyfCBWIf/NGC2iUdNw1lofUjUU=
+github.com/whyrusleeping/cbor-gen v0.0.0-20200826160007-0b9f6c5fb163/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ=
+github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f h1:jQa4QT2UP9WYv2nzyawpKMOCl+Z/jW7djv2/J50lj9E=
+github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f/go.mod h1:p9UJB6dDgdPgMJZs7UjUOdulKyRr9fqkS+6JKAInPy8=
+github.com/whyrusleeping/go-ctrlnet v0.0.0-20180313164037-f564fbbdaa95/go.mod h1:SJqKCCPXRfBFCwXjfNT/skfsceF7+MBFLI2OrvuRA7g=
+github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k=
+github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc=
+github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM=
+github.com/whyrusleeping/go-logging v0.0.1/go.mod h1:lDPYj54zutzG1XYfHAhcc7oNXEburHQBn+Iqd4yS4vE=
+github.com/whyrusleeping/go-notifier v0.0.0-20170827234753-097c5d47330f/go.mod h1:cZNvX9cFybI01GriPRMXDtczuvUhgbcYr9iCGaNlRv8=
+github.com/whyrusleeping/go-smux-multiplex v3.0.16+incompatible/go.mod h1:34LEDbeKFZInPUrAG+bjuJmUXONGdEFW7XL0SpTY1y4=
+github.com/whyrusleeping/go-smux-multistream v2.0.2+incompatible/go.mod h1:dRWHHvc4HDQSHh9gbKEBbUZ+f2Q8iZTPG3UOGYODxSQ=
+github.com/whyrusleeping/go-smux-yamux v2.0.8+incompatible/go.mod h1:6qHUzBXUbB9MXmw3AUdB52L8sEb/hScCqOdW2kj/wuI=
+github.com/whyrusleeping/go-smux-yamux v2.0.9+incompatible/go.mod h1:6qHUzBXUbB9MXmw3AUdB52L8sEb/hScCqOdW2kj/wuI=
+github.com/whyrusleeping/ledger-filecoin-go v0.9.1-0.20201010031517-c3dcc1bddce4 h1:NwiwjQDB3CzQ5XH0rdMh1oQqzJH7O2PSLWxif/w3zsY=
+github.com/whyrusleeping/ledger-filecoin-go v0.9.1-0.20201010031517-c3dcc1bddce4/go.mod h1:K+EVq8d5QcQ2At5VECsA+SNZvWefyBXh8TnIsxo1OvQ=
+github.com/whyrusleeping/mafmt v1.2.8/go.mod h1:faQJFPbLSxzD9xpA02ttW/tS9vZykNvXwGvqIpk20FA=
+github.com/whyrusleeping/mdns v0.0.0-20180901202407-ef14215e6b30/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4=
+github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4=
+github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 h1:E9S12nwJwEOXe2d6gT6qxdvqMnNq+VnSsKPgm2ZZNds=
+github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI=
+github.com/whyrusleeping/pubsub v0.0.0-20131020042734-02de8aa2db3d h1:wnjWu1N8UTNf2zzF5FWlEyNNbNw5GMVHaHaaLdvdTdA=
+github.com/whyrusleeping/pubsub v0.0.0-20131020042734-02de8aa2db3d/go.mod h1:g7ckxrjiFh8mi1AY7ox23PZD0g6QU/TxW3U3unX7I3A=
+github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee h1:lYbXeSvJi5zk5GLKVuid9TVjS9a0OmLIDKTfoZBL6Ow=
+github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee/go.mod h1:m2aV4LZI4Aez7dP5PMyVKEHhUyEJ/RjmPEDOpDvudHg=
+github.com/whyrusleeping/yamux v1.1.5/go.mod h1:E8LnQQ8HKx5KD29HZFUwM1PxCOdPRzGwur1mcYhXcD8=
+github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
+github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/xlab/c-for-go v0.0.0-20201112171043-ea6dce5809cb h1:/7/dQyiKnxAOj9L69FhST7uMe17U015XPzX7cy+5ykM=
+github.com/xlab/c-for-go v0.0.0-20201112171043-ea6dce5809cb/go.mod h1:pbNsDSxn1ICiNn9Ct4ZGNrwzfkkwYbx/lw8VuyutFIg=
+github.com/xlab/pkgconfig v0.0.0-20170226114623-cea12a0fd245 h1:Sw125DKxZhPUI4JLlWugkzsrlB50jR9v2khiD9FxuSo=
+github.com/xlab/pkgconfig v0.0.0-20170226114623-cea12a0fd245/go.mod h1:C+diUUz7pxhNY6KAoLgrTYARGWnt82zWTylZlxT92vk=
+github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
+github.com/xorcare/golden v0.6.0/go.mod h1:7T39/ZMvaSEZlBPoYfVFmsBLmUl3uz9IuzWj/U6FtvQ=
+github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542 h1:oWgZJmC1DorFZDpfMfWg7xk29yEOZiXmo/wZl+utTI8=
+github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542/go.mod h1:7T39/ZMvaSEZlBPoYfVFmsBLmUl3uz9IuzWj/U6FtvQ=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/zondax/hid v0.9.0 h1:eiT3P6vNxAEVxXMw66eZUAAnU2zD33JBkfG/EnfAKl8=
+github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM=
+github.com/zondax/ledger-go v0.12.1 h1:hYRcyznPRJp+5mzF2sazTLP2nGvGjYDD2VzhHhFomLU=
+github.com/zondax/ledger-go v0.12.1/go.mod h1:KatxXrVDzgWwbssUWsF5+cOJHXPvzQ09YSlzGNuhOEo=
+go.dedis.ch/fixbuf v1.0.3 h1:hGcV9Cd/znUxlusJ64eAlExS+5cJDIyTyEG+otu5wQs=
+go.dedis.ch/fixbuf v1.0.3/go.mod h1:yzJMt34Wa5xD37V5RTdmp38cz3QhMagdGoem9anUalw=
+go.dedis.ch/kyber/v3 v3.0.4/go.mod h1:OzvaEnPvKlyrWyp3kGXlFdp7ap1VC6RkZDTaPikqhsQ=
+go.dedis.ch/kyber/v3 v3.0.9 h1:i0ZbOQocHUjfFasBiUql5zVeC7u/vahFd96DFA8UOWk=
+go.dedis.ch/kyber/v3 v3.0.9/go.mod h1:rhNjUUg6ahf8HEg5HUvVBYoWY4boAafX8tYxX+PS+qg=
+go.dedis.ch/protobuf v1.0.5/go.mod h1:eIV4wicvi6JK0q/QnfIEGeSFNG0ZeB24kzut5+HaRLo=
+go.dedis.ch/protobuf v1.0.7/go.mod h1:pv5ysfkDX/EawiPqcW3ikOxsL5t+BqnV6xHSmE79KI4=
+go.dedis.ch/protobuf v1.0.11 h1:FTYVIEzY/bfl37lu3pR4lIj+F9Vp1jE8oh91VmxKgLo=
+go.dedis.ch/protobuf v1.0.11/go.mod h1:97QR256dnkimeNdfmURz0wAMNVbd1VmLXhG1CrTYrJ4=
+go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
+go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
+go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
+go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
+go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
+go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
+go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
+go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
+go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA=
+go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto=
+go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
+go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/dig v1.10.0 h1:yLmDDj9/zuDjv3gz8GQGviXMs9TfysIUMUilCpgzUJY=
+go.uber.org/dig v1.10.0/go.mod h1:X34SnWGr8Fyla9zQNO2GSO2D+TIuqB14OS8JhYocIyw=
+go.uber.org/fx v1.9.0 h1:7OAz8ucp35AU8eydejpYG7QrbE8rLKzGhHbZlJi5LYY=
+go.uber.org/fx v1.9.0/go.mod h1:mFdUyAUuJ3w4jAckiKSKbldsxy1ojpAMJ+dVZg5Y0Aw=
+go.uber.org/goleak v1.0.0 h1:qsup4IcBdlmsnGfqyLl4Ntn3C2XCCuKAE7DwHpScyUo=
+go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
+go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
+go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
+go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
+go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
+go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
+go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
+go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
+go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
+go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
+go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
+go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
+go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
+go4.org v0.0.0-20200411211856-f5505b9728dd h1:BNJlw5kRTzdmyfh5U8F93HA2OwkP7ZGwA51eJ/0wKOU=
+go4.org v0.0.0-20200411211856-f5505b9728dd/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg=
+golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
+golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
+golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
+golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
+golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
+golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp v0.0.0-20200513190911-00229845015e h1:rMqLP+9XLy+LdbCXHjJHAmTfXCr93W7oruWA6Hq1Alc=
+golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
+golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
+golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
+golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180524181706-dfa909b99c79/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190921015927-1a5e07d1ff72/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200519113804-d87ec0cfa476/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180202135801-37707fdb30a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190302025703-b6889370fb10/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190524122548-abf6ff778158/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190524152521-dbbf3f1254d4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190526052359-791d8a0f4d09/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190902133755-9109b7679e13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191025021431-6c3a3bfe00ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191025090151-53bf42e6b339/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200812155832-6a926be9bd1d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200926100807-9d91bd62050c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191030062658-86caa796c7ab/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200711155855-7342f9734a7d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200827010519-17fd2f27a9e3/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20201112185108-eeaa07dd7696 h1:Bfazo+enXJET5SbHeh95NtxabJF6fJ9r/jpfRJgd3j4=
+golang.org/x/tools v0.0.0-20201112185108-eeaa07dd7696/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
+gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
+gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU=
+gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
+gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
+gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
+google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
+google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
+google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
+google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
+google.golang.org/api v0.3.2/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
+google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
+google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
+google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
+google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
+google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
+google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20200608115520-7c474a2e3482 h1:i+Aiej6cta/Frzp13/swvwz5O00kYcSe0A/C5Wd7zX8=
+google.golang.org/genproto v0.0.0-20200608115520-7c474a2e3482/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
+google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
+google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
+google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
+google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
+google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
+google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
+google.golang.org/grpc v1.31.1 h1:SfXqXS5hkufcdZ/mHtYCh53P2b+92WQq/DZcKLgsFRs=
+google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
+google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
+gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk=
+gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+gopkg.in/src-d/go-cli.v0 v0.0.0-20181105080154-d492247bbc0d/go.mod h1:z+K8VcOYVYcSwSjGebuDL6176A1XskgbtNl64NSg+n8=
+gopkg.in/src-d/go-log.v1 v1.0.1/go.mod h1:GN34hKP0g305ysm2/hctJ0Y8nWP3zxXXJ8GFabTyABE=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
+gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
+gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
+grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
+honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
+honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+howett.net/plist v0.0.0-20181124034731-591f970eefbb h1:jhnBjNi9UFpfpl8YZhA9CrOqpnJdvzuiHsl/dnxl11M=
+howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
+launchpad.net/gocheck v0.0.0-20140225173054-000000000087 h1:Izowp2XBH6Ya6rv+hqbceQyw/gSGoXfH/UPoTGduL54=
+launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM=
+modernc.org/cc v1.0.0 h1:nPibNuDEx6tvYrUAtvDTTw98rx5juGsa5zuDnKwEEQQ=
+modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
+modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8=
+modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
+modernc.org/golex v1.0.1 h1:EYKY1a3wStt0RzHaH8mdSRNg78Ub0OHxYfCRWw35YtM=
+modernc.org/golex v1.0.1/go.mod h1:QCA53QtsT1NdGkaZZkF5ezFwk4IXh4BGNafAARTC254=
+modernc.org/lex v1.0.0/go.mod h1:G6rxMTy3cH2iA0iXL/HRRv4Znu8MK4higxph/lE7ypk=
+modernc.org/lexer v1.0.0/go.mod h1:F/Dld0YKYdZCLQ7bD0USbWL4YKCyTDRDHiDTOs0q0vk=
+modernc.org/mathutil v1.1.1 h1:FeylZSVX8S+58VsyJlkEj2bcpdytmp9MmDKZkKx8OIE=
+modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
+modernc.org/strutil v1.1.0 h1:+1/yCzZxY2pZwwrsbH+4T7BQMoLQ9QiBshRC9eicYsc=
+modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
+modernc.org/xc v1.0.0 h1:7ccXrupWZIS3twbUGrtKmHS2DXY6xegFua+6O3xgAFU=
+modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
+rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
+rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
+sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
+sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
+sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
diff --git a/testplans/lotus-soup/init.go b/testplans/lotus-soup/init.go
new file mode 100644
index 000000000..113e6c099
--- /dev/null
+++ b/testplans/lotus-soup/init.go
@@ -0,0 +1,52 @@
+package main
+
+import (
+ "os"
+
+ "github.com/filecoin-project/lotus/build"
+ "github.com/filecoin-project/lotus/chain/actors/policy"
+
+ "github.com/filecoin-project/go-state-types/abi"
+
+ "github.com/ipfs/go-log/v2"
+)
+
+func init() {
+ build.BlockDelaySecs = 2
+ build.PropagationDelaySecs = 1
+
+ _ = log.SetLogLevel("*", "WARN")
+ _ = log.SetLogLevel("dht/RtRefreshManager", "ERROR") // noisy
+ _ = log.SetLogLevel("bitswap", "ERROR") // noisy
+
+ _ = os.Setenv("BELLMAN_NO_GPU", "1")
+
+ build.InsecurePoStValidation = true
+ build.DisableBuiltinAssets = true
+
+ // MessageConfidence is the amount of tipsets we wait after a message is
+ // mined, e.g. payment channel creation, to be considered committed.
+ build.MessageConfidence = 1
+
+ // The duration of a deadline's challenge window, the period before a
+ // deadline when the challenge is available.
+ //
+ // This will auto-scale the proving period.
+ policy.SetWPoStChallengeWindow(abi.ChainEpoch(5))
+
+ // Number of epochs between publishing the precommit and when the challenge for interactive PoRep is drawn
+ // used to ensure it is not predictable by miner.
+ policy.SetPreCommitChallengeDelay(abi.ChainEpoch(10))
+
+ policy.SetConsensusMinerMinPower(abi.NewTokenAmount(2048))
+ policy.SetSupportedProofTypes(abi.RegisteredSealProof_StackedDrg2KiBV1)
+ policy.SetMinVerifiedDealSize(abi.NewTokenAmount(256))
+
+ // Disable upgrades.
+ build.UpgradeSmokeHeight = -1
+ build.UpgradeIgnitionHeight = -2
+ build.UpgradeLiftoffHeight = -3
+ // We need to _run_ this upgrade because genesis doesn't support v2, so
+ // we run it at height 0.
+ build.UpgradeActorsV2Height = 0
+}
diff --git a/testplans/lotus-soup/main.go b/testplans/lotus-soup/main.go
new file mode 100644
index 000000000..cc8851bb0
--- /dev/null
+++ b/testplans/lotus-soup/main.go
@@ -0,0 +1,24 @@
+package main
+
+import (
+ "github.com/filecoin-project/oni/lotus-soup/paych"
+ "github.com/filecoin-project/oni/lotus-soup/rfwp"
+ "github.com/filecoin-project/oni/lotus-soup/testkit"
+
+ "github.com/testground/sdk-go/run"
+)
+
+var cases = map[string]interface{}{
+ "deals-e2e": testkit.WrapTestEnvironment(dealsE2E),
+ "recovery-failed-windowed-post": testkit.WrapTestEnvironment(rfwp.RecoveryFromFailedWindowedPoStE2E),
+ "deals-stress": testkit.WrapTestEnvironment(dealsStress),
+ "drand-halting": testkit.WrapTestEnvironment(dealsE2E),
+ "drand-outage": testkit.WrapTestEnvironment(dealsE2E),
+ "paych-stress": testkit.WrapTestEnvironment(paych.Stress),
+}
+
+func main() {
+ sanityCheck()
+
+ run.InvokeMap(cases)
+}
diff --git a/testplans/lotus-soup/manifest.toml b/testplans/lotus-soup/manifest.toml
new file mode 100644
index 000000000..dca0355b0
--- /dev/null
+++ b/testplans/lotus-soup/manifest.toml
@@ -0,0 +1,215 @@
+name = "lotus-soup"
+
+[defaults]
+builder = "docker:go"
+runner = "local:docker"
+
+[builders."exec:go"]
+enabled = true
+
+[builders."docker:go"]
+enabled = true
+build_base_image = "iptestground/oni-buildbase:v11-lotus"
+runtime_image = "iptestground/oni-runtime:v5"
+
+[runners."local:exec"]
+enabled = true
+
+[runners."local:docker"]
+enabled = true
+
+[runners."cluster:k8s"]
+enabled = true
+
+######################
+##
+## Testcases
+##
+######################
+
+[[testcases]]
+name = "deals-e2e"
+instances = { min = 1, max = 100, default = 5 }
+
+ [testcases.params]
+ clients = { type = "int", default = 1 }
+ miners = { type = "int", default = 1 }
+ balance = { type = "float", default = 1 }
+ sectors = { type = "int", default = 1 }
+ role = { type = "string" }
+
+ genesis_timestamp_offset = { type = "int", default = 0 }
+
+ random_beacon_type = { type = "enum", default = "mock", options = ["mock", "local-drand", "external-drand"] }
+
+ # Params relevant to drand nodes. drand nodes should have role="drand", and must all be
+ # in the same composition group. There must be at least threshold drand nodes.
+ # To get lotus nodes to actually use the drand nodes, you must set random_beacon_type="local-drand"
+ # for the lotus node groups.
+ drand_period = { type = "duration", default="10s" }
+ drand_threshold = { type = "int", default = 2 }
+ drand_gossip_relay = { type = "bool", default = true }
+ drand_log_level = { type = "string", default="info" }
+
+ # Params relevant to pubsub tracing
+ enable_pubsub_tracer = { type = "bool", default = false }
+ mining_mode = { type = "enum", default = "synchronized", options = ["synchronized", "natural"] }
+
+ # Fast retrieval
+ fast_retrieval = { type = "bool", default = false }
+
+
+[[testcases]]
+name = "drand-halting"
+instances = { min = 1, max = 100, default = 5 }
+
+ [testcases.params]
+ clients = { type = "int", default = 1 }
+ miners = { type = "int", default = 1 }
+ balance = { type = "float", default = 1 }
+ sectors = { type = "int", default = 1 }
+ role = { type = "string" }
+ genesis_timestamp_offset = { type = "int", default = 0 }
+
+
+ random_beacon_type = { type = "enum", default = "local-drand", options = ["mock", "local-drand", "external-drand"] }
+
+ # Params relevant to drand nodes. drand nodes should have role="drand", and must all be
+ # in the same composition group. There must be at least threshold drand nodes.
+ # To get lotus nodes to actually use the drand nodes, you must set random_beacon_type="local-drand"
+ # for the lotus node groups.
+ drand_period = { type = "duration", default="10s" }
+ drand_threshold = { type = "int", default = 2 }
+ drand_gossip_relay = { type = "bool", default = true }
+ drand_log_level = { type = "string", default="info" }
+ suspend_events = { type = "string", default="", desc = "a sequence of halt/resume/wait events separated by '->'" }
+
+ # Params relevant to pubsub tracing
+ enable_pubsub_tracer = { type = "bool", default = false } # Mining Mode: synchronized -vs- natural time
+ mining_mode = { type = "enum", default = "synchronized", options = ["synchronized", "natural"] }
+
+
+[[testcases]]
+name = "drand-outage"
+instances = { min = 1, max = 100, default = 5 }
+
+ [testcases.params]
+ clients = { type = "int", default = 0 }
+ miners = { type = "int", default = 3 }
+ balance = { type = "float", default = 1 }
+ sectors = { type = "int", default = 1 }
+ role = { type = "string" }
+ genesis_timestamp_offset = { type = "int", default = 0 }
+
+
+ random_beacon_type = { type = "enum", default = "local-drand", options = ["mock", "local-drand", "external-drand"] }
+
+ # Params relevant to drand nodes. drand nodes should have role="drand", and must all be
+ # in the same composition group. There must be at least threshold drand nodes.
+ # To get lotus nodes to actually use the drand nodes, you must set random_beacon_type="local-drand"
+ # for the lotus node groups.
+ drand_period = { type = "duration", default="30s" }
+ drand_catchup_period = { type = "duration", default="10s" }
+ drand_threshold = { type = "int", default = 2 }
+ drand_gossip_relay = { type = "bool", default = true }
+ drand_log_level = { type = "string", default="info" }
+ suspend_events = { type = "string", default="", desc = "a sequence of halt/resume/wait events separated by '->'" }
+
+ # Params relevant to pubsub tracing
+ enable_pubsub_tracer = { type = "bool", default = false } # Mining Mode: synchronized -vs- natural time
+ mining_mode = { type = "enum", default = "synchronized", options = ["synchronized", "natural"] }
+
+
+[[testcases]]
+name = "deals-stress"
+instances = { min = 1, max = 100, default = 5 }
+
+ [testcases.params]
+ clients = { type = "int", default = 1 }
+ miners = { type = "int", default = 1 }
+ balance = { type = "float", default = 1 }
+ sectors = { type = "int", default = 1 }
+ role = { type = "string" }
+
+ genesis_timestamp_offset = { type = "int", default = 0 }
+
+ random_beacon_type = { type = "enum", default = "mock", options = ["mock", "local-drand", "external-drand"] }
+
+ # Params relevant to drand nodes. drand nodes should have role="drand", and must all be
+ # in the same composition group. There must be at least threshold drand nodes.
+ # To get lotus nodes to actually use the drand nodes, you must set random_beacon_type="local-drand"
+ # for the lotus node groups.
+ drand_period = { type = "duration", default="10s" }
+ drand_threshold = { type = "int", default = 2 }
+ drand_gossip_relay = { type = "bool", default = true }
+
+ # Params relevant to pubsub tracing
+ enable_pubsub_tracer = { type = "bool", default = false }
+
+ # Mining Mode: synchronized -vs- natural time
+ mining_mode = { type = "enum", default = "synchronized", options = ["synchronized", "natural"] }
+
+ deals = { type = "int", default = 1 }
+ deal_mode = { type = "enum", default = "serial", options = ["serial", "concurrent"] }
+
+
+[[testcases]]
+name = "paych-stress"
+instances = { min = 1, max = 100, default = 5 }
+
+ [testcases.params]
+ clients = { type = "int", default = 1 }
+ miners = { type = "int", default = 1 }
+ balance = { type = "float", default = 1 }
+ sectors = { type = "int", default = 1 }
+ role = { type = "string" }
+ genesis_timestamp_offset = { type = "int", default = 0 }
+
+ random_beacon_type = { type = "enum", default = "local-drand", options = ["mock", "local-drand", "external-drand"] }
+
+ # Params relevant to drand nodes. drand nodes should have role="drand", and must all be
+ # in the same composition group. There must be at least threshold drand nodes.
+ # To get lotus nodes to actually use the drand nodes, you must set random_beacon_type="local-drand"
+ # for the lotus node groups.
+ drand_period = { type = "duration", default="10s" }
+ drand_threshold = { type = "int", default = 2 }
+ drand_gossip_relay = { type = "bool", default = true }
+ drand_log_level = { type = "string", default="info" }
+ suspend_events = { type = "string", default="", desc = "a sequence of halt/resume/wait events separated by '->'" }
+
+ # Params relevant to pubsub tracing
+ enable_pubsub_tracer = { type = "bool", default = false } # Mining Mode: synchronized -vs- natural time
+ mining_mode = { type = "enum", default = "synchronized", options = ["synchronized", "natural"] }
+
+ # ********** Test-case specific **********
+ increments = { type = "int", default = "100", desc = "increments in which to send payment vouchers" }
+ lane_count = { type = "int", default = "256", desc = "lanes to open; vouchers will be distributed across these lanes in round-robin fashion" }
+
+
+[[testcases]]
+name = "recovery-failed-windowed-post"
+instances = { min = 1, max = 100, default = 5 }
+
+ [testcases.params]
+ clients = { type = "int", default = 1 }
+ miners = { type = "int", default = 1 }
+ balance = { type = "int", default = 1 }
+ sectors = { type = "int", default = 1 }
+ role = { type = "string" }
+
+ genesis_timestamp_offset = { type = "int", default = 0 }
+
+ random_beacon_type = { type = "enum", default = "mock", options = ["mock", "local-drand", "external-drand"] }
+
+ # Params relevant to drand nodes. drand nodes should have role="drand", and must all be
+ # in the same composition group. There must be at least threshold drand nodes.
+ # To get lotus nodes to actually use the drand nodes, you must set random_beacon_type="local-drand"
+ # for the lotus node groups.
+ drand_period = { type = "duration", default="10s" }
+ drand_threshold = { type = "int", default = 2 }
+ drand_gossip_relay = { type = "bool", default = true }
+ drand_log_level = { type = "string", default="info" }
+
+ # Params relevant to pubsub tracing
+ enable_pubsub_tracer = { type = "bool", default = false }
+ mining_mode = { type = "enum", default = "synchronized", options = ["synchronized", "natural"] }
diff --git a/testplans/lotus-soup/paych/README.md b/testplans/lotus-soup/paych/README.md
new file mode 100644
index 000000000..dbd5879ed
--- /dev/null
+++ b/testplans/lotus-soup/paych/README.md
@@ -0,0 +1,32 @@
+# Payment channels end-to-end tests
+
+This package contains the following test cases, each of which is described
+further below.
+
+- Payment channels stress test case (`stress.go`).
+
+## Payment channels stress test case (`stress.go`)
+
+***WIP | blocked due to https://github.com/filecoin-project/lotus/issues/2297***
+
+This test case turns all clients into payment receivers and senders.
+The first member to start in the group becomes the _receiver_.
+All other members become _senders_.
+
+The _senders_ will open a single payment channel to the _receiver_, and will
+wait for the message to be posted on-chain. We are setting
+`build.MessageConfidence=1`, in order to accelerate the test. So we'll only wait
+for a single tipset confirmation once we witness the message.
+
+Once the message is posted, we load the payment channel actor address and create
+as many lanes as the `lane_count` test parameter dictates.
+
+When then fetch our total balance, and start sending it on the payment channel,
+round-robinning across all lanes, until our balance is extinguished.
+
+**TODO:**
+
+- [ ] Assertions, metrics, etc. Actually gather statistics. Right now this is
+ just a smoke test, and it fails.
+- [ ] Implement the _receiver_ logic.
+- [ ] Model test lifetime by signalling end.
\ No newline at end of file
diff --git a/testplans/lotus-soup/paych/stress.go b/testplans/lotus-soup/paych/stress.go
new file mode 100644
index 000000000..0ca78580e
--- /dev/null
+++ b/testplans/lotus-soup/paych/stress.go
@@ -0,0 +1,313 @@
+package paych
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "time"
+
+ "github.com/ipfs/go-cid"
+
+ "github.com/filecoin-project/lotus/build"
+ "github.com/filecoin-project/specs-actors/actors/builtin/paych"
+
+ "github.com/filecoin-project/go-address"
+ "github.com/filecoin-project/go-state-types/big"
+ "github.com/testground/sdk-go/sync"
+
+ "github.com/filecoin-project/oni/lotus-soup/testkit"
+)
+
+var SendersDoneState = sync.State("senders-done")
+var ReceiverReadyState = sync.State("receiver-ready")
+var ReceiverAddedVouchersState = sync.State("receiver-added-vouchers")
+
+var VoucherTopic = sync.NewTopic("voucher", &paych.SignedVoucher{})
+var SettleTopic = sync.NewTopic("settle", cid.Cid{})
+
+type ClientMode uint64
+
+const (
+ ModeSender ClientMode = iota
+ ModeReceiver
+)
+
+func (cm ClientMode) String() string {
+ return [...]string{"Sender", "Receiver"}[cm]
+}
+
+func getClientMode(groupSeq int64) ClientMode {
+ if groupSeq == 1 {
+ return ModeReceiver
+ }
+ return ModeSender
+}
+
+// TODO Stress is currently WIP. We found blockers in Lotus that prevent us from
+// making progress. See https://github.com/filecoin-project/lotus/issues/2297.
+func Stress(t *testkit.TestEnvironment) error {
+ // Dispatch/forward non-client roles to defaults.
+ if t.Role != "client" {
+ return testkit.HandleDefaultRole(t)
+ }
+
+ // This is a client role.
+ t.RecordMessage("running payments client")
+
+ ctx := context.Background()
+ cl, err := testkit.PrepareClient(t)
+ if err != nil {
+ return err
+ }
+
+ // are we the receiver or a sender?
+ mode := getClientMode(t.GroupSeq)
+ t.RecordMessage("acting as %s", mode)
+
+ var clients []*testkit.ClientAddressesMsg
+ sctx, cancel := context.WithCancel(ctx)
+ clientsCh := make(chan *testkit.ClientAddressesMsg)
+ t.SyncClient.MustSubscribe(sctx, testkit.ClientsAddrsTopic, clientsCh)
+ for i := 0; i < t.TestGroupInstanceCount; i++ {
+ clients = append(clients, <-clientsCh)
+ }
+ cancel()
+
+ switch mode {
+ case ModeReceiver:
+ err := runReceiver(t, ctx, cl)
+ if err != nil {
+ return err
+ }
+
+ case ModeSender:
+ err := runSender(ctx, t, clients, cl)
+ if err != nil {
+ return err
+ }
+ }
+
+ // Signal that the client is done
+ t.SyncClient.MustSignalEntry(ctx, testkit.StateDone)
+
+ // Signal to the miners to stop mining
+ t.SyncClient.MustSignalEntry(ctx, testkit.StateStopMining)
+
+ return nil
+}
+
+func runSender(ctx context.Context, t *testkit.TestEnvironment, clients []*testkit.ClientAddressesMsg, cl *testkit.LotusClient) error {
+ var (
+ // lanes to open; vouchers will be distributed across these lanes in round-robin fashion
+ laneCount = t.IntParam("lane_count")
+ // number of vouchers to send on each lane
+ vouchersPerLane = t.IntParam("vouchers_per_lane")
+ // increments in which to send payment vouchers
+ increments = big.Mul(big.NewInt(int64(t.IntParam("increments"))), big.NewInt(int64(build.FilecoinPrecision)))
+ // channel amount should be enough to cover all vouchers
+ channelAmt = big.Mul(big.NewInt(int64(laneCount*vouchersPerLane)), increments)
+ )
+
+ // Lock up funds in the payment channel.
+ recv := findReceiver(clients)
+ balance, err := cl.FullApi.WalletBalance(ctx, cl.Wallet.Address)
+ if err != nil {
+ return fmt.Errorf("failed to acquire wallet balance: %w", err)
+ }
+
+ t.RecordMessage("my balance: %d", balance)
+ t.RecordMessage("creating payment channel; from=%s, to=%s, funds=%d", cl.Wallet.Address, recv.WalletAddr, channelAmt)
+
+ pid := os.Getpid()
+ t.RecordMessage("sender pid: %d", pid)
+
+ time.Sleep(20 * time.Second)
+
+ channel, err := cl.FullApi.PaychGet(ctx, cl.Wallet.Address, recv.WalletAddr, channelAmt)
+ if err != nil {
+ return fmt.Errorf("failed to create payment channel: %w", err)
+ }
+
+ if addr := channel.Channel; addr != address.Undef {
+ return fmt.Errorf("expected an Undef channel address, got: %s", addr)
+ }
+
+ t.RecordMessage("payment channel created; msg_cid=%s", channel.WaitSentinel)
+ t.RecordMessage("waiting for payment channel message to appear on chain")
+
+ // wait for the channel creation message to appear on chain.
+ _, err = cl.FullApi.StateWaitMsg(ctx, channel.WaitSentinel, 2)
+ if err != nil {
+ return fmt.Errorf("failed while waiting for payment channel creation msg to appear on chain: %w", err)
+ }
+
+ // need to wait so that the channel is tracked.
+ // the full API waits for build.MessageConfidence (=1 in tests) before tracking the channel.
+ // we wait for 2 confirmations, so we have the assurance the channel is tracked.
+
+ t.RecordMessage("get payment channel address")
+ channelAddr, err := cl.FullApi.PaychGetWaitReady(ctx, channel.WaitSentinel)
+ if err != nil {
+ return fmt.Errorf("failed to get payment channel address: %w", err)
+ }
+
+ t.RecordMessage("channel address: %s", channelAddr)
+ t.RecordMessage("allocating lanes; count=%d", laneCount)
+
+ // allocate as many lanes as required
+ var lanes []uint64
+ for i := 0; i < laneCount; i++ {
+ lane, err := cl.FullApi.PaychAllocateLane(ctx, channelAddr)
+ if err != nil {
+ return fmt.Errorf("failed to allocate lane: %w", err)
+ }
+ lanes = append(lanes, lane)
+ }
+
+ t.RecordMessage("lanes allocated; count=%d", laneCount)
+
+ <-t.SyncClient.MustBarrier(ctx, ReceiverReadyState, 1).C
+
+ t.RecordMessage("sending payments in round-robin fashion across lanes; increments=%d", increments)
+
+ // create vouchers
+ remaining := channelAmt
+ for i := 0; i < vouchersPerLane; i++ {
+ for _, lane := range lanes {
+ voucherAmt := big.Mul(big.NewInt(int64(i+1)), increments)
+ voucher, err := cl.FullApi.PaychVoucherCreate(ctx, channelAddr, voucherAmt, lane)
+ if err != nil {
+ return fmt.Errorf("failed to create voucher: %w", err)
+ }
+ t.RecordMessage("payment voucher created; lane=%d, nonce=%d, amount=%d", voucher.Voucher.Lane, voucher.Voucher.Nonce, voucher.Voucher.Amount)
+
+ _, err = t.SyncClient.Publish(ctx, VoucherTopic, voucher.Voucher)
+ if err != nil {
+ return fmt.Errorf("failed to publish voucher: %w", err)
+ }
+
+ remaining = big.Sub(remaining, increments)
+ t.RecordMessage("remaining balance: %d", remaining)
+ }
+ }
+
+ t.RecordMessage("finished sending all payment vouchers")
+
+ // Inform the receiver that all vouchers have been created
+ t.SyncClient.MustSignalEntry(ctx, SendersDoneState)
+
+ // Wait for the receiver to add all vouchers
+ <-t.SyncClient.MustBarrier(ctx, ReceiverAddedVouchersState, 1).C
+
+ t.RecordMessage("settle channel")
+
+ // Settle the channel. When the receiver sees the settle message, they
+ // should automatically submit all vouchers.
+ settleMsgCid, err := cl.FullApi.PaychSettle(ctx, channelAddr)
+ if err != nil {
+ return fmt.Errorf("failed to settle payment channel: %w", err)
+ }
+
+ t.SyncClient.Publish(ctx, SettleTopic, settleMsgCid)
+ if err != nil {
+ return fmt.Errorf("failed to publish settle message cid: %w", err)
+ }
+
+ return nil
+}
+
+func findReceiver(clients []*testkit.ClientAddressesMsg) *testkit.ClientAddressesMsg {
+ for _, c := range clients {
+ if getClientMode(c.GroupSeq) == ModeReceiver {
+ return c
+ }
+ }
+ return nil
+}
+
+func runReceiver(t *testkit.TestEnvironment, ctx context.Context, cl *testkit.LotusClient) error {
+ // lanes to open; vouchers will be distributed across these lanes in round-robin fashion
+ laneCount := t.IntParam("lane_count")
+ // number of vouchers to send on each lane
+ vouchersPerLane := t.IntParam("vouchers_per_lane")
+ totalVouchers := laneCount * vouchersPerLane
+
+ vouchers := make(chan *paych.SignedVoucher)
+ vouchersSub, err := t.SyncClient.Subscribe(ctx, VoucherTopic, vouchers)
+ if err != nil {
+ return fmt.Errorf("failed to subscribe to voucher topic: %w", err)
+ }
+
+ settleMsgChan := make(chan cid.Cid)
+ settleSub, err := t.SyncClient.Subscribe(ctx, SettleTopic, settleMsgChan)
+ if err != nil {
+ return fmt.Errorf("failed to subscribe to settle topic: %w", err)
+ }
+
+ // inform the clients that the receiver is ready for incoming vouchers
+ t.SyncClient.MustSignalEntry(ctx, ReceiverReadyState)
+
+ t.RecordMessage("adding %d payment vouchers", totalVouchers)
+
+ // Add each of the vouchers
+ var addedVouchers []*paych.SignedVoucher
+ for i := 0; i < totalVouchers; i++ {
+ v := <-vouchers
+ addedVouchers = append(addedVouchers, v)
+
+ _, err := cl.FullApi.PaychVoucherAdd(ctx, v.ChannelAddr, v, nil, big.NewInt(0))
+ if err != nil {
+ return fmt.Errorf("failed to add voucher: %w", err)
+ }
+ spendable, err := cl.FullApi.PaychVoucherCheckSpendable(ctx, v.ChannelAddr, v, nil, nil)
+ if err != nil {
+ return fmt.Errorf("failed to check voucher spendable: %w", err)
+ }
+ if !spendable {
+ return fmt.Errorf("expected voucher %d to be spendable", i)
+ }
+
+ t.RecordMessage("payment voucher added; lane=%d, nonce=%d, amount=%d", v.Lane, v.Nonce, v.Amount)
+ }
+
+ vouchersSub.Done()
+
+ t.RecordMessage("finished adding all payment vouchers")
+
+ // Inform the clients that the receiver has added all vouchers
+ t.SyncClient.MustSignalEntry(ctx, ReceiverAddedVouchersState)
+
+ // Wait for the settle message (put on chain by the sender)
+ t.RecordMessage("waiting for client to put settle message on chain")
+ settleMsgCid := <-settleMsgChan
+ settleSub.Done()
+
+ time.Sleep(5 * time.Second)
+
+ t.RecordMessage("waiting for confirmation of settle message on chain: %s", settleMsgCid)
+ _, err = cl.FullApi.StateWaitMsg(ctx, settleMsgCid, 10)
+ if err != nil {
+ return fmt.Errorf("failed to wait for settle message: %w", err)
+ }
+
+ // Note: Once the receiver sees the settle message on chain, it will
+ // automatically call submit voucher with the best vouchers
+
+ // TODO: Uncomment this section once this PR is merged:
+ // https://github.com/filecoin-project/lotus/pull/3197
+ //t.RecordMessage("checking that all %d vouchers are no longer spendable", len(addedVouchers))
+ //for i, v := range addedVouchers {
+ // spendable, err := cl.FullApi.PaychVoucherCheckSpendable(ctx, v.ChannelAddr, v, nil, nil)
+ // if err != nil {
+ // return fmt.Errorf("failed to check voucher spendable: %w", err)
+ // }
+ // // Should no longer be spendable because the best voucher has been submitted
+ // if spendable {
+ // return fmt.Errorf("expected voucher %d to no longer be spendable", i)
+ // }
+ //}
+
+ t.RecordMessage("all vouchers were submitted successfully")
+
+ return nil
+}
diff --git a/testplans/lotus-soup/rfwp/chain_state.go b/testplans/lotus-soup/rfwp/chain_state.go
new file mode 100644
index 000000000..ce2af2690
--- /dev/null
+++ b/testplans/lotus-soup/rfwp/chain_state.go
@@ -0,0 +1,796 @@
+package rfwp
+
+import (
+ "bufio"
+ "bytes"
+ "context"
+ "encoding/json"
+ "fmt"
+ "io"
+ "os"
+ "sort"
+ "text/tabwriter"
+ "time"
+
+ "github.com/filecoin-project/go-address"
+ "github.com/filecoin-project/go-state-types/big"
+ "github.com/filecoin-project/lotus/api/apibstore"
+ "github.com/filecoin-project/lotus/build"
+
+ "github.com/filecoin-project/lotus/api"
+ "github.com/filecoin-project/lotus/chain/store"
+ "github.com/filecoin-project/lotus/chain/types"
+
+ "github.com/filecoin-project/oni/lotus-soup/testkit"
+
+ "github.com/filecoin-project/go-state-types/abi"
+ sealing "github.com/filecoin-project/lotus/extern/storage-sealing"
+
+ "github.com/filecoin-project/lotus/chain/actors/builtin/miner"
+ tstats "github.com/filecoin-project/lotus/tools/stats"
+)
+
+func UpdateChainState(t *testkit.TestEnvironment, m *testkit.LotusMiner) error {
+ height := 0
+ headlag := 3
+
+ ctx := context.Background()
+
+ tipsetsCh, err := tstats.GetTips(ctx, m.FullApi, abi.ChainEpoch(height), headlag)
+ if err != nil {
+ return err
+ }
+
+ jsonFilename := fmt.Sprintf("%s%cchain-state.ndjson", t.TestOutputsPath, os.PathSeparator)
+ jsonFile, err := os.Create(jsonFilename)
+ if err != nil {
+ return err
+ }
+ defer jsonFile.Close()
+ jsonEncoder := json.NewEncoder(jsonFile)
+
+ for tipset := range tipsetsCh {
+ maddrs, err := m.FullApi.StateListMiners(ctx, tipset.Key())
+ if err != nil {
+ return err
+ }
+
+ snapshot := ChainSnapshot{
+ Height: tipset.Height(),
+ MinerStates: make(map[string]*MinerStateSnapshot),
+ }
+
+ err = func() error {
+ cs.Lock()
+ defer cs.Unlock()
+
+ for _, maddr := range maddrs {
+ err := func() error {
+ filename := fmt.Sprintf("%s%cstate-%s-%d", t.TestOutputsPath, os.PathSeparator, maddr, tipset.Height())
+
+ f, err := os.Create(filename)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ w := bufio.NewWriter(f)
+ defer w.Flush()
+
+ minerInfo, err := info(t, m, maddr, w, tipset.Height())
+ if err != nil {
+ return err
+ }
+ writeText(w, minerInfo)
+
+ if tipset.Height()%100 == 0 {
+ printDiff(t, minerInfo, tipset.Height())
+ }
+
+ faultState, err := provingFaults(t, m, maddr, tipset.Height())
+ if err != nil {
+ return err
+ }
+ writeText(w, faultState)
+
+ provState, err := provingInfo(t, m, maddr, tipset.Height())
+ if err != nil {
+ return err
+ }
+ writeText(w, provState)
+
+ // record diff
+ recordDiff(minerInfo, provState, tipset.Height())
+
+ deadlines, err := provingDeadlines(t, m, maddr, tipset.Height())
+ if err != nil {
+ return err
+ }
+ writeText(w, deadlines)
+
+ sectorInfo, err := sectorsList(t, m, maddr, w, tipset.Height())
+ if err != nil {
+ return err
+ }
+ writeText(w, sectorInfo)
+
+ snapshot.MinerStates[maddr.String()] = &MinerStateSnapshot{
+ Info: minerInfo,
+ Faults: faultState,
+ ProvingInfo: provState,
+ Deadlines: deadlines,
+ Sectors: sectorInfo,
+ }
+
+ return jsonEncoder.Encode(snapshot)
+ }()
+ if err != nil {
+ return err
+ }
+ }
+
+ cs.PrevHeight = tipset.Height()
+
+ return nil
+ }()
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+type ChainSnapshot struct {
+ Height abi.ChainEpoch
+
+ MinerStates map[string]*MinerStateSnapshot
+}
+
+type MinerStateSnapshot struct {
+ Info *MinerInfo
+ Faults *ProvingFaultState
+ ProvingInfo *ProvingInfoState
+ Deadlines *ProvingDeadlines
+ Sectors *SectorInfo
+}
+
+// writeText marshals m to text and writes to w, swallowing any errors along the way.
+func writeText(w io.Writer, m plainTextMarshaler) {
+ b, err := m.MarshalPlainText()
+ if err != nil {
+ return
+ }
+ _, _ = w.Write(b)
+}
+
+// if we make our structs `encoding.TextMarshaler`s, they all get stringified when marshaling to JSON
+// instead of just using the default struct marshaler.
+// so here's encoding.TextMarshaler with a different name, so that doesn't happen.
+type plainTextMarshaler interface {
+ MarshalPlainText() ([]byte, error)
+}
+
+type ProvingFaultState struct {
+ // FaultedSectors is a slice per-deadline faulty sectors. If the miner
+ // has no faulty sectors, this will be nil.
+ FaultedSectors [][]uint64
+}
+
+func (s *ProvingFaultState) MarshalPlainText() ([]byte, error) {
+ w := &bytes.Buffer{}
+
+ if len(s.FaultedSectors) == 0 {
+ fmt.Fprintf(w, "no faulty sectors\n")
+ return w.Bytes(), nil
+ }
+
+ tw := tabwriter.NewWriter(w, 2, 4, 2, ' ', 0)
+ _, _ = fmt.Fprintf(tw, "deadline\tsectors")
+ for deadline, sectors := range s.FaultedSectors {
+ for _, num := range sectors {
+ _, _ = fmt.Fprintf(tw, "%d\t%d\n", deadline, num)
+ }
+ }
+
+ return w.Bytes(), nil
+}
+
+func provingFaults(t *testkit.TestEnvironment, m *testkit.LotusMiner, maddr address.Address, height abi.ChainEpoch) (*ProvingFaultState, error) {
+ api := m.FullApi
+ ctx := context.Background()
+
+ head, err := api.ChainHead(ctx)
+ if err != nil {
+ return nil, err
+ }
+ deadlines, err := api.StateMinerDeadlines(ctx, maddr, head.Key())
+ if err != nil {
+ return nil, err
+ }
+ faultedSectors := make([][]uint64, len(deadlines))
+ hasFaults := false
+ for dlIdx := range deadlines {
+ partitions, err := api.StateMinerPartitions(ctx, maddr, uint64(dlIdx), types.EmptyTSK)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, partition := range partitions {
+ faulty, err := partition.FaultySectors.All(10000000)
+ if err != nil {
+ return nil, err
+ }
+
+ if len(faulty) > 0 {
+ hasFaults = true
+ }
+
+ faultedSectors[dlIdx] = append(faultedSectors[dlIdx], faulty...)
+ }
+ }
+ result := new(ProvingFaultState)
+ if hasFaults {
+ result.FaultedSectors = faultedSectors
+ }
+
+ return result, nil
+}
+
+type ProvingInfoState struct {
+ CurrentEpoch abi.ChainEpoch
+
+ ProvingPeriodStart abi.ChainEpoch
+
+ Faults uint64
+ ProvenSectors uint64
+ FaultPercent float64
+ Recoveries uint64
+
+ DeadlineIndex uint64
+ DeadlineSectors uint64
+ DeadlineOpen abi.ChainEpoch
+ DeadlineClose abi.ChainEpoch
+ DeadlineChallenge abi.ChainEpoch
+ DeadlineFaultCutoff abi.ChainEpoch
+
+ WPoStProvingPeriod abi.ChainEpoch
+}
+
+func (s *ProvingInfoState) MarshalPlainText() ([]byte, error) {
+ w := &bytes.Buffer{}
+ fmt.Fprintf(w, "Current Epoch: %d\n", s.CurrentEpoch)
+ fmt.Fprintf(w, "Chain Period: %d\n", s.CurrentEpoch/s.WPoStProvingPeriod)
+ fmt.Fprintf(w, "Chain Period Start: %s\n", epochTime(s.CurrentEpoch, (s.CurrentEpoch/s.WPoStProvingPeriod)*s.WPoStProvingPeriod))
+ fmt.Fprintf(w, "Chain Period End: %s\n\n", epochTime(s.CurrentEpoch, (s.CurrentEpoch/s.WPoStProvingPeriod+1)*s.WPoStProvingPeriod))
+
+ fmt.Fprintf(w, "Proving Period Boundary: %d\n", s.ProvingPeriodStart%s.WPoStProvingPeriod)
+ fmt.Fprintf(w, "Proving Period Start: %s\n", epochTime(s.CurrentEpoch, s.ProvingPeriodStart))
+ fmt.Fprintf(w, "Next Period Start: %s\n\n", epochTime(s.CurrentEpoch, s.ProvingPeriodStart+s.WPoStProvingPeriod))
+
+ fmt.Fprintf(w, "Faults: %d (%.2f%%)\n", s.Faults, s.FaultPercent)
+ fmt.Fprintf(w, "Recovering: %d\n", s.Recoveries)
+ //fmt.Fprintf(w, "New Sectors: %d\n\n", s.NewSectors)
+
+ fmt.Fprintf(w, "Deadline Index: %d\n", s.DeadlineIndex)
+ fmt.Fprintf(w, "Deadline Sectors: %d\n", s.DeadlineSectors)
+
+ fmt.Fprintf(w, "Deadline Open: %s\n", epochTime(s.CurrentEpoch, s.DeadlineOpen))
+ fmt.Fprintf(w, "Deadline Close: %s\n", epochTime(s.CurrentEpoch, s.DeadlineClose))
+ fmt.Fprintf(w, "Deadline Challenge: %s\n", epochTime(s.CurrentEpoch, s.DeadlineChallenge))
+ fmt.Fprintf(w, "Deadline FaultCutoff: %s\n", epochTime(s.CurrentEpoch, s.DeadlineFaultCutoff))
+
+ return w.Bytes(), nil
+}
+
+func provingInfo(t *testkit.TestEnvironment, m *testkit.LotusMiner, maddr address.Address, height abi.ChainEpoch) (*ProvingInfoState, error) {
+ lapi := m.FullApi
+ ctx := context.Background()
+
+ head, err := lapi.ChainHead(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ cd, err := lapi.StateMinerProvingDeadline(ctx, maddr, head.Key())
+ if err != nil {
+ return nil, err
+ }
+
+ deadlines, err := lapi.StateMinerDeadlines(ctx, maddr, head.Key())
+ if err != nil {
+ return nil, err
+ }
+
+ parts := map[uint64][]api.Partition{}
+ for dlIdx := range deadlines {
+ part, err := lapi.StateMinerPartitions(ctx, maddr, uint64(dlIdx), types.EmptyTSK)
+ if err != nil {
+ return nil, err
+ }
+
+ parts[uint64(dlIdx)] = part
+ }
+
+ proving := uint64(0)
+ faults := uint64(0)
+ recovering := uint64(0)
+
+ for _, partitions := range parts {
+ for _, partition := range partitions {
+ sc, err := partition.LiveSectors.Count()
+ if err != nil {
+ return nil, err
+ }
+ proving += sc
+
+ fc, err := partition.FaultySectors.Count()
+ if err != nil {
+ return nil, err
+ }
+ faults += fc
+
+ rc, err := partition.RecoveringSectors.Count()
+ if err != nil {
+ return nil, err
+ }
+ recovering += rc
+ }
+ }
+
+ var faultPerc float64
+ if proving > 0 {
+ faultPerc = float64(faults*10000/proving) / 100
+ }
+
+ s := ProvingInfoState{
+ CurrentEpoch: cd.CurrentEpoch,
+ ProvingPeriodStart: cd.PeriodStart,
+ Faults: faults,
+ ProvenSectors: proving,
+ FaultPercent: faultPerc,
+ Recoveries: recovering,
+ DeadlineIndex: cd.Index,
+ DeadlineOpen: cd.Open,
+ DeadlineClose: cd.Close,
+ DeadlineChallenge: cd.Challenge,
+ DeadlineFaultCutoff: cd.FaultCutoff,
+ WPoStProvingPeriod: cd.WPoStProvingPeriod,
+ }
+
+ if cd.Index < cd.WPoStPeriodDeadlines {
+ for _, partition := range parts[cd.Index] {
+ sc, err := partition.LiveSectors.Count()
+ if err != nil {
+ return nil, err
+ }
+ s.DeadlineSectors += sc
+ }
+ }
+
+ return &s, nil
+}
+
+func epochTime(curr, e abi.ChainEpoch) string {
+ switch {
+ case curr > e:
+ return fmt.Sprintf("%d (%s ago)", e, time.Second*time.Duration(int64(build.BlockDelaySecs)*int64(curr-e)))
+ case curr == e:
+ return fmt.Sprintf("%d (now)", e)
+ case curr < e:
+ return fmt.Sprintf("%d (in %s)", e, time.Second*time.Duration(int64(build.BlockDelaySecs)*int64(e-curr)))
+ }
+
+ panic("math broke")
+}
+
+type ProvingDeadlines struct {
+ Deadlines []DeadlineInfo
+}
+
+type DeadlineInfo struct {
+ Sectors uint64
+ Partitions int
+ Proven uint64
+ Current bool
+}
+
+func (d *ProvingDeadlines) MarshalPlainText() ([]byte, error) {
+ w := new(bytes.Buffer)
+ tw := tabwriter.NewWriter(w, 2, 4, 2, ' ', 0)
+ _, _ = fmt.Fprintln(tw, "deadline\tsectors\tpartitions\tproven")
+
+ for i, di := range d.Deadlines {
+ var cur string
+ if di.Current {
+ cur += "\t(current)"
+ }
+ _, _ = fmt.Fprintf(tw, "%d\t%d\t%d\t%d%s\n", i, di.Sectors, di.Partitions, di.Proven, cur)
+ }
+ tw.Flush()
+ return w.Bytes(), nil
+}
+
+func provingDeadlines(t *testkit.TestEnvironment, m *testkit.LotusMiner, maddr address.Address, height abi.ChainEpoch) (*ProvingDeadlines, error) {
+ lapi := m.FullApi
+ ctx := context.Background()
+
+ deadlines, err := lapi.StateMinerDeadlines(ctx, maddr, types.EmptyTSK)
+ if err != nil {
+ return nil, err
+ }
+
+ di, err := lapi.StateMinerProvingDeadline(ctx, maddr, types.EmptyTSK)
+ if err != nil {
+ return nil, err
+ }
+
+ infos := make([]DeadlineInfo, 0, len(deadlines))
+ for dlIdx, deadline := range deadlines {
+ partitions, err := lapi.StateMinerPartitions(ctx, maddr, uint64(dlIdx), types.EmptyTSK)
+ if err != nil {
+ return nil, err
+ }
+
+ provenPartitions, err := deadline.PostSubmissions.Count()
+ if err != nil {
+ return nil, err
+ }
+
+ var cur string
+ if di.Index == uint64(dlIdx) {
+ cur += "\t(current)"
+ }
+
+ outInfo := DeadlineInfo{
+ //Sectors: c,
+ Partitions: len(partitions),
+ Proven: provenPartitions,
+ Current: di.Index == uint64(dlIdx),
+ }
+ infos = append(infos, outInfo)
+ //_, _ = fmt.Fprintf(tw, "%d\t%d\t%d%s\n", dlIdx, len(partitions), provenPartitions, cur)
+ }
+
+ return &ProvingDeadlines{Deadlines: infos}, nil
+}
+
+type SectorInfo struct {
+ Sectors []abi.SectorNumber
+ SectorStates map[abi.SectorNumber]api.SectorInfo
+ Committed []abi.SectorNumber
+ Proving []abi.SectorNumber
+}
+
+func (i *SectorInfo) MarshalPlainText() ([]byte, error) {
+ provingIDs := make(map[abi.SectorNumber]struct{}, len(i.Proving))
+ for _, id := range i.Proving {
+ provingIDs[id] = struct{}{}
+ }
+ commitedIDs := make(map[abi.SectorNumber]struct{}, len(i.Committed))
+ for _, id := range i.Committed {
+ commitedIDs[id] = struct{}{}
+ }
+
+ w := new(bytes.Buffer)
+ tw := tabwriter.NewWriter(w, 8, 4, 1, ' ', 0)
+
+ for _, s := range i.Sectors {
+ _, inSSet := commitedIDs[s]
+ _, inPSet := provingIDs[s]
+
+ st, ok := i.SectorStates[s]
+ if !ok {
+ continue
+ }
+
+ fmt.Fprintf(tw, "%d: %s\tsSet: %s\tpSet: %s\ttktH: %d\tseedH: %d\tdeals: %v\n",
+ s,
+ st.State,
+ yesno(inSSet),
+ yesno(inPSet),
+ st.Ticket.Epoch,
+ st.Seed.Epoch,
+ st.Deals,
+ )
+ }
+
+ if err := tw.Flush(); err != nil {
+ return nil, err
+ }
+ return w.Bytes(), nil
+}
+
+func sectorsList(t *testkit.TestEnvironment, m *testkit.LotusMiner, maddr address.Address, w io.Writer, height abi.ChainEpoch) (*SectorInfo, error) {
+ node := m.FullApi
+ ctx := context.Background()
+
+ list, err := m.MinerApi.SectorsList(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ activeSet, err := node.StateMinerActiveSectors(ctx, maddr, types.EmptyTSK)
+ if err != nil {
+ return nil, err
+ }
+ activeIDs := make(map[abi.SectorNumber]struct{}, len(activeSet))
+ for _, info := range activeSet {
+ activeIDs[info.SectorNumber] = struct{}{}
+ }
+
+ sset, err := node.StateMinerSectors(ctx, maddr, nil, types.EmptyTSK)
+ if err != nil {
+ return nil, err
+ }
+ commitedIDs := make(map[abi.SectorNumber]struct{}, len(activeSet))
+ for _, info := range sset {
+ commitedIDs[info.SectorNumber] = struct{}{}
+ }
+
+ sort.Slice(list, func(i, j int) bool {
+ return list[i] < list[j]
+ })
+
+ i := SectorInfo{Sectors: list, SectorStates: make(map[abi.SectorNumber]api.SectorInfo, len(list))}
+
+ for _, s := range list {
+ st, err := m.MinerApi.SectorsStatus(ctx, s, true)
+ if err != nil {
+ fmt.Fprintf(w, "%d:\tError: %s\n", s, err)
+ continue
+ }
+ i.SectorStates[s] = st
+ }
+ return &i, nil
+}
+
+func yesno(b bool) string {
+ if b {
+ return "YES"
+ }
+ return "NO"
+}
+
+type MinerInfo struct {
+ MinerAddr address.Address
+ SectorSize string
+
+ MinerPower *api.MinerPower
+
+ CommittedBytes big.Int
+ ProvingBytes big.Int
+ FaultyBytes big.Int
+ FaultyPercentage float64
+
+ Balance big.Int
+ PreCommitDeposits big.Int
+ LockedFunds big.Int
+ AvailableFunds big.Int
+ WorkerBalance big.Int
+ MarketEscrow big.Int
+ MarketLocked big.Int
+
+ SectorStateCounts map[sealing.SectorState]int
+}
+
+func (i *MinerInfo) MarshalPlainText() ([]byte, error) {
+ w := new(bytes.Buffer)
+ fmt.Fprintf(w, "Miner: %s\n", i.MinerAddr)
+ fmt.Fprintf(w, "Sector Size: %s\n", i.SectorSize)
+
+ pow := i.MinerPower
+ rpercI := types.BigDiv(types.BigMul(pow.MinerPower.RawBytePower, types.NewInt(1000000)), pow.TotalPower.RawBytePower)
+ qpercI := types.BigDiv(types.BigMul(pow.MinerPower.QualityAdjPower, types.NewInt(1000000)), pow.TotalPower.QualityAdjPower)
+
+ fmt.Fprintf(w, "Byte Power: %s / %s (%0.4f%%)\n",
+ types.SizeStr(pow.MinerPower.RawBytePower),
+ types.SizeStr(pow.TotalPower.RawBytePower),
+ float64(rpercI.Int64())/10000)
+
+ fmt.Fprintf(w, "Actual Power: %s / %s (%0.4f%%)\n",
+ types.DeciStr(pow.MinerPower.QualityAdjPower),
+ types.DeciStr(pow.TotalPower.QualityAdjPower),
+ float64(qpercI.Int64())/10000)
+
+ fmt.Fprintf(w, "\tCommitted: %s\n", types.SizeStr(i.CommittedBytes))
+
+ if i.FaultyBytes.Int == nil || i.FaultyBytes.IsZero() {
+ fmt.Fprintf(w, "\tProving: %s\n", types.SizeStr(i.ProvingBytes))
+ } else {
+ fmt.Fprintf(w, "\tProving: %s (%s Faulty, %.2f%%)\n",
+ types.SizeStr(i.ProvingBytes),
+ types.SizeStr(i.FaultyBytes),
+ i.FaultyPercentage)
+ }
+
+ if !i.MinerPower.HasMinPower {
+ fmt.Fprintf(w, "Below minimum power threshold, no blocks will be won\n")
+ } else {
+ expWinChance := float64(types.BigMul(qpercI, types.NewInt(build.BlocksPerEpoch)).Int64()) / 1000000
+ if expWinChance > 0 {
+ if expWinChance > 1 {
+ expWinChance = 1
+ }
+ winRate := time.Duration(float64(time.Second*time.Duration(build.BlockDelaySecs)) / expWinChance)
+ winPerDay := float64(time.Hour*24) / float64(winRate)
+
+ fmt.Fprintln(w, "Expected block win rate: ")
+ fmt.Fprintf(w, "%.4f/day (every %s)\n", winPerDay, winRate.Truncate(time.Second))
+ }
+ }
+
+ fmt.Fprintf(w, "Miner Balance: %s\n", types.FIL(i.Balance))
+ fmt.Fprintf(w, "\tPreCommit: %s\n", types.FIL(i.PreCommitDeposits))
+ fmt.Fprintf(w, "\tLocked: %s\n", types.FIL(i.LockedFunds))
+ fmt.Fprintf(w, "\tAvailable: %s\n", types.FIL(i.AvailableFunds))
+ fmt.Fprintf(w, "Worker Balance: %s\n", types.FIL(i.WorkerBalance))
+ fmt.Fprintf(w, "Market (Escrow): %s\n", types.FIL(i.MarketEscrow))
+ fmt.Fprintf(w, "Market (Locked): %s\n\n", types.FIL(i.MarketLocked))
+
+ buckets := i.SectorStateCounts
+
+ var sorted []stateMeta
+ for state, i := range buckets {
+ sorted = append(sorted, stateMeta{i: i, state: state})
+ }
+
+ sort.Slice(sorted, func(i, j int) bool {
+ return stateOrder[sorted[i].state].i < stateOrder[sorted[j].state].i
+ })
+
+ for _, s := range sorted {
+ _, _ = fmt.Fprintf(w, "\t%s: %d\n", s.state, s.i)
+ }
+
+ return w.Bytes(), nil
+}
+
+func info(t *testkit.TestEnvironment, m *testkit.LotusMiner, maddr address.Address, w io.Writer, height abi.ChainEpoch) (*MinerInfo, error) {
+ api := m.FullApi
+ ctx := context.Background()
+
+ ts, err := api.ChainHead(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ mact, err := api.StateGetActor(ctx, maddr, ts.Key())
+ if err != nil {
+ return nil, err
+ }
+
+ i := MinerInfo{MinerAddr: maddr}
+
+ // Sector size
+ mi, err := api.StateMinerInfo(ctx, maddr, ts.Key())
+ if err != nil {
+ return nil, err
+ }
+
+ i.SectorSize = types.SizeStr(types.NewInt(uint64(mi.SectorSize)))
+
+ i.MinerPower, err = api.StateMinerPower(ctx, maddr, ts.Key())
+ if err != nil {
+ return nil, err
+ }
+
+ secCounts, err := api.StateMinerSectorCount(ctx, maddr, ts.Key())
+ if err != nil {
+ return nil, err
+ }
+ faults, err := api.StateMinerFaults(ctx, maddr, ts.Key())
+ if err != nil {
+ return nil, err
+ }
+
+ nfaults, err := faults.Count()
+ if err != nil {
+ return nil, err
+ }
+
+ i.CommittedBytes = types.BigMul(types.NewInt(secCounts.Live), types.NewInt(uint64(mi.SectorSize)))
+ i.ProvingBytes = types.BigMul(types.NewInt(secCounts.Active), types.NewInt(uint64(mi.SectorSize)))
+
+ if nfaults != 0 {
+ if secCounts.Live != 0 {
+ i.FaultyPercentage = float64(10000*nfaults/secCounts.Live) / 100.
+ }
+ i.FaultyBytes = types.BigMul(types.NewInt(nfaults), types.NewInt(uint64(mi.SectorSize)))
+ }
+
+ stor := store.ActorStore(ctx, apibstore.NewAPIBlockstore(api))
+ mas, err := miner.Load(stor, mact)
+ if err != nil {
+ return nil, err
+ }
+
+ funds, err := mas.LockedFunds()
+ if err != nil {
+ return nil, err
+ }
+
+ i.Balance = mact.Balance
+ i.PreCommitDeposits = funds.PreCommitDeposits
+ i.LockedFunds = funds.VestingFunds
+ i.AvailableFunds, err = mas.AvailableBalance(mact.Balance)
+ if err != nil {
+ return nil, err
+ }
+
+ wb, err := api.WalletBalance(ctx, mi.Worker)
+ if err != nil {
+ return nil, err
+ }
+ i.WorkerBalance = wb
+
+ mb, err := api.StateMarketBalance(ctx, maddr, types.EmptyTSK)
+ if err != nil {
+ return nil, err
+ }
+ i.MarketEscrow = mb.Escrow
+ i.MarketLocked = mb.Locked
+
+ sectors, err := m.MinerApi.SectorsList(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ buckets := map[sealing.SectorState]int{
+ "Total": len(sectors),
+ }
+ for _, s := range sectors {
+ st, err := m.MinerApi.SectorsStatus(ctx, s, true)
+ if err != nil {
+ return nil, err
+ }
+
+ buckets[sealing.SectorState(st.State)]++
+ }
+ i.SectorStateCounts = buckets
+
+ return &i, nil
+}
+
+type stateMeta struct {
+ i int
+ state sealing.SectorState
+}
+
+var stateOrder = map[sealing.SectorState]stateMeta{}
+var stateList = []stateMeta{
+ {state: "Total"},
+ {state: sealing.Proving},
+
+ {state: sealing.UndefinedSectorState},
+ {state: sealing.Empty},
+ {state: sealing.Packing},
+ {state: sealing.PreCommit1},
+ {state: sealing.PreCommit2},
+ {state: sealing.PreCommitting},
+ {state: sealing.PreCommitWait},
+ {state: sealing.WaitSeed},
+ {state: sealing.Committing},
+ {state: sealing.CommitWait},
+ {state: sealing.FinalizeSector},
+
+ {state: sealing.FailedUnrecoverable},
+ {state: sealing.SealPreCommit1Failed},
+ {state: sealing.SealPreCommit2Failed},
+ {state: sealing.PreCommitFailed},
+ {state: sealing.ComputeProofFailed},
+ {state: sealing.CommitFailed},
+ {state: sealing.PackingFailed},
+ {state: sealing.FinalizeFailed},
+ {state: sealing.Faulty},
+ {state: sealing.FaultReported},
+ {state: sealing.FaultedFinal},
+}
+
+func init() {
+ for i, state := range stateList {
+ stateOrder[state.state] = stateMeta{
+ i: i,
+ }
+ }
+}
diff --git a/testplans/lotus-soup/rfwp/diffs.go b/testplans/lotus-soup/rfwp/diffs.go
new file mode 100644
index 000000000..8384420d6
--- /dev/null
+++ b/testplans/lotus-soup/rfwp/diffs.go
@@ -0,0 +1,295 @@
+package rfwp
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "sort"
+ "sync"
+
+ "github.com/filecoin-project/go-state-types/abi"
+ "github.com/filecoin-project/go-state-types/big"
+ "github.com/filecoin-project/oni/lotus-soup/testkit"
+)
+
+type ChainState struct {
+ sync.Mutex
+
+ PrevHeight abi.ChainEpoch
+ DiffHeight map[string]map[string]map[abi.ChainEpoch]big.Int // height -> value
+ DiffValue map[string]map[string]map[string][]abi.ChainEpoch // value -> []height
+ DiffCmp map[string]map[string]map[string][]abi.ChainEpoch // difference (height, height-1) -> []height
+ valueTypes []string
+}
+
+func NewChainState() *ChainState {
+ cs := &ChainState{}
+ cs.PrevHeight = abi.ChainEpoch(-1)
+ cs.DiffHeight = make(map[string]map[string]map[abi.ChainEpoch]big.Int) // height -> value
+ cs.DiffValue = make(map[string]map[string]map[string][]abi.ChainEpoch) // value -> []height
+ cs.DiffCmp = make(map[string]map[string]map[string][]abi.ChainEpoch) // difference (height, height-1) -> []height
+ cs.valueTypes = []string{"MinerPower", "CommittedBytes", "ProvingBytes", "Balance", "PreCommitDeposits", "LockedFunds", "AvailableFunds", "WorkerBalance", "MarketEscrow", "MarketLocked", "Faults", "ProvenSectors", "Recoveries"}
+ return cs
+}
+
+var (
+ cs *ChainState
+)
+
+func init() {
+ cs = NewChainState()
+}
+
+func printDiff(t *testkit.TestEnvironment, mi *MinerInfo, height abi.ChainEpoch) {
+ maddr := mi.MinerAddr.String()
+ filename := fmt.Sprintf("%s%cdiff-%s-%d", t.TestOutputsPath, os.PathSeparator, maddr, height)
+
+ f, err := os.Create(filename)
+ if err != nil {
+ panic(err)
+ }
+ defer f.Close()
+
+ w := bufio.NewWriter(f)
+ defer w.Flush()
+
+ keys := make([]string, 0, len(cs.DiffCmp[maddr]))
+ for k := range cs.DiffCmp[maddr] {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+
+ fmt.Fprintln(w, "=====", maddr, "=====")
+ for i, valueName := range keys {
+ fmt.Fprintln(w, toCharStr(i), "=====", valueName, "=====")
+ if len(cs.DiffCmp[maddr][valueName]) > 0 {
+ fmt.Fprintf(w, "%s diff of |\n", toCharStr(i))
+ }
+
+ for difference, heights := range cs.DiffCmp[maddr][valueName] {
+ fmt.Fprintf(w, "%s diff of %30v at heights %v\n", toCharStr(i), difference, heights)
+ }
+ }
+}
+
+func recordDiff(mi *MinerInfo, ps *ProvingInfoState, height abi.ChainEpoch) {
+ maddr := mi.MinerAddr.String()
+ if _, ok := cs.DiffHeight[maddr]; !ok {
+ cs.DiffHeight[maddr] = make(map[string]map[abi.ChainEpoch]big.Int)
+ cs.DiffValue[maddr] = make(map[string]map[string][]abi.ChainEpoch)
+ cs.DiffCmp[maddr] = make(map[string]map[string][]abi.ChainEpoch)
+
+ for _, v := range cs.valueTypes {
+ cs.DiffHeight[maddr][v] = make(map[abi.ChainEpoch]big.Int)
+ cs.DiffValue[maddr][v] = make(map[string][]abi.ChainEpoch)
+ cs.DiffCmp[maddr][v] = make(map[string][]abi.ChainEpoch)
+ }
+ }
+
+ {
+ value := big.Int(mi.MinerPower.MinerPower.RawBytePower)
+ cs.DiffHeight[maddr]["MinerPower"][height] = value
+ cs.DiffValue[maddr]["MinerPower"][value.String()] = append(cs.DiffValue[maddr]["MinerPower"][value.String()], height)
+
+ if cs.PrevHeight != -1 {
+ prevValue := cs.DiffHeight[maddr]["MinerPower"][cs.PrevHeight]
+ cmp := big.Zero()
+ cmp.Sub(value.Int, prevValue.Int) // value - prevValue
+ if big.Cmp(cmp, big.Zero()) != 0 {
+ cs.DiffCmp[maddr]["MinerPower"][cmp.String()] = append(cs.DiffCmp[maddr]["MinerPower"][cmp.String()], height)
+ }
+ }
+ }
+
+ {
+ value := big.Int(mi.CommittedBytes)
+ cs.DiffHeight[maddr]["CommittedBytes"][height] = value
+ cs.DiffValue[maddr]["CommittedBytes"][value.String()] = append(cs.DiffValue[maddr]["CommittedBytes"][value.String()], height)
+
+ if cs.PrevHeight != -1 {
+ prevValue := cs.DiffHeight[maddr]["CommittedBytes"][cs.PrevHeight]
+ cmp := big.Zero()
+ cmp.Sub(value.Int, prevValue.Int) // value - prevValue
+ if big.Cmp(cmp, big.Zero()) != 0 {
+ cs.DiffCmp[maddr]["CommittedBytes"][cmp.String()] = append(cs.DiffCmp[maddr]["CommittedBytes"][cmp.String()], height)
+ }
+ }
+ }
+
+ {
+ value := big.Int(mi.ProvingBytes)
+ cs.DiffHeight[maddr]["ProvingBytes"][height] = value
+ cs.DiffValue[maddr]["ProvingBytes"][value.String()] = append(cs.DiffValue[maddr]["ProvingBytes"][value.String()], height)
+
+ if cs.PrevHeight != -1 {
+ prevValue := cs.DiffHeight[maddr]["ProvingBytes"][cs.PrevHeight]
+ cmp := big.Zero()
+ cmp.Sub(value.Int, prevValue.Int) // value - prevValue
+ if big.Cmp(cmp, big.Zero()) != 0 {
+ cs.DiffCmp[maddr]["ProvingBytes"][cmp.String()] = append(cs.DiffCmp[maddr]["ProvingBytes"][cmp.String()], height)
+ }
+ }
+ }
+
+ {
+ value := big.Int(mi.Balance)
+ roundBalance(&value)
+ cs.DiffHeight[maddr]["Balance"][height] = value
+ cs.DiffValue[maddr]["Balance"][value.String()] = append(cs.DiffValue[maddr]["Balance"][value.String()], height)
+
+ if cs.PrevHeight != -1 {
+ prevValue := cs.DiffHeight[maddr]["Balance"][cs.PrevHeight]
+ cmp := big.Zero()
+ cmp.Sub(value.Int, prevValue.Int) // value - prevValue
+ if big.Cmp(cmp, big.Zero()) != 0 {
+ cs.DiffCmp[maddr]["Balance"][cmp.String()] = append(cs.DiffCmp[maddr]["Balance"][cmp.String()], height)
+ }
+ }
+ }
+
+ {
+ value := big.Int(mi.PreCommitDeposits)
+ cs.DiffHeight[maddr]["PreCommitDeposits"][height] = value
+ cs.DiffValue[maddr]["PreCommitDeposits"][value.String()] = append(cs.DiffValue[maddr]["PreCommitDeposits"][value.String()], height)
+
+ if cs.PrevHeight != -1 {
+ prevValue := cs.DiffHeight[maddr]["PreCommitDeposits"][cs.PrevHeight]
+ cmp := big.Zero()
+ cmp.Sub(value.Int, prevValue.Int) // value - prevValue
+ if big.Cmp(cmp, big.Zero()) != 0 {
+ cs.DiffCmp[maddr]["PreCommitDeposits"][cmp.String()] = append(cs.DiffCmp[maddr]["PreCommitDeposits"][cmp.String()], height)
+ }
+ }
+ }
+
+ {
+ value := big.Int(mi.LockedFunds)
+ roundBalance(&value)
+ cs.DiffHeight[maddr]["LockedFunds"][height] = value
+ cs.DiffValue[maddr]["LockedFunds"][value.String()] = append(cs.DiffValue[maddr]["LockedFunds"][value.String()], height)
+
+ if cs.PrevHeight != -1 {
+ prevValue := cs.DiffHeight[maddr]["LockedFunds"][cs.PrevHeight]
+ cmp := big.Zero()
+ cmp.Sub(value.Int, prevValue.Int) // value - prevValue
+ if big.Cmp(cmp, big.Zero()) != 0 {
+ cs.DiffCmp[maddr]["LockedFunds"][cmp.String()] = append(cs.DiffCmp[maddr]["LockedFunds"][cmp.String()], height)
+ }
+ }
+ }
+
+ {
+ value := big.Int(mi.AvailableFunds)
+ roundBalance(&value)
+ cs.DiffHeight[maddr]["AvailableFunds"][height] = value
+ cs.DiffValue[maddr]["AvailableFunds"][value.String()] = append(cs.DiffValue[maddr]["AvailableFunds"][value.String()], height)
+
+ if cs.PrevHeight != -1 {
+ prevValue := cs.DiffHeight[maddr]["AvailableFunds"][cs.PrevHeight]
+ cmp := big.Zero()
+ cmp.Sub(value.Int, prevValue.Int) // value - prevValue
+ if big.Cmp(cmp, big.Zero()) != 0 {
+ cs.DiffCmp[maddr]["AvailableFunds"][cmp.String()] = append(cs.DiffCmp[maddr]["AvailableFunds"][cmp.String()], height)
+ }
+ }
+ }
+
+ {
+ value := big.Int(mi.WorkerBalance)
+ cs.DiffHeight[maddr]["WorkerBalance"][height] = value
+ cs.DiffValue[maddr]["WorkerBalance"][value.String()] = append(cs.DiffValue[maddr]["WorkerBalance"][value.String()], height)
+
+ if cs.PrevHeight != -1 {
+ prevValue := cs.DiffHeight[maddr]["WorkerBalance"][cs.PrevHeight]
+ cmp := big.Zero()
+ cmp.Sub(value.Int, prevValue.Int) // value - prevValue
+ if big.Cmp(cmp, big.Zero()) != 0 {
+ cs.DiffCmp[maddr]["WorkerBalance"][cmp.String()] = append(cs.DiffCmp[maddr]["WorkerBalance"][cmp.String()], height)
+ }
+ }
+ }
+
+ {
+ value := big.Int(mi.MarketEscrow)
+ cs.DiffHeight[maddr]["MarketEscrow"][height] = value
+ cs.DiffValue[maddr]["MarketEscrow"][value.String()] = append(cs.DiffValue[maddr]["MarketEscrow"][value.String()], height)
+
+ if cs.PrevHeight != -1 {
+ prevValue := cs.DiffHeight[maddr]["MarketEscrow"][cs.PrevHeight]
+ cmp := big.Zero()
+ cmp.Sub(value.Int, prevValue.Int) // value - prevValue
+ if big.Cmp(cmp, big.Zero()) != 0 {
+ cs.DiffCmp[maddr]["MarketEscrow"][cmp.String()] = append(cs.DiffCmp[maddr]["MarketEscrow"][cmp.String()], height)
+ }
+ }
+ }
+
+ {
+ value := big.Int(mi.MarketLocked)
+ cs.DiffHeight[maddr]["MarketLocked"][height] = value
+ cs.DiffValue[maddr]["MarketLocked"][value.String()] = append(cs.DiffValue[maddr]["MarketLocked"][value.String()], height)
+
+ if cs.PrevHeight != -1 {
+ prevValue := cs.DiffHeight[maddr]["MarketLocked"][cs.PrevHeight]
+ cmp := big.Zero()
+ cmp.Sub(value.Int, prevValue.Int) // value - prevValue
+ if big.Cmp(cmp, big.Zero()) != 0 {
+ cs.DiffCmp[maddr]["MarketLocked"][cmp.String()] = append(cs.DiffCmp[maddr]["MarketLocked"][cmp.String()], height)
+ }
+ }
+ }
+
+ {
+ value := big.NewInt(int64(ps.Faults))
+ cs.DiffHeight[maddr]["Faults"][height] = value
+ cs.DiffValue[maddr]["Faults"][value.String()] = append(cs.DiffValue[maddr]["Faults"][value.String()], height)
+
+ if cs.PrevHeight != -1 {
+ prevValue := cs.DiffHeight[maddr]["Faults"][cs.PrevHeight]
+ cmp := big.Zero()
+ cmp.Sub(value.Int, prevValue.Int) // value - prevValue
+ if big.Cmp(cmp, big.Zero()) != 0 {
+ cs.DiffCmp[maddr]["Faults"][cmp.String()] = append(cs.DiffCmp[maddr]["Faults"][cmp.String()], height)
+ }
+ }
+ }
+
+ {
+ value := big.NewInt(int64(ps.ProvenSectors))
+ cs.DiffHeight[maddr]["ProvenSectors"][height] = value
+ cs.DiffValue[maddr]["ProvenSectors"][value.String()] = append(cs.DiffValue[maddr]["ProvenSectors"][value.String()], height)
+
+ if cs.PrevHeight != -1 {
+ prevValue := cs.DiffHeight[maddr]["ProvenSectors"][cs.PrevHeight]
+ cmp := big.Zero()
+ cmp.Sub(value.Int, prevValue.Int) // value - prevValue
+ if big.Cmp(cmp, big.Zero()) != 0 {
+ cs.DiffCmp[maddr]["ProvenSectors"][cmp.String()] = append(cs.DiffCmp[maddr]["ProvenSectors"][cmp.String()], height)
+ }
+ }
+ }
+
+ {
+ value := big.NewInt(int64(ps.Recoveries))
+ cs.DiffHeight[maddr]["Recoveries"][height] = value
+ cs.DiffValue[maddr]["Recoveries"][value.String()] = append(cs.DiffValue[maddr]["Recoveries"][value.String()], height)
+
+ if cs.PrevHeight != -1 {
+ prevValue := cs.DiffHeight[maddr]["Recoveries"][cs.PrevHeight]
+ cmp := big.Zero()
+ cmp.Sub(value.Int, prevValue.Int) // value - prevValue
+ if big.Cmp(cmp, big.Zero()) != 0 {
+ cs.DiffCmp[maddr]["Recoveries"][cmp.String()] = append(cs.DiffCmp[maddr]["Recoveries"][cmp.String()], height)
+ }
+ }
+ }
+}
+
+func roundBalance(i *big.Int) {
+ *i = big.Div(*i, big.NewInt(1000000000000000))
+ *i = big.Mul(*i, big.NewInt(1000000000000000))
+}
+
+func toCharStr(i int) string {
+ return string('a' + i)
+}
diff --git a/testplans/lotus-soup/rfwp/e2e.go b/testplans/lotus-soup/rfwp/e2e.go
new file mode 100644
index 000000000..170f86c62
--- /dev/null
+++ b/testplans/lotus-soup/rfwp/e2e.go
@@ -0,0 +1,347 @@
+package rfwp
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "math/rand"
+ "os"
+ "sort"
+ "strings"
+ "time"
+
+ "github.com/filecoin-project/go-state-types/abi"
+ "github.com/filecoin-project/go-state-types/big"
+ "github.com/filecoin-project/lotus/api"
+ "github.com/filecoin-project/oni/lotus-soup/testkit"
+ "golang.org/x/sync/errgroup"
+)
+
+func RecoveryFromFailedWindowedPoStE2E(t *testkit.TestEnvironment) error {
+ switch t.Role {
+ case "bootstrapper":
+ return testkit.HandleDefaultRole(t)
+ case "client":
+ return handleClient(t)
+ case "miner":
+ return handleMiner(t)
+ case "miner-full-slash":
+ return handleMinerFullSlash(t)
+ case "miner-partial-slash":
+ return handleMinerPartialSlash(t)
+ }
+
+ return fmt.Errorf("unknown role: %s", t.Role)
+}
+
+func handleMiner(t *testkit.TestEnvironment) error {
+ m, err := testkit.PrepareMiner(t)
+ if err != nil {
+ return err
+ }
+
+ ctx := context.Background()
+ myActorAddr, err := m.MinerApi.ActorAddress(ctx)
+ if err != nil {
+ return err
+ }
+
+ t.RecordMessage("running miner: %s", myActorAddr)
+
+ if t.GroupSeq == 1 {
+ go FetchChainState(t, m)
+ }
+
+ go UpdateChainState(t, m)
+
+ minersToBeSlashed := 2
+ ch := make(chan testkit.SlashedMinerMsg)
+ sub := t.SyncClient.MustSubscribe(ctx, testkit.SlashedMinerTopic, ch)
+ var eg errgroup.Group
+
+ for i := 0; i < minersToBeSlashed; i++ {
+ select {
+ case slashedMiner := <-ch:
+ // wait for slash
+ eg.Go(func() error {
+ select {
+ case <-waitForSlash(t, slashedMiner):
+ case err = <-t.SyncClient.MustBarrier(ctx, testkit.StateAbortTest, 1).C:
+ if err != nil {
+ return err
+ }
+ return errors.New("got abort signal, exitting")
+ }
+ return nil
+ })
+ case err := <-sub.Done():
+ return fmt.Errorf("got error while waiting for slashed miners: %w", err)
+ case err := <-t.SyncClient.MustBarrier(ctx, testkit.StateAbortTest, 1).C:
+ if err != nil {
+ return err
+ }
+ return errors.New("got abort signal, exitting")
+ }
+ }
+
+ errc := make(chan error)
+ go func() {
+ errc <- eg.Wait()
+ }()
+
+ select {
+ case err := <-errc:
+ if err != nil {
+ return err
+ }
+ case err := <-t.SyncClient.MustBarrier(ctx, testkit.StateAbortTest, 1).C:
+ if err != nil {
+ return err
+ }
+ return errors.New("got abort signal, exitting")
+ }
+
+ t.SyncClient.MustSignalAndWait(ctx, testkit.StateDone, t.TestInstanceCount)
+ return nil
+}
+
+func waitForSlash(t *testkit.TestEnvironment, msg testkit.SlashedMinerMsg) chan error {
+ // assert that balance got reduced with that much 5 times (sector fee)
+ // assert that balance got reduced with that much 2 times (termination fee)
+ // assert that balance got increased with that much 10 times (block reward)
+ // assert that power got increased with that much 1 times (after sector is sealed)
+ // assert that power got reduced with that much 1 times (after sector is announced faulty)
+ slashedMiner := msg.MinerActorAddr
+
+ errc := make(chan error)
+ go func() {
+ foundSlashConditions := false
+ for range time.Tick(10 * time.Second) {
+ if foundSlashConditions {
+ close(errc)
+ return
+ }
+ t.RecordMessage("wait for slashing, tick")
+ func() {
+ cs.Lock()
+ defer cs.Unlock()
+
+ negativeAmounts := []big.Int{}
+ negativeDiffs := make(map[big.Int][]abi.ChainEpoch)
+
+ for am, heights := range cs.DiffCmp[slashedMiner.String()]["LockedFunds"] {
+ amount, err := big.FromString(am)
+ if err != nil {
+ errc <- fmt.Errorf("cannot parse LockedFunds amount: %w:", err)
+ return
+ }
+
+ // amount is negative => slash condition
+ if big.Cmp(amount, big.Zero()) < 0 {
+ negativeDiffs[amount] = heights
+ negativeAmounts = append(negativeAmounts, amount)
+ }
+ }
+
+ t.RecordMessage("negative diffs: %d", len(negativeDiffs))
+ if len(negativeDiffs) < 3 {
+ return
+ }
+
+ sort.Slice(negativeAmounts, func(i, j int) bool { return big.Cmp(negativeAmounts[i], negativeAmounts[j]) > 0 })
+
+ // TODO: confirm the largest is > 18 filecoin
+ // TODO: confirm the next largest is > 9 filecoin
+ foundSlashConditions = true
+ }()
+ }
+ }()
+
+ return errc
+}
+
+func handleMinerFullSlash(t *testkit.TestEnvironment) error {
+ m, err := testkit.PrepareMiner(t)
+ if err != nil {
+ return err
+ }
+
+ ctx := context.Background()
+ myActorAddr, err := m.MinerApi.ActorAddress(ctx)
+ if err != nil {
+ return err
+ }
+
+ t.RecordMessage("running miner, full slash: %s", myActorAddr)
+
+ // TODO: wait until we have sealed a deal for a client
+ time.Sleep(240 * time.Second)
+
+ t.RecordMessage("shutting down miner, full slash: %s", myActorAddr)
+
+ ctxt, cancel := context.WithTimeout(ctx, 10*time.Second)
+ defer cancel()
+ err = m.StopFn(ctxt)
+ if err != nil {
+ //return err
+ t.RecordMessage("err from StopFn: %s", err.Error()) // TODO: expect this to be fixed on Lotus
+ }
+
+ t.RecordMessage("shutdown miner, full slash: %s", myActorAddr)
+
+ t.SyncClient.MustPublish(ctx, testkit.SlashedMinerTopic, testkit.SlashedMinerMsg{
+ MinerActorAddr: myActorAddr,
+ })
+
+ t.SyncClient.MustSignalAndWait(ctx, testkit.StateDone, t.TestInstanceCount)
+ return nil
+}
+
+func handleMinerPartialSlash(t *testkit.TestEnvironment) error {
+ m, err := testkit.PrepareMiner(t)
+ if err != nil {
+ return err
+ }
+
+ ctx := context.Background()
+ myActorAddr, err := m.MinerApi.ActorAddress(ctx)
+ if err != nil {
+ return err
+ }
+
+ t.RecordMessage("running miner, partial slash: %s", myActorAddr)
+
+ // TODO: wait until we have sealed a deal for a client
+ time.Sleep(185 * time.Second)
+
+ t.RecordMessage("shutting down miner, partial slash: %s", myActorAddr)
+
+ ctxt, cancel := context.WithTimeout(ctx, 10*time.Second)
+ defer cancel()
+ err = m.StopFn(ctxt)
+ if err != nil {
+ //return err
+ t.RecordMessage("err from StopFn: %s", err.Error()) // TODO: expect this to be fixed on Lotus
+ }
+
+ t.RecordMessage("shutdown miner, partial slash: %s", myActorAddr)
+
+ t.SyncClient.MustPublish(ctx, testkit.SlashedMinerTopic, testkit.SlashedMinerMsg{
+ MinerActorAddr: myActorAddr,
+ })
+
+ time.Sleep(300 * time.Second)
+
+ rm, err := testkit.RestoreMiner(t, m)
+ if err != nil {
+ t.RecordMessage("got err: %s", err.Error())
+ return err
+ }
+
+ myActorAddr, err = rm.MinerApi.ActorAddress(ctx)
+ if err != nil {
+ t.RecordMessage("got err: %s", err.Error())
+ return err
+ }
+
+ t.RecordMessage("running miner again, partial slash: %s", myActorAddr)
+
+ time.Sleep(3600 * time.Second)
+
+ //t.SyncClient.MustSignalAndWait(ctx, testkit.StateDone, t.TestInstanceCount)
+ return nil
+}
+
+func handleClient(t *testkit.TestEnvironment) error {
+ cl, err := testkit.PrepareClient(t)
+ if err != nil {
+ return err
+ }
+
+ // This is a client role
+ t.RecordMessage("running client")
+
+ ctx := context.Background()
+ client := cl.FullApi
+
+ time.Sleep(10 * time.Second)
+
+ // select a miner based on our GroupSeq (client 1 -> miner 1 ; client 2 -> miner 2)
+ // this assumes that all miner instances receive the same sorted MinerAddrs slice
+ minerAddr := cl.MinerAddrs[t.InitContext.GroupSeq-1]
+ if err := client.NetConnect(ctx, minerAddr.MinerNetAddrs); err != nil {
+ return err
+ }
+ t.D().Counter(fmt.Sprintf("send-data-to,miner=%s", minerAddr.MinerActorAddr)).Inc(1)
+
+ t.RecordMessage("selected %s as the miner", minerAddr.MinerActorAddr)
+
+ time.Sleep(2 * time.Second)
+
+ // generate 1800 bytes of random data
+ data := make([]byte, 1800)
+ rand.New(rand.NewSource(time.Now().UnixNano())).Read(data)
+
+ file, err := ioutil.TempFile("/tmp", "data")
+ if err != nil {
+ return err
+ }
+ defer os.Remove(file.Name())
+
+ _, err = file.Write(data)
+ if err != nil {
+ return err
+ }
+
+ fcid, err := client.ClientImport(ctx, api.FileRef{Path: file.Name(), IsCAR: false})
+ if err != nil {
+ return err
+ }
+ t.RecordMessage("file cid: %s", fcid)
+
+ // start deal
+ t1 := time.Now()
+ fastRetrieval := false
+ deal := testkit.StartDeal(ctx, minerAddr.MinerActorAddr, client, fcid.Root, fastRetrieval)
+ t.RecordMessage("started deal: %s", deal)
+
+ // this sleep is only necessary because deals don't immediately get logged in the dealstore, we should fix this
+ time.Sleep(2 * time.Second)
+
+ t.RecordMessage("waiting for deal to be sealed")
+ testkit.WaitDealSealed(t, ctx, client, deal)
+ t.D().ResettingHistogram("deal.sealed").Update(int64(time.Since(t1)))
+
+ // TODO: wait to stop miner (ideally get a signal, rather than sleep)
+ time.Sleep(180 * time.Second)
+
+ t.RecordMessage("trying to retrieve %s", fcid)
+ info, err := client.ClientGetDealInfo(ctx, *deal)
+ if err != nil {
+ return err
+ }
+
+ carExport := true
+ err = testkit.RetrieveData(t, ctx, client, fcid.Root, &info.PieceCID, carExport, data)
+ if err != nil && strings.Contains(err.Error(), "cannot make retrieval deal for zero bytes") {
+ t.D().Counter("deal.expect-slashing").Inc(1)
+ } else if err != nil {
+ // unknown error => fail test
+ t.RecordFailure(err)
+
+ // send signal to abort test
+ t.SyncClient.MustSignalEntry(ctx, testkit.StateAbortTest)
+
+ t.D().ResettingHistogram("deal.retrieved.err").Update(int64(time.Since(t1)))
+ time.Sleep(10 * time.Second) // wait for metrics to be emitted
+
+ return nil
+ }
+
+ t.D().ResettingHistogram("deal.retrieved").Update(int64(time.Since(t1)))
+ time.Sleep(10 * time.Second) // wait for metrics to be emitted
+
+ t.SyncClient.MustSignalAndWait(ctx, testkit.StateDone, t.TestInstanceCount) // TODO: not sure about this
+ return nil
+}
diff --git a/testplans/lotus-soup/rfwp/html_chain_state.go b/testplans/lotus-soup/rfwp/html_chain_state.go
new file mode 100644
index 000000000..bc39b58d2
--- /dev/null
+++ b/testplans/lotus-soup/rfwp/html_chain_state.go
@@ -0,0 +1,66 @@
+package rfwp
+
+import (
+ "context"
+ "fmt"
+ "os"
+
+ "github.com/filecoin-project/oni/lotus-soup/testkit"
+
+ "github.com/filecoin-project/go-address"
+ "github.com/filecoin-project/go-state-types/abi"
+ "github.com/filecoin-project/lotus/cli"
+ tstats "github.com/filecoin-project/lotus/tools/stats"
+ "github.com/ipfs/go-cid"
+)
+
+func FetchChainState(t *testkit.TestEnvironment, m *testkit.LotusMiner) error {
+ height := 0
+ headlag := 3
+
+ ctx := context.Background()
+ api := m.FullApi
+
+ tipsetsCh, err := tstats.GetTips(ctx, m.FullApi, abi.ChainEpoch(height), headlag)
+ if err != nil {
+ return err
+ }
+
+ for tipset := range tipsetsCh {
+ err := func() error {
+ filename := fmt.Sprintf("%s%cchain-state-%d.html", t.TestOutputsPath, os.PathSeparator, tipset.Height())
+ file, err := os.Create(filename)
+ defer file.Close()
+ if err != nil {
+ return err
+ }
+
+ stout, err := api.StateCompute(ctx, tipset.Height(), nil, tipset.Key())
+ if err != nil {
+ return err
+ }
+
+ codeCache := map[address.Address]cid.Cid{}
+ getCode := func(addr address.Address) (cid.Cid, error) {
+ if c, found := codeCache[addr]; found {
+ return c, nil
+ }
+
+ c, err := api.StateGetActor(ctx, addr, tipset.Key())
+ if err != nil {
+ return cid.Cid{}, err
+ }
+
+ codeCache[addr] = c.Code
+ return c.Code, nil
+ }
+
+ return cli.ComputeStateHTMLTempl(file, tipset, stout, getCode)
+ }()
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/testplans/lotus-soup/runner/main.go b/testplans/lotus-soup/runner/main.go
new file mode 100644
index 000000000..e867533f0
--- /dev/null
+++ b/testplans/lotus-soup/runner/main.go
@@ -0,0 +1,120 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ "path"
+
+ "github.com/codeskyblue/go-sh"
+)
+
+type jobDefinition struct {
+ runNumber int
+ compositionPath string
+ outputDir string
+ skipStdout bool
+}
+
+type jobResult struct {
+ job jobDefinition
+ runError error
+}
+
+func runComposition(job jobDefinition) jobResult {
+ outputArchive := path.Join(job.outputDir, "test-outputs.tgz")
+ cmd := sh.Command("testground", "run", "composition", "-f", job.compositionPath, "--collect", "-o", outputArchive)
+ if err := os.MkdirAll(job.outputDir, os.ModePerm); err != nil {
+ return jobResult{runError: fmt.Errorf("unable to make output directory: %w", err)}
+ }
+
+ outPath := path.Join(job.outputDir, "run.out")
+ outFile, err := os.Create(outPath)
+ if err != nil {
+ return jobResult{runError: fmt.Errorf("unable to create output file %s: %w", outPath, err)}
+ }
+ if job.skipStdout {
+ cmd.Stdout = outFile
+ } else {
+ cmd.Stdout = io.MultiWriter(os.Stdout, outFile)
+ }
+ log.Printf("starting test run %d. writing testground client output to %s\n", job.runNumber, outPath)
+ if err = cmd.Run(); err != nil {
+ return jobResult{job: job, runError: err}
+ }
+ return jobResult{job: job}
+}
+
+func worker(id int, jobs <-chan jobDefinition, results chan<- jobResult) {
+ log.Printf("started worker %d\n", id)
+ for j := range jobs {
+ log.Printf("worker %d started test run %d\n", id, j.runNumber)
+ results <- runComposition(j)
+ }
+}
+
+func buildComposition(compositionPath string, outputDir string) (string, error) {
+ outComp := path.Join(outputDir, "composition.toml")
+ err := sh.Command("cp", compositionPath, outComp).Run()
+ if err != nil {
+ return "", err
+ }
+
+ return outComp, sh.Command("testground", "build", "composition", "-w", "-f", outComp).Run()
+}
+
+func main() {
+ runs := flag.Int("runs", 1, "number of times to run composition")
+ parallelism := flag.Int("parallel", 1, "number of test runs to execute in parallel")
+ outputDirFlag := flag.String("output", "", "path to output directory (will use temp dir if unset)")
+ flag.Parse()
+
+ if len(flag.Args()) != 1 {
+ log.Fatal("must provide a single composition file path argument")
+ }
+
+ outdir := *outputDirFlag
+ if outdir == "" {
+ var err error
+ outdir, err = ioutil.TempDir(os.TempDir(), "oni-batch-run-")
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+ if err := os.MkdirAll(outdir, os.ModePerm); err != nil {
+ log.Fatal(err)
+ }
+
+ compositionPath := flag.Args()[0]
+
+ // first build the composition and write out the artifacts.
+ // we copy to a temp file first to avoid modifying the original
+ log.Printf("building composition %s\n", compositionPath)
+ compositionPath, err := buildComposition(compositionPath, outdir)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ jobs := make(chan jobDefinition, *runs)
+ results := make(chan jobResult, *runs)
+ for w := 1; w <= *parallelism; w++ {
+ go worker(w, jobs, results)
+ }
+
+ for j := 1; j <= *runs; j++ {
+ dir := path.Join(outdir, fmt.Sprintf("run-%d", j))
+ skipStdout := *parallelism != 1
+ jobs <- jobDefinition{runNumber: j, compositionPath: compositionPath, outputDir: dir, skipStdout: skipStdout}
+ }
+ close(jobs)
+
+ for i := 0; i < *runs; i++ {
+ r := <-results
+ if r.runError != nil {
+ log.Printf("error running job %d: %s\n", r.job.runNumber, r.runError)
+ }
+ }
+}
diff --git a/testplans/lotus-soup/sanity.go b/testplans/lotus-soup/sanity.go
new file mode 100644
index 000000000..b06a653c5
--- /dev/null
+++ b/testplans/lotus-soup/sanity.go
@@ -0,0 +1,35 @@
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+)
+
+func sanityCheck() {
+ enhanceMsg := func(msg string, a ...interface{}) string {
+ return fmt.Sprintf("sanity check: "+msg+"; if running on local:exec, make sure to run `make` from the root of the oni repo", a...)
+ }
+
+ dir := "/var/tmp/filecoin-proof-parameters"
+ stat, err := os.Stat(dir)
+ if os.IsNotExist(err) {
+ panic(enhanceMsg("proofs parameters not available in /var/tmp/filecoin-proof-parameters"))
+ }
+ if err != nil {
+ panic(enhanceMsg("failed to stat /var/tmp/filecoin-proof-parameters: %s", err))
+ }
+
+ if !stat.IsDir() {
+ panic(enhanceMsg("/var/tmp/filecoin-proof-parameters is not a directory; aborting"))
+ }
+
+ files, err := ioutil.ReadDir(dir)
+ if err != nil {
+ panic(enhanceMsg("failed list directory /var/tmp/filecoin-proof-parameters: %s", err))
+ }
+
+ if len(files) == 0 {
+ panic(enhanceMsg("no files in /var/tmp/filecoin-proof-parameters"))
+ }
+}
diff --git a/testplans/lotus-soup/statemachine/statemachine.go b/testplans/lotus-soup/statemachine/statemachine.go
new file mode 100644
index 000000000..17de614db
--- /dev/null
+++ b/testplans/lotus-soup/statemachine/statemachine.go
@@ -0,0 +1,108 @@
+package statemachine
+
+import (
+ "errors"
+ "sync"
+)
+
+// This code has been shamelessly lifted from this blog post:
+// https://venilnoronha.io/a-simple-state-machine-framework-in-go
+// Many thanks to the author, Venil Norohnha
+
+// ErrEventRejected is the error returned when the state machine cannot process
+// an event in the state that it is in.
+var ErrEventRejected = errors.New("event rejected")
+
+const (
+ // Default represents the default state of the system.
+ Default StateType = ""
+
+ // NoOp represents a no-op event.
+ NoOp EventType = "NoOp"
+)
+
+// StateType represents an extensible state type in the state machine.
+type StateType string
+
+// EventType represents an extensible event type in the state machine.
+type EventType string
+
+// EventContext represents the context to be passed to the action implementation.
+type EventContext interface{}
+
+// Action represents the action to be executed in a given state.
+type Action interface {
+ Execute(eventCtx EventContext) EventType
+}
+
+// Events represents a mapping of events and states.
+type Events map[EventType]StateType
+
+// State binds a state with an action and a set of events it can handle.
+type State struct {
+ Action Action
+ Events Events
+}
+
+// States represents a mapping of states and their implementations.
+type States map[StateType]State
+
+// StateMachine represents the state machine.
+type StateMachine struct {
+ // Previous represents the previous state.
+ Previous StateType
+
+ // Current represents the current state.
+ Current StateType
+
+ // States holds the configuration of states and events handled by the state machine.
+ States States
+
+ // mutex ensures that only 1 event is processed by the state machine at any given time.
+ mutex sync.Mutex
+}
+
+// getNextState returns the next state for the event given the machine's current
+// state, or an error if the event can't be handled in the given state.
+func (s *StateMachine) getNextState(event EventType) (StateType, error) {
+ if state, ok := s.States[s.Current]; ok {
+ if state.Events != nil {
+ if next, ok := state.Events[event]; ok {
+ return next, nil
+ }
+ }
+ }
+ return Default, ErrEventRejected
+}
+
+// SendEvent sends an event to the state machine.
+func (s *StateMachine) SendEvent(event EventType, eventCtx EventContext) error {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+
+ for {
+ // Determine the next state for the event given the machine's current state.
+ nextState, err := s.getNextState(event)
+ if err != nil {
+ return ErrEventRejected
+ }
+
+ // Identify the state definition for the next state.
+ state, ok := s.States[nextState]
+ if !ok || state.Action == nil {
+ // configuration error
+ }
+
+ // Transition over to the next state.
+ s.Previous = s.Current
+ s.Current = nextState
+
+ // Execute the next state's action and loop over again if the event returned
+ // is not a no-op.
+ nextEvent := state.Action.Execute(eventCtx)
+ if nextEvent == NoOp {
+ return nil
+ }
+ event = nextEvent
+ }
+}
diff --git a/testplans/lotus-soup/statemachine/suspend.go b/testplans/lotus-soup/statemachine/suspend.go
new file mode 100644
index 000000000..11bade7c1
--- /dev/null
+++ b/testplans/lotus-soup/statemachine/suspend.go
@@ -0,0 +1,128 @@
+package statemachine
+
+import (
+ "fmt"
+ "strings"
+ "time"
+)
+
+const (
+ Running StateType = "running"
+ Suspended StateType = "suspended"
+
+ Halt EventType = "halt"
+ Resume EventType = "resume"
+)
+
+type Suspendable interface {
+ Halt()
+ Resume()
+}
+
+type HaltAction struct{}
+
+func (a *HaltAction) Execute(ctx EventContext) EventType {
+ s, ok := ctx.(*Suspender)
+ if !ok {
+ fmt.Println("unable to halt, event context is not Suspendable")
+ return NoOp
+ }
+ s.target.Halt()
+ return NoOp
+}
+
+type ResumeAction struct{}
+
+func (a *ResumeAction) Execute(ctx EventContext) EventType {
+ s, ok := ctx.(*Suspender)
+ if !ok {
+ fmt.Println("unable to resume, event context is not Suspendable")
+ return NoOp
+ }
+ s.target.Resume()
+ return NoOp
+}
+
+type Suspender struct {
+ StateMachine
+ target Suspendable
+ log LogFn
+}
+
+type LogFn func(fmt string, args ...interface{})
+
+func NewSuspender(target Suspendable, log LogFn) *Suspender {
+ return &Suspender{
+ target: target,
+ log: log,
+ StateMachine: StateMachine{
+ Current: Running,
+ States: States{
+ Running: State{
+ Action: &ResumeAction{},
+ Events: Events{
+ Halt: Suspended,
+ },
+ },
+
+ Suspended: State{
+ Action: &HaltAction{},
+ Events: Events{
+ Resume: Running,
+ },
+ },
+ },
+ },
+ }
+}
+
+func (s *Suspender) RunEvents(eventSpec string) {
+ s.log("running event spec: %s", eventSpec)
+ for _, et := range parseEventSpec(eventSpec, s.log) {
+ if et.delay != 0 {
+ //s.log("waiting %s", et.delay.String())
+ time.Sleep(et.delay)
+ continue
+ }
+ if et.event == "" {
+ s.log("ignoring empty event")
+ continue
+ }
+ s.log("sending event %s", et.event)
+ err := s.SendEvent(et.event, s)
+ if err != nil {
+ s.log("error sending event %s: %s", et.event, err)
+ }
+ }
+}
+
+type eventTiming struct {
+ delay time.Duration
+ event EventType
+}
+
+func parseEventSpec(spec string, log LogFn) []eventTiming {
+ fields := strings.Split(spec, "->")
+ out := make([]eventTiming, 0, len(fields))
+ for _, f := range fields {
+ f = strings.TrimSpace(f)
+ words := strings.Split(f, " ")
+
+ // TODO: try to implement a "waiting" state instead of special casing like this
+ if words[0] == "wait" {
+ if len(words) != 2 {
+ log("expected 'wait' to be followed by duration, e.g. 'wait 30s'. ignoring.")
+ continue
+ }
+ d, err := time.ParseDuration(words[1])
+ if err != nil {
+ log("bad argument for 'wait': %s", err)
+ continue
+ }
+ out = append(out, eventTiming{delay: d})
+ } else {
+ out = append(out, eventTiming{event: EventType(words[0])})
+ }
+ }
+ return out
+}
diff --git a/testplans/lotus-soup/testkit/deals.go b/testplans/lotus-soup/testkit/deals.go
new file mode 100644
index 000000000..5bfba17da
--- /dev/null
+++ b/testplans/lotus-soup/testkit/deals.go
@@ -0,0 +1,74 @@
+package testkit
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/filecoin-project/go-address"
+ "github.com/filecoin-project/go-fil-markets/storagemarket"
+ "github.com/filecoin-project/go-state-types/abi"
+ "github.com/filecoin-project/lotus/api"
+ "github.com/filecoin-project/lotus/chain/types"
+ "github.com/ipfs/go-cid"
+
+ tstats "github.com/filecoin-project/lotus/tools/stats"
+)
+
+func StartDeal(ctx context.Context, minerActorAddr address.Address, client api.FullNode, fcid cid.Cid, fastRetrieval bool) *cid.Cid {
+ addr, err := client.WalletDefaultAddress(ctx)
+ if err != nil {
+ panic(err)
+ }
+
+ deal, err := client.ClientStartDeal(ctx, &api.StartDealParams{
+ Data: &storagemarket.DataRef{
+ TransferType: storagemarket.TTGraphsync,
+ Root: fcid,
+ },
+ Wallet: addr,
+ Miner: minerActorAddr,
+ EpochPrice: types.NewInt(1000),
+ MinBlocksDuration: 640000,
+ DealStartEpoch: 200,
+ FastRetrieval: fastRetrieval,
+ })
+ if err != nil {
+ panic(err)
+ }
+ return deal
+}
+
+func WaitDealSealed(t *TestEnvironment, ctx context.Context, client api.FullNode, deal *cid.Cid) {
+ height := 0
+ headlag := 3
+
+ cctx, cancel := context.WithCancel(ctx)
+ defer cancel()
+
+ tipsetsCh, err := tstats.GetTips(cctx, client, abi.ChainEpoch(height), headlag)
+ if err != nil {
+ panic(err)
+ }
+
+ for tipset := range tipsetsCh {
+ t.RecordMessage("got tipset: height %d", tipset.Height())
+
+ di, err := client.ClientGetDealInfo(ctx, *deal)
+ if err != nil {
+ panic(err)
+ }
+ switch di.State {
+ case storagemarket.StorageDealProposalRejected:
+ panic("deal rejected")
+ case storagemarket.StorageDealFailing:
+ panic("deal failed")
+ case storagemarket.StorageDealError:
+ panic(fmt.Sprintf("deal errored %s", di.Message))
+ case storagemarket.StorageDealActive:
+ t.RecordMessage("completed deal: %s", di)
+ return
+ }
+
+ t.RecordMessage("deal state: %s", storagemarket.DealStates[di.State])
+ }
+}
diff --git a/testplans/lotus-soup/testkit/defaults.go b/testplans/lotus-soup/testkit/defaults.go
new file mode 100644
index 000000000..a0681f37c
--- /dev/null
+++ b/testplans/lotus-soup/testkit/defaults.go
@@ -0,0 +1,55 @@
+package testkit
+
+import "fmt"
+
+type RoleName = string
+
+var DefaultRoles = map[RoleName]func(*TestEnvironment) error{
+ "bootstrapper": func(t *TestEnvironment) error {
+ b, err := PrepareBootstrapper(t)
+ if err != nil {
+ return err
+ }
+ return b.RunDefault()
+ },
+ "miner": func(t *TestEnvironment) error {
+ m, err := PrepareMiner(t)
+ if err != nil {
+ return err
+ }
+ return m.RunDefault()
+ },
+ "client": func(t *TestEnvironment) error {
+ c, err := PrepareClient(t)
+ if err != nil {
+ return err
+ }
+ return c.RunDefault()
+ },
+ "drand": func(t *TestEnvironment) error {
+ d, err := PrepareDrandInstance(t)
+ if err != nil {
+ return err
+ }
+ return d.RunDefault()
+ },
+ "pubsub-tracer": func(t *TestEnvironment) error {
+ tr, err := PreparePubsubTracer(t)
+ if err != nil {
+ return err
+ }
+ return tr.RunDefault()
+ },
+}
+
+// HandleDefaultRole handles a role by running its default behaviour.
+//
+// This function is suitable to forward to when a test case doesn't need to
+// explicitly handle/alter a role.
+func HandleDefaultRole(t *TestEnvironment) error {
+ f, ok := DefaultRoles[t.Role]
+ if !ok {
+ panic(fmt.Sprintf("unrecognized role: %s", t.Role))
+ }
+ return f(t)
+}
diff --git a/testplans/lotus-soup/testkit/lotus_opts.go b/testplans/lotus-soup/testkit/lotus_opts.go
new file mode 100644
index 000000000..46df9078d
--- /dev/null
+++ b/testplans/lotus-soup/testkit/lotus_opts.go
@@ -0,0 +1,67 @@
+package testkit
+
+import (
+ "fmt"
+
+ "github.com/filecoin-project/lotus/node"
+ "github.com/filecoin-project/lotus/node/config"
+ "github.com/filecoin-project/lotus/node/modules"
+ "github.com/filecoin-project/lotus/node/modules/dtypes"
+ "github.com/filecoin-project/lotus/node/modules/lp2p"
+ "github.com/filecoin-project/lotus/node/repo"
+
+ "github.com/libp2p/go-libp2p-core/peer"
+ ma "github.com/multiformats/go-multiaddr"
+)
+
+func withGenesis(gb []byte) node.Option {
+ return node.Override(new(modules.Genesis), modules.LoadGenesis(gb))
+}
+
+func withBootstrapper(ab []byte) node.Option {
+ return node.Override(new(dtypes.BootstrapPeers),
+ func() (dtypes.BootstrapPeers, error) {
+ if ab == nil {
+ return dtypes.BootstrapPeers{}, nil
+ }
+
+ a, err := ma.NewMultiaddrBytes(ab)
+ if err != nil {
+ return nil, err
+ }
+ ai, err := peer.AddrInfoFromP2pAddr(a)
+ if err != nil {
+ return nil, err
+ }
+ return dtypes.BootstrapPeers{*ai}, nil
+ })
+}
+
+func withPubsubConfig(bootstrapper bool, pubsubTracer string) node.Option {
+ return node.Override(new(*config.Pubsub), func() *config.Pubsub {
+ return &config.Pubsub{
+ Bootstrapper: bootstrapper,
+ RemoteTracer: pubsubTracer,
+ }
+ })
+}
+
+func withListenAddress(ip string) node.Option {
+ addrs := []string{fmt.Sprintf("/ip4/%s/tcp/0", ip)}
+ return node.Override(node.StartListeningKey, lp2p.StartListening(addrs))
+}
+
+func withMinerListenAddress(ip string) node.Option {
+ addrs := []string{fmt.Sprintf("/ip4/%s/tcp/0", ip)}
+ return node.Override(node.StartListeningKey, lp2p.StartListening(addrs))
+}
+
+func withApiEndpoint(addr string) node.Option {
+ return node.Override(node.SetApiEndpointKey, func(lr repo.LockedRepo) error {
+ apima, err := ma.NewMultiaddr(addr)
+ if err != nil {
+ return err
+ }
+ return lr.SetAPIEndpoint(apima)
+ })
+}
diff --git a/testplans/lotus-soup/testkit/net.go b/testplans/lotus-soup/testkit/net.go
new file mode 100644
index 000000000..018813830
--- /dev/null
+++ b/testplans/lotus-soup/testkit/net.go
@@ -0,0 +1,87 @@
+package testkit
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/testground/sdk-go/network"
+ "github.com/testground/sdk-go/sync"
+)
+
+func ApplyNetworkParameters(t *TestEnvironment) {
+ if !t.TestSidecar {
+ t.RecordMessage("no test sidecar, skipping network config")
+ return
+ }
+
+ ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ defer cancel()
+
+ ls := network.LinkShape{}
+
+ if t.IsParamSet("latency_range") {
+ r := t.DurationRangeParam("latency_range")
+ ls.Latency = r.ChooseRandom()
+ t.D().RecordPoint("latency_ms", float64(ls.Latency.Milliseconds()))
+ }
+
+ if t.IsParamSet("jitter_range") {
+ r := t.DurationRangeParam("jitter_range")
+ ls.Jitter = r.ChooseRandom()
+ t.D().RecordPoint("jitter_ms", float64(ls.Jitter.Milliseconds()))
+ }
+
+ if t.IsParamSet("loss_range") {
+ r := t.FloatRangeParam("loss_range")
+ ls.Loss = r.ChooseRandom()
+ t.D().RecordPoint("packet_loss", float64(ls.Loss))
+ }
+
+ if t.IsParamSet("corrupt_range") {
+ r := t.FloatRangeParam("corrupt_range")
+ ls.Corrupt = r.ChooseRandom()
+ t.D().RecordPoint("corrupt_packet_probability", float64(ls.Corrupt))
+ }
+
+ if t.IsParamSet("corrupt_corr_range") {
+ r := t.FloatRangeParam("corrupt_corr_range")
+ ls.CorruptCorr = r.ChooseRandom()
+ t.D().RecordPoint("corrupt_packet_correlation", float64(ls.CorruptCorr))
+ }
+
+ if t.IsParamSet("reorder_range") {
+ r := t.FloatRangeParam("reorder_range")
+ ls.Reorder = r.ChooseRandom()
+ t.D().RecordPoint("reordered_packet_probability", float64(ls.Reorder))
+ }
+
+ if t.IsParamSet("reorder_corr_range") {
+ r := t.FloatRangeParam("reorder_corr_range")
+ ls.ReorderCorr = r.ChooseRandom()
+ t.D().RecordPoint("reordered_packet_correlation", float64(ls.ReorderCorr))
+ }
+
+ if t.IsParamSet("duplicate_range") {
+ r := t.FloatRangeParam("duplicate_range")
+ ls.Duplicate = r.ChooseRandom()
+ t.D().RecordPoint("duplicate_packet_probability", float64(ls.Duplicate))
+ }
+
+ if t.IsParamSet("duplicate_corr_range") {
+ r := t.FloatRangeParam("duplicate_corr_range")
+ ls.DuplicateCorr = r.ChooseRandom()
+ t.D().RecordPoint("duplicate_packet_correlation", float64(ls.DuplicateCorr))
+ }
+
+ t.NetClient.MustConfigureNetwork(ctx, &network.Config{
+ Network: "default",
+ Enable: true,
+ Default: ls,
+ CallbackState: sync.State(fmt.Sprintf("latency-configured-%s", t.TestGroupID)),
+ CallbackTarget: t.TestGroupInstanceCount,
+ RoutingPolicy: network.AllowAll,
+ })
+
+ t.DumpJSON("network-link-shape.json", ls)
+}
diff --git a/testplans/lotus-soup/testkit/node.go b/testplans/lotus-soup/testkit/node.go
new file mode 100644
index 000000000..08439bfcb
--- /dev/null
+++ b/testplans/lotus-soup/testkit/node.go
@@ -0,0 +1,252 @@
+package testkit
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "os"
+ "sort"
+ "time"
+
+ "github.com/filecoin-project/lotus/api"
+ "github.com/filecoin-project/lotus/chain/beacon"
+ "github.com/filecoin-project/lotus/chain/wallet"
+ "github.com/filecoin-project/lotus/metrics"
+ "github.com/filecoin-project/lotus/miner"
+ "github.com/filecoin-project/lotus/node"
+ "github.com/filecoin-project/lotus/node/modules/dtypes"
+ modtest "github.com/filecoin-project/lotus/node/modules/testing"
+ tstats "github.com/filecoin-project/lotus/tools/stats"
+
+ influxdb "github.com/kpacha/opencensus-influxdb"
+ ma "github.com/multiformats/go-multiaddr"
+ manet "github.com/multiformats/go-multiaddr-net"
+ "go.opencensus.io/stats"
+ "go.opencensus.io/stats/view"
+)
+
+var PrepareNodeTimeout = 3 * time.Minute
+
+type LotusNode struct {
+ FullApi api.FullNode
+ MinerApi api.StorageMiner
+ StopFn node.StopFunc
+ Wallet *wallet.Key
+ MineOne func(context.Context, miner.MineReq) error
+}
+
+func (n *LotusNode) setWallet(ctx context.Context, walletKey *wallet.Key) error {
+ _, err := n.FullApi.WalletImport(ctx, &walletKey.KeyInfo)
+ if err != nil {
+ return err
+ }
+
+ err = n.FullApi.WalletSetDefault(ctx, walletKey.Address)
+ if err != nil {
+ return err
+ }
+
+ n.Wallet = walletKey
+
+ return nil
+}
+
+func WaitForBalances(t *TestEnvironment, ctx context.Context, nodes int) ([]*InitialBalanceMsg, error) {
+ ch := make(chan *InitialBalanceMsg)
+ sub := t.SyncClient.MustSubscribe(ctx, BalanceTopic, ch)
+
+ balances := make([]*InitialBalanceMsg, 0, nodes)
+ for i := 0; i < nodes; i++ {
+ select {
+ case m := <-ch:
+ balances = append(balances, m)
+ case err := <-sub.Done():
+ return nil, fmt.Errorf("got error while waiting for balances: %w", err)
+ }
+ }
+
+ return balances, nil
+}
+
+func CollectPreseals(t *TestEnvironment, ctx context.Context, miners int) ([]*PresealMsg, error) {
+ ch := make(chan *PresealMsg)
+ sub := t.SyncClient.MustSubscribe(ctx, PresealTopic, ch)
+
+ preseals := make([]*PresealMsg, 0, miners)
+ for i := 0; i < miners; i++ {
+ select {
+ case m := <-ch:
+ preseals = append(preseals, m)
+ case err := <-sub.Done():
+ return nil, fmt.Errorf("got error while waiting for preseals: %w", err)
+ }
+ }
+
+ sort.Slice(preseals, func(i, j int) bool {
+ return preseals[i].Seqno < preseals[j].Seqno
+ })
+
+ return preseals, nil
+}
+
+func WaitForGenesis(t *TestEnvironment, ctx context.Context) (*GenesisMsg, error) {
+ genesisCh := make(chan *GenesisMsg)
+ sub := t.SyncClient.MustSubscribe(ctx, GenesisTopic, genesisCh)
+
+ select {
+ case genesisMsg := <-genesisCh:
+ return genesisMsg, nil
+ case err := <-sub.Done():
+ return nil, fmt.Errorf("error while waiting for genesis msg: %w", err)
+ }
+}
+
+func CollectMinerAddrs(t *TestEnvironment, ctx context.Context, miners int) ([]MinerAddressesMsg, error) {
+ ch := make(chan MinerAddressesMsg)
+ sub := t.SyncClient.MustSubscribe(ctx, MinersAddrsTopic, ch)
+
+ addrs := make([]MinerAddressesMsg, 0, miners)
+ for i := 0; i < miners; i++ {
+ select {
+ case a := <-ch:
+ addrs = append(addrs, a)
+ case err := <-sub.Done():
+ return nil, fmt.Errorf("got error while waiting for miners addrs: %w", err)
+ }
+ }
+
+ return addrs, nil
+}
+
+func CollectClientAddrs(t *TestEnvironment, ctx context.Context, clients int) ([]*ClientAddressesMsg, error) {
+ ch := make(chan *ClientAddressesMsg)
+ sub := t.SyncClient.MustSubscribe(ctx, ClientsAddrsTopic, ch)
+
+ addrs := make([]*ClientAddressesMsg, 0, clients)
+ for i := 0; i < clients; i++ {
+ select {
+ case a := <-ch:
+ addrs = append(addrs, a)
+ case err := <-sub.Done():
+ return nil, fmt.Errorf("got error while waiting for clients addrs: %w", err)
+ }
+ }
+
+ return addrs, nil
+}
+
+func GetPubsubTracerMaddr(ctx context.Context, t *TestEnvironment) (string, error) {
+ if !t.BooleanParam("enable_pubsub_tracer") {
+ return "", nil
+ }
+
+ ch := make(chan *PubsubTracerMsg)
+ sub := t.SyncClient.MustSubscribe(ctx, PubsubTracerTopic, ch)
+
+ select {
+ case m := <-ch:
+ return m.Multiaddr, nil
+ case err := <-sub.Done():
+ return "", fmt.Errorf("got error while waiting for pubsub tracer config: %w", err)
+ }
+}
+
+func GetRandomBeaconOpts(ctx context.Context, t *TestEnvironment) (node.Option, error) {
+ beaconType := t.StringParam("random_beacon_type")
+ switch beaconType {
+ case "external-drand":
+ noop := func(settings *node.Settings) error {
+ return nil
+ }
+ return noop, nil
+
+ case "local-drand":
+ cfg, err := waitForDrandConfig(ctx, t.SyncClient)
+ if err != nil {
+ t.RecordMessage("error getting drand config: %w", err)
+ return nil, err
+
+ }
+ t.RecordMessage("setting drand config: %v", cfg)
+ return node.Options(
+ node.Override(new(dtypes.DrandConfig), cfg.Config),
+ node.Override(new(dtypes.DrandBootstrap), cfg.GossipBootstrap),
+ ), nil
+
+ case "mock":
+ return node.Options(
+ node.Override(new(beacon.RandomBeacon), modtest.RandomBeacon),
+ node.Override(new(dtypes.DrandConfig), dtypes.DrandConfig{
+ ChainInfoJSON: "{\"Hash\":\"wtf\"}",
+ }),
+ node.Override(new(dtypes.DrandBootstrap), dtypes.DrandBootstrap{}),
+ ), nil
+
+ default:
+ return nil, fmt.Errorf("unknown random_beacon_type: %s", beaconType)
+ }
+}
+
+func startServer(endpoint ma.Multiaddr, srv *http.Server) (listenAddr string, err error) {
+ lst, err := manet.Listen(endpoint)
+ if err != nil {
+ return "", fmt.Errorf("could not listen: %w", err)
+ }
+
+ go func() {
+ _ = srv.Serve(manet.NetListener(lst))
+ }()
+
+ return lst.Addr().String(), nil
+}
+
+func registerAndExportMetrics(instanceName string) {
+ // Register all Lotus metric views
+ err := view.Register(metrics.DefaultViews...)
+ if err != nil {
+ panic(err)
+ }
+
+ // Set the metric to one so it is published to the exporter
+ stats.Record(context.Background(), metrics.LotusInfo.M(1))
+
+ // Register our custom exporter to opencensus
+ e, err := influxdb.NewExporter(context.Background(), influxdb.Options{
+ Database: "testground",
+ Address: os.Getenv("INFLUXDB_URL"),
+ Username: "",
+ Password: "",
+ InstanceName: instanceName,
+ })
+ if err != nil {
+ panic(err)
+ }
+ view.RegisterExporter(e)
+ view.SetReportingPeriod(5 * time.Second)
+}
+
+func collectStats(t *TestEnvironment, ctx context.Context, api api.FullNode) error {
+ t.RecordMessage("collecting blockchain stats")
+
+ influxAddr := os.Getenv("INFLUXDB_URL")
+ influxUser := ""
+ influxPass := ""
+ influxDb := "testground"
+
+ influx, err := tstats.InfluxClient(influxAddr, influxUser, influxPass)
+ if err != nil {
+ t.RecordMessage(err.Error())
+ return err
+ }
+
+ height := int64(0)
+ headlag := 1
+
+ go func() {
+ time.Sleep(15 * time.Second)
+ t.RecordMessage("calling tstats.Collect")
+ tstats.Collect(context.Background(), api, influx, influxDb, height, headlag)
+ }()
+
+ return nil
+}
diff --git a/testplans/lotus-soup/testkit/retrieval.go b/testplans/lotus-soup/testkit/retrieval.go
new file mode 100644
index 000000000..de3dee6be
--- /dev/null
+++ b/testplans/lotus-soup/testkit/retrieval.go
@@ -0,0 +1,106 @@
+package testkit
+
+import (
+ "bytes"
+ "context"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "time"
+
+ "github.com/filecoin-project/lotus/api"
+ "github.com/ipfs/go-cid"
+ files "github.com/ipfs/go-ipfs-files"
+ ipld "github.com/ipfs/go-ipld-format"
+ dag "github.com/ipfs/go-merkledag"
+ dstest "github.com/ipfs/go-merkledag/test"
+ unixfile "github.com/ipfs/go-unixfs/file"
+ "github.com/ipld/go-car"
+)
+
+func RetrieveData(t *TestEnvironment, ctx context.Context, client api.FullNode, fcid cid.Cid, _ *cid.Cid, carExport bool, data []byte) error {
+ t1 := time.Now()
+ offers, err := client.ClientFindData(ctx, fcid, nil)
+ if err != nil {
+ panic(err)
+ }
+ for _, o := range offers {
+ t.D().Counter(fmt.Sprintf("find-data.offer,miner=%s", o.Miner)).Inc(1)
+ }
+ t.D().ResettingHistogram("find-data").Update(int64(time.Since(t1)))
+
+ if len(offers) < 1 {
+ panic("no offers")
+ }
+
+ rpath, err := ioutil.TempDir("", "lotus-retrieve-test-")
+ if err != nil {
+ panic(err)
+ }
+ defer os.RemoveAll(rpath)
+
+ caddr, err := client.WalletDefaultAddress(ctx)
+ if err != nil {
+ return err
+ }
+
+ ref := &api.FileRef{
+ Path: filepath.Join(rpath, "ret"),
+ IsCAR: carExport,
+ }
+ t1 = time.Now()
+ err = client.ClientRetrieve(ctx, offers[0].Order(caddr), ref)
+ if err != nil {
+ return err
+ }
+ t.D().ResettingHistogram("retrieve-data").Update(int64(time.Since(t1)))
+
+ rdata, err := ioutil.ReadFile(filepath.Join(rpath, "ret"))
+ if err != nil {
+ return err
+ }
+
+ if carExport {
+ rdata = ExtractCarData(ctx, rdata, rpath)
+ }
+
+ if !bytes.Equal(rdata, data) {
+ return errors.New("wrong data retrieved")
+ }
+
+ t.RecordMessage("retrieved successfully")
+
+ return nil
+}
+
+func ExtractCarData(ctx context.Context, rdata []byte, rpath string) []byte {
+ bserv := dstest.Bserv()
+ ch, err := car.LoadCar(bserv.Blockstore(), bytes.NewReader(rdata))
+ if err != nil {
+ panic(err)
+ }
+ b, err := bserv.GetBlock(ctx, ch.Roots[0])
+ if err != nil {
+ panic(err)
+ }
+ nd, err := ipld.Decode(b)
+ if err != nil {
+ panic(err)
+ }
+ dserv := dag.NewDAGService(bserv)
+ fil, err := unixfile.NewUnixfsFile(ctx, dserv, nd)
+ if err != nil {
+ panic(err)
+ }
+ outPath := filepath.Join(rpath, "retLoadedCAR")
+ if err := files.WriteTo(fil, outPath); err != nil {
+ panic(err)
+ }
+ rdata, err = ioutil.ReadFile(outPath)
+ if err != nil {
+ panic(err)
+ }
+ return rdata
+}
diff --git a/testplans/lotus-soup/testkit/role_bootstrapper.go b/testplans/lotus-soup/testkit/role_bootstrapper.go
new file mode 100644
index 000000000..14f74c5ed
--- /dev/null
+++ b/testplans/lotus-soup/testkit/role_bootstrapper.go
@@ -0,0 +1,202 @@
+package testkit
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ mbig "math/big"
+ "time"
+
+ "github.com/filecoin-project/lotus/build"
+ "github.com/filecoin-project/lotus/chain/gen"
+ "github.com/filecoin-project/lotus/chain/types"
+ "github.com/filecoin-project/lotus/genesis"
+ "github.com/filecoin-project/lotus/node"
+ "github.com/filecoin-project/lotus/node/modules"
+ modtest "github.com/filecoin-project/lotus/node/modules/testing"
+ "github.com/filecoin-project/lotus/node/repo"
+ "github.com/google/uuid"
+
+ "github.com/filecoin-project/go-state-types/big"
+
+ "github.com/libp2p/go-libp2p-core/peer"
+ ma "github.com/multiformats/go-multiaddr"
+)
+
+// Bootstrapper is a special kind of process that produces a genesis block with
+// the initial wallet balances and preseals for all enlisted miners and clients.
+type Bootstrapper struct {
+ *LotusNode
+
+ t *TestEnvironment
+}
+
+func PrepareBootstrapper(t *TestEnvironment) (*Bootstrapper, error) {
+ var (
+ clients = t.IntParam("clients")
+ miners = t.IntParam("miners")
+ nodes = clients + miners
+ )
+
+ ctx, cancel := context.WithTimeout(context.Background(), PrepareNodeTimeout)
+ defer cancel()
+
+ pubsubTracerMaddr, err := GetPubsubTracerMaddr(ctx, t)
+ if err != nil {
+ return nil, err
+ }
+
+ randomBeaconOpt, err := GetRandomBeaconOpts(ctx, t)
+ if err != nil {
+ return nil, err
+ }
+
+ // the first duty of the boostrapper is to construct the genesis block
+ // first collect all client and miner balances to assign initial funds
+ balances, err := WaitForBalances(t, ctx, nodes)
+ if err != nil {
+ return nil, err
+ }
+
+ totalBalance := big.Zero()
+ for _, b := range balances {
+ totalBalance = big.Add(filToAttoFil(b.Balance), totalBalance)
+ }
+
+ totalBalanceFil := attoFilToFil(totalBalance)
+ t.RecordMessage("TOTAL BALANCE: %s AttoFIL (%s FIL)", totalBalance, totalBalanceFil)
+ if max := types.TotalFilecoinInt; totalBalanceFil.GreaterThanEqual(max) {
+ panic(fmt.Sprintf("total sum of balances is greater than max Filecoin ever; sum=%s, max=%s", totalBalance, max))
+ }
+
+ // then collect all preseals from miners
+ preseals, err := CollectPreseals(t, ctx, miners)
+ if err != nil {
+ return nil, err
+ }
+
+ // now construct the genesis block
+ var genesisActors []genesis.Actor
+ var genesisMiners []genesis.Miner
+
+ for _, bm := range balances {
+ balance := filToAttoFil(bm.Balance)
+ t.RecordMessage("balance assigned to actor %s: %s AttoFIL", bm.Addr, balance)
+ genesisActors = append(genesisActors,
+ genesis.Actor{
+ Type: genesis.TAccount,
+ Balance: balance,
+ Meta: (&genesis.AccountMeta{Owner: bm.Addr}).ActorMeta(),
+ })
+ }
+
+ for _, pm := range preseals {
+ genesisMiners = append(genesisMiners, pm.Miner)
+ }
+
+ genesisTemplate := genesis.Template{
+ Accounts: genesisActors,
+ Miners: genesisMiners,
+ Timestamp: uint64(time.Now().Unix()) - uint64(t.IntParam("genesis_timestamp_offset")),
+ VerifregRootKey: gen.DefaultVerifregRootkeyActor,
+ RemainderAccount: gen.DefaultRemainderAccountActor,
+ NetworkName: "testground-local-" + uuid.New().String(),
+ }
+
+ // dump the genesis block
+ // var jsonBuf bytes.Buffer
+ // jsonEnc := json.NewEncoder(&jsonBuf)
+ // err := jsonEnc.Encode(genesisTemplate)
+ // if err != nil {
+ // panic(err)
+ // }
+ // runenv.RecordMessage(fmt.Sprintf("Genesis template: %s", string(jsonBuf.Bytes())))
+
+ // this is horrendously disgusting, we use this contraption to side effect the construction
+ // of the genesis block in the buffer -- yes, a side effect of dependency injection.
+ // I remember when software was straightforward...
+ var genesisBuffer bytes.Buffer
+
+ bootstrapperIP := t.NetClient.MustGetDataNetworkIP().String()
+
+ n := &LotusNode{}
+ stop, err := node.New(context.Background(),
+ node.FullAPI(&n.FullApi),
+ node.Online(),
+ node.Repo(repo.NewMemory(nil)),
+ node.Override(new(modules.Genesis), modtest.MakeGenesisMem(&genesisBuffer, genesisTemplate)),
+ withApiEndpoint(fmt.Sprintf("/ip4/0.0.0.0/tcp/%s", t.PortNumber("node_rpc", "0"))),
+ withListenAddress(bootstrapperIP),
+ withBootstrapper(nil),
+ withPubsubConfig(true, pubsubTracerMaddr),
+ randomBeaconOpt,
+ )
+ if err != nil {
+ return nil, err
+ }
+ n.StopFn = stop
+
+ var bootstrapperAddr ma.Multiaddr
+
+ bootstrapperAddrs, err := n.FullApi.NetAddrsListen(ctx)
+ if err != nil {
+ stop(context.TODO())
+ return nil, err
+ }
+ for _, a := range bootstrapperAddrs.Addrs {
+ ip, err := a.ValueForProtocol(ma.P_IP4)
+ if err != nil {
+ continue
+ }
+ if ip != bootstrapperIP {
+ continue
+ }
+ addrs, err := peer.AddrInfoToP2pAddrs(&peer.AddrInfo{
+ ID: bootstrapperAddrs.ID,
+ Addrs: []ma.Multiaddr{a},
+ })
+ if err != nil {
+ panic(err)
+ }
+ bootstrapperAddr = addrs[0]
+ break
+ }
+
+ if bootstrapperAddr == nil {
+ panic("failed to determine bootstrapper address")
+ }
+
+ genesisMsg := &GenesisMsg{
+ Genesis: genesisBuffer.Bytes(),
+ Bootstrapper: bootstrapperAddr.Bytes(),
+ }
+ t.SyncClient.MustPublish(ctx, GenesisTopic, genesisMsg)
+
+ t.RecordMessage("waiting for all nodes to be ready")
+ t.SyncClient.MustSignalAndWait(ctx, StateReady, t.TestInstanceCount)
+
+ return &Bootstrapper{n, t}, nil
+}
+
+// RunDefault runs a default bootstrapper.
+func (b *Bootstrapper) RunDefault() error {
+ b.t.RecordMessage("running bootstrapper")
+ ctx := context.Background()
+ b.t.SyncClient.MustSignalAndWait(ctx, StateDone, b.t.TestInstanceCount)
+ return nil
+}
+
+// filToAttoFil converts a fractional filecoin value into AttoFIL, rounding if necessary
+func filToAttoFil(f float64) big.Int {
+ a := mbig.NewFloat(f)
+ a.Mul(a, mbig.NewFloat(float64(build.FilecoinPrecision)))
+ i, _ := a.Int(nil)
+ return big.Int{Int: i}
+}
+
+func attoFilToFil(atto big.Int) big.Int {
+ i := big.NewInt(0)
+ i.Add(i.Int, atto.Int)
+ i.Div(i.Int, big.NewIntUnsigned(build.FilecoinPrecision).Int)
+ return i
+}
diff --git a/testplans/lotus-soup/testkit/role_client.go b/testplans/lotus-soup/testkit/role_client.go
new file mode 100644
index 000000000..8db68bddf
--- /dev/null
+++ b/testplans/lotus-soup/testkit/role_client.go
@@ -0,0 +1,198 @@
+package testkit
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "time"
+
+ "contrib.go.opencensus.io/exporter/prometheus"
+ "github.com/filecoin-project/go-jsonrpc"
+ "github.com/filecoin-project/go-jsonrpc/auth"
+ "github.com/filecoin-project/lotus/api"
+ "github.com/filecoin-project/lotus/api/apistruct"
+ "github.com/filecoin-project/lotus/chain/types"
+ "github.com/filecoin-project/lotus/chain/wallet"
+ "github.com/filecoin-project/lotus/node"
+ "github.com/filecoin-project/lotus/node/repo"
+ "github.com/gorilla/mux"
+ "github.com/hashicorp/go-multierror"
+)
+
+type LotusClient struct {
+ *LotusNode
+
+ t *TestEnvironment
+ MinerAddrs []MinerAddressesMsg
+}
+
+func PrepareClient(t *TestEnvironment) (*LotusClient, error) {
+ ctx, cancel := context.WithTimeout(context.Background(), PrepareNodeTimeout)
+ defer cancel()
+
+ ApplyNetworkParameters(t)
+
+ pubsubTracer, err := GetPubsubTracerMaddr(ctx, t)
+ if err != nil {
+ return nil, err
+ }
+
+ drandOpt, err := GetRandomBeaconOpts(ctx, t)
+ if err != nil {
+ return nil, err
+ }
+
+ // first create a wallet
+ walletKey, err := wallet.GenerateKey(types.KTBLS)
+ if err != nil {
+ return nil, err
+ }
+
+ // publish the account ID/balance
+ balance := t.FloatParam("balance")
+ balanceMsg := &InitialBalanceMsg{Addr: walletKey.Address, Balance: balance}
+ t.SyncClient.Publish(ctx, BalanceTopic, balanceMsg)
+
+ // then collect the genesis block and bootstrapper address
+ genesisMsg, err := WaitForGenesis(t, ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ clientIP := t.NetClient.MustGetDataNetworkIP().String()
+
+ nodeRepo := repo.NewMemory(nil)
+
+ // create the node
+ n := &LotusNode{}
+ stop, err := node.New(context.Background(),
+ node.FullAPI(&n.FullApi),
+ node.Online(),
+ node.Repo(nodeRepo),
+ withApiEndpoint(fmt.Sprintf("/ip4/0.0.0.0/tcp/%s", t.PortNumber("node_rpc", "0"))),
+ withGenesis(genesisMsg.Genesis),
+ withListenAddress(clientIP),
+ withBootstrapper(genesisMsg.Bootstrapper),
+ withPubsubConfig(false, pubsubTracer),
+ drandOpt,
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ // set the wallet
+ err = n.setWallet(ctx, walletKey)
+ if err != nil {
+ _ = stop(context.TODO())
+ return nil, err
+ }
+
+ fullSrv, err := startFullNodeAPIServer(t, nodeRepo, n.FullApi)
+ if err != nil {
+ return nil, err
+ }
+
+ n.StopFn = func(ctx context.Context) error {
+ var err *multierror.Error
+ err = multierror.Append(fullSrv.Shutdown(ctx))
+ err = multierror.Append(stop(ctx))
+ return err.ErrorOrNil()
+ }
+
+ registerAndExportMetrics(fmt.Sprintf("client_%d", t.GroupSeq))
+
+ t.RecordMessage("publish our address to the clients addr topic")
+ addrinfo, err := n.FullApi.NetAddrsListen(ctx)
+ if err != nil {
+ return nil, err
+ }
+ t.SyncClient.MustPublish(ctx, ClientsAddrsTopic, &ClientAddressesMsg{
+ PeerNetAddr: addrinfo,
+ WalletAddr: walletKey.Address,
+ GroupSeq: t.GroupSeq,
+ })
+
+ t.RecordMessage("waiting for all nodes to be ready")
+ t.SyncClient.MustSignalAndWait(ctx, StateReady, t.TestInstanceCount)
+
+ // collect miner addresses.
+ addrs, err := CollectMinerAddrs(t, ctx, t.IntParam("miners"))
+ if err != nil {
+ return nil, err
+ }
+ t.RecordMessage("got %v miner addrs", len(addrs))
+
+ // densely connect the client to the full node and the miners themselves.
+ for _, miner := range addrs {
+ if err := n.FullApi.NetConnect(ctx, miner.FullNetAddrs); err != nil {
+ return nil, fmt.Errorf("client failed to connect to full node of miner: %w", err)
+ }
+ if err := n.FullApi.NetConnect(ctx, miner.MinerNetAddrs); err != nil {
+ return nil, fmt.Errorf("client failed to connect to storage miner node node of miner: %w", err)
+ }
+ }
+
+ // wait for all clients to have completed identify, pubsub negotiation with miners.
+ time.Sleep(1 * time.Second)
+
+ peers, err := n.FullApi.NetPeers(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("failed to query connected peers: %w", err)
+ }
+
+ t.RecordMessage("connected peers: %d", len(peers))
+
+ cl := &LotusClient{
+ t: t,
+ LotusNode: n,
+ MinerAddrs: addrs,
+ }
+ return cl, nil
+}
+
+func (c *LotusClient) RunDefault() error {
+ // run forever
+ c.t.RecordMessage("running default client forever")
+ c.t.WaitUntilAllDone()
+ return nil
+}
+
+func startFullNodeAPIServer(t *TestEnvironment, repo repo.Repo, api api.FullNode) (*http.Server, error) {
+ mux := mux.NewRouter()
+
+ rpcServer := jsonrpc.NewServer()
+ rpcServer.Register("Filecoin", api)
+
+ mux.Handle("/rpc/v0", rpcServer)
+
+ exporter, err := prometheus.NewExporter(prometheus.Options{
+ Namespace: "lotus",
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ mux.Handle("/debug/metrics", exporter)
+
+ ah := &auth.Handler{
+ Verify: func(ctx context.Context, token string) ([]auth.Permission, error) {
+ return apistruct.AllPermissions, nil
+ },
+ Next: mux.ServeHTTP,
+ }
+
+ srv := &http.Server{Handler: ah}
+
+ endpoint, err := repo.APIEndpoint()
+ if err != nil {
+ return nil, fmt.Errorf("no API endpoint in repo: %w", err)
+ }
+
+ listenAddr, err := startServer(endpoint, srv)
+ if err != nil {
+ return nil, fmt.Errorf("failed to start client API endpoint: %w", err)
+ }
+
+ t.RecordMessage("started node API server at %s", listenAddr)
+ return srv, nil
+}
diff --git a/testplans/lotus-soup/testkit/role_drand.go b/testplans/lotus-soup/testkit/role_drand.go
new file mode 100644
index 000000000..f54497252
--- /dev/null
+++ b/testplans/lotus-soup/testkit/role_drand.go
@@ -0,0 +1,391 @@
+package testkit
+
+import (
+ "bytes"
+ "context"
+ "encoding/hex"
+ "fmt"
+ "io/ioutil"
+ "net"
+ "os"
+ "path"
+ "time"
+
+ "github.com/drand/drand/chain"
+ "github.com/drand/drand/client"
+ hclient "github.com/drand/drand/client/http"
+ "github.com/drand/drand/core"
+ "github.com/drand/drand/key"
+ "github.com/drand/drand/log"
+ "github.com/drand/drand/lp2p"
+ dnet "github.com/drand/drand/net"
+ "github.com/drand/drand/protobuf/drand"
+ dtest "github.com/drand/drand/test"
+ "github.com/filecoin-project/lotus/node/modules/dtypes"
+ "github.com/libp2p/go-libp2p-core/peer"
+ ma "github.com/multiformats/go-multiaddr"
+ "github.com/testground/sdk-go/sync"
+
+ "github.com/filecoin-project/oni/lotus-soup/statemachine"
+)
+
+var (
+ PrepareDrandTimeout = 3 * time.Minute
+ secretDKG = "dkgsecret"
+)
+
+type DrandInstance struct {
+ daemon *core.Drand
+ httpClient client.Client
+ ctrlClient *dnet.ControlClient
+ gossipRelay *lp2p.GossipRelayNode
+
+ t *TestEnvironment
+ stateDir string
+ priv *key.Pair
+ pubAddr string
+ privAddr string
+ ctrlAddr string
+}
+
+func (dr *DrandInstance) Start() error {
+ opts := []core.ConfigOption{
+ core.WithLogLevel(getLogLevel(dr.t)),
+ core.WithConfigFolder(dr.stateDir),
+ core.WithPublicListenAddress(dr.pubAddr),
+ core.WithPrivateListenAddress(dr.privAddr),
+ core.WithControlPort(dr.ctrlAddr),
+ core.WithInsecure(),
+ }
+ conf := core.NewConfig(opts...)
+ fs := key.NewFileStore(conf.ConfigFolder())
+ fs.SaveKeyPair(dr.priv)
+ key.Save(path.Join(dr.stateDir, "public.toml"), dr.priv.Public, false)
+ if dr.daemon == nil {
+ drand, err := core.NewDrand(fs, conf)
+ if err != nil {
+ return err
+ }
+ dr.daemon = drand
+ } else {
+ drand, err := core.LoadDrand(fs, conf)
+ if err != nil {
+ return err
+ }
+ drand.StartBeacon(true)
+ dr.daemon = drand
+ }
+ return nil
+}
+
+func (dr *DrandInstance) Ping() bool {
+ cl := dr.ctrl()
+ if err := cl.Ping(); err != nil {
+ return false
+ }
+ return true
+}
+
+func (dr *DrandInstance) Close() error {
+ dr.gossipRelay.Shutdown()
+ dr.daemon.Stop(context.Background())
+ return os.RemoveAll(dr.stateDir)
+}
+
+func (dr *DrandInstance) ctrl() *dnet.ControlClient {
+ if dr.ctrlClient != nil {
+ return dr.ctrlClient
+ }
+ cl, err := dnet.NewControlClient(dr.ctrlAddr)
+ if err != nil {
+ dr.t.RecordMessage("drand can't instantiate control client: %w", err)
+ return nil
+ }
+ dr.ctrlClient = cl
+ return cl
+}
+
+func (dr *DrandInstance) RunDKG(nodes, thr int, timeout string, leader bool, leaderAddr string, beaconOffset int) *key.Group {
+ cl := dr.ctrl()
+ p := dr.t.DurationParam("drand_period")
+ catchupPeriod := dr.t.DurationParam("drand_catchup_period")
+ t, _ := time.ParseDuration(timeout)
+ var grp *drand.GroupPacket
+ var err error
+ if leader {
+ grp, err = cl.InitDKGLeader(nodes, thr, p, catchupPeriod, t, nil, secretDKG, beaconOffset)
+ } else {
+ leader := dnet.CreatePeer(leaderAddr, false)
+ grp, err = cl.InitDKG(leader, nil, secretDKG)
+ }
+ if err != nil {
+ dr.t.RecordMessage("drand dkg run failed: %w", err)
+ return nil
+ }
+ kg, _ := key.GroupFromProto(grp)
+ return kg
+}
+
+func (dr *DrandInstance) Halt() {
+ dr.t.RecordMessage("drand node #%d halting", dr.t.GroupSeq)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ dr.daemon.Stop(ctx)
+}
+
+func (dr *DrandInstance) Resume() {
+ dr.t.RecordMessage("drand node #%d resuming", dr.t.GroupSeq)
+ dr.Start()
+ // block until we can fetch the round corresponding to the current time
+ startTime := time.Now()
+ round := dr.httpClient.RoundAt(startTime)
+ timeout := 120 * time.Second
+ ctx, cancel := context.WithTimeout(context.Background(), timeout)
+ defer cancel()
+
+ done := make(chan struct{}, 1)
+ go func() {
+ for {
+ res, err := dr.httpClient.Get(ctx, round)
+ if err == nil {
+ dr.t.RecordMessage("drand chain caught up to round %d", res.Round())
+ done <- struct{}{}
+ return
+ }
+ time.Sleep(2 * time.Second)
+ }
+ }()
+
+ select {
+ case <-ctx.Done():
+ dr.t.RecordMessage("drand chain failed to catch up after %s", timeout.String())
+ case <-done:
+ dr.t.RecordMessage("drand chain resumed after %s catchup time", time.Since(startTime))
+ }
+}
+
+func (dr *DrandInstance) RunDefault() error {
+ dr.t.RecordMessage("running drand node")
+
+ if dr.t.IsParamSet("suspend_events") {
+ suspender := statemachine.NewSuspender(dr, dr.t.RecordMessage)
+ suspender.RunEvents(dr.t.StringParam("suspend_events"))
+ }
+
+ dr.t.WaitUntilAllDone()
+ return nil
+}
+
+// prepareDrandNode starts a drand instance and runs a DKG with the other members of the composition group.
+// Once the chain is running, the leader publishes the chain info needed by lotus nodes on
+// drandConfigTopic
+func PrepareDrandInstance(t *TestEnvironment) (*DrandInstance, error) {
+ ctx, cancel := context.WithTimeout(context.Background(), PrepareDrandTimeout)
+ defer cancel()
+
+ ApplyNetworkParameters(t)
+
+ startTime := time.Now()
+
+ seq := t.GroupSeq
+ isLeader := seq == 1
+ nNodes := t.TestGroupInstanceCount
+
+ myAddr := t.NetClient.MustGetDataNetworkIP()
+ threshold := t.IntParam("drand_threshold")
+ runGossipRelay := t.BooleanParam("drand_gossip_relay")
+
+ beaconOffset := 3
+
+ stateDir, err := ioutil.TempDir("/tmp", fmt.Sprintf("drand-%d", t.GroupSeq))
+ if err != nil {
+ return nil, err
+ }
+
+ dr := DrandInstance{
+ t: t,
+ stateDir: stateDir,
+ pubAddr: dtest.FreeBind(myAddr.String()),
+ privAddr: dtest.FreeBind(myAddr.String()),
+ ctrlAddr: dtest.FreeBind("localhost"),
+ }
+ dr.priv = key.NewKeyPair(dr.privAddr)
+
+ // share the node addresses with other nodes
+ // TODO: if we implement TLS, this is where we'd share public TLS keys
+ type NodeAddr struct {
+ PrivateAddr string
+ PublicAddr string
+ IsLeader bool
+ }
+ addrTopic := sync.NewTopic("drand-addrs", &NodeAddr{})
+ var publicAddrs []string
+ var leaderAddr string
+ ch := make(chan *NodeAddr)
+ _, sub := t.SyncClient.MustPublishSubscribe(ctx, addrTopic, &NodeAddr{
+ PrivateAddr: dr.privAddr,
+ PublicAddr: dr.pubAddr,
+ IsLeader: isLeader,
+ }, ch)
+ for i := 0; i < nNodes; i++ {
+ select {
+ case msg := <-ch:
+ publicAddrs = append(publicAddrs, fmt.Sprintf("http://%s", msg.PublicAddr))
+ if msg.IsLeader {
+ leaderAddr = msg.PrivateAddr
+ }
+ case err := <-sub.Done():
+ return nil, fmt.Errorf("unable to read drand addrs from sync service: %w", err)
+ }
+ }
+ if leaderAddr == "" {
+ return nil, fmt.Errorf("got %d drand addrs, but no leader", len(publicAddrs))
+ }
+
+ t.SyncClient.MustSignalAndWait(ctx, "drand-start", nNodes)
+ t.RecordMessage("Starting drand sharing ceremony")
+ if err := dr.Start(); err != nil {
+ return nil, err
+ }
+
+ alive := false
+ waitSecs := 10
+ for i := 0; i < waitSecs; i++ {
+ if !dr.Ping() {
+ time.Sleep(time.Second)
+ continue
+ }
+ t.R().RecordPoint("drand_first_ping", time.Now().Sub(startTime).Seconds())
+ alive = true
+ break
+ }
+ if !alive {
+ return nil, fmt.Errorf("drand node %d failed to start after %d seconds", t.GroupSeq, waitSecs)
+ }
+
+ // run DKG
+ t.SyncClient.MustSignalAndWait(ctx, "drand-dkg-start", nNodes)
+ if !isLeader {
+ time.Sleep(3 * time.Second)
+ }
+ grp := dr.RunDKG(nNodes, threshold, "10s", isLeader, leaderAddr, beaconOffset)
+ if grp == nil {
+ return nil, fmt.Errorf("drand dkg failed")
+ }
+ t.R().RecordPoint("drand_dkg_complete", time.Now().Sub(startTime).Seconds())
+
+ t.RecordMessage("drand dkg complete, waiting for chain start: %v", time.Until(time.Unix(grp.GenesisTime, 0).Add(grp.Period)))
+
+ // wait for chain to begin
+ to := time.Until(time.Unix(grp.GenesisTime, 0).Add(5 * time.Second).Add(grp.Period))
+ time.Sleep(to)
+
+ t.RecordMessage("drand beacon chain started, fetching initial round via http")
+ // verify that we can get a round of randomness from the chain using an http client
+ info := chain.NewChainInfo(grp)
+ myPublicAddr := fmt.Sprintf("http://%s", dr.pubAddr)
+ dr.httpClient, err = hclient.NewWithInfo(myPublicAddr, info, nil)
+ if err != nil {
+ return nil, fmt.Errorf("unable to create drand http client: %w", err)
+ }
+
+ _, err = dr.httpClient.Get(ctx, 1)
+ if err != nil {
+ return nil, fmt.Errorf("unable to get initial drand round: %w", err)
+ }
+
+ // start gossip relay (unless disabled via testplan parameter)
+ var relayAddrs []peer.AddrInfo
+
+ if runGossipRelay {
+ gossipDir := path.Join(stateDir, "gossip-relay")
+ listenAddr := fmt.Sprintf("/ip4/%s/tcp/7777", myAddr.String())
+ relayCfg := lp2p.GossipRelayConfig{
+ ChainHash: hex.EncodeToString(info.Hash()),
+ Addr: listenAddr,
+ DataDir: gossipDir,
+ IdentityPath: path.Join(gossipDir, "identity.key"),
+ Insecure: true,
+ Client: dr.httpClient,
+ }
+ t.RecordMessage("starting drand gossip relay")
+ dr.gossipRelay, err = lp2p.NewGossipRelayNode(log.NewLogger(nil, getLogLevel(t)), &relayCfg)
+ if err != nil {
+ return nil, fmt.Errorf("failed to construct drand gossip relay: %w", err)
+ }
+
+ t.RecordMessage("sharing gossip relay addrs")
+ // share the gossip relay addrs so we can publish them in DrandRuntimeInfo
+ relayInfo, err := relayAddrInfo(dr.gossipRelay.Multiaddrs(), myAddr)
+ if err != nil {
+ return nil, err
+ }
+ infoCh := make(chan *peer.AddrInfo, nNodes)
+ infoTopic := sync.NewTopic("drand-gossip-addrs", &peer.AddrInfo{})
+
+ _, sub := t.SyncClient.MustPublishSubscribe(ctx, infoTopic, relayInfo, infoCh)
+ for i := 0; i < nNodes; i++ {
+ select {
+ case ai := <-infoCh:
+ relayAddrs = append(relayAddrs, *ai)
+ case err := <-sub.Done():
+ return nil, fmt.Errorf("unable to get drand relay addr from sync service: %w", err)
+ }
+ }
+ }
+
+ // if we're the leader, publish the config to the sync service
+ if isLeader {
+ buf := bytes.Buffer{}
+ if err := info.ToJSON(&buf); err != nil {
+ return nil, fmt.Errorf("error marshaling chain info: %w", err)
+ }
+ cfg := DrandRuntimeInfo{
+ Config: dtypes.DrandConfig{
+ Servers: publicAddrs,
+ ChainInfoJSON: buf.String(),
+ },
+ GossipBootstrap: relayAddrs,
+ }
+ t.DebugSpew("publishing drand config on sync topic: %v", cfg)
+ t.SyncClient.MustPublish(ctx, DrandConfigTopic, &cfg)
+ }
+
+ // signal ready state
+ t.SyncClient.MustSignalAndWait(ctx, StateReady, t.TestInstanceCount)
+ return &dr, nil
+}
+
+// waitForDrandConfig should be called by filecoin instances before constructing the lotus Node
+// you can use the returned dtypes.DrandConfig to override the default production config.
+func waitForDrandConfig(ctx context.Context, client sync.Client) (*DrandRuntimeInfo, error) {
+ ch := make(chan *DrandRuntimeInfo, 1)
+ sub := client.MustSubscribe(ctx, DrandConfigTopic, ch)
+ select {
+ case cfg := <-ch:
+ return cfg, nil
+ case err := <-sub.Done():
+ return nil, err
+ }
+}
+
+func relayAddrInfo(addrs []ma.Multiaddr, dataIP net.IP) (*peer.AddrInfo, error) {
+ for _, a := range addrs {
+ if ip, _ := a.ValueForProtocol(ma.P_IP4); ip != dataIP.String() {
+ continue
+ }
+ return peer.AddrInfoFromP2pAddr(a)
+ }
+ return nil, fmt.Errorf("no addr found with data ip %s in addrs: %v", dataIP, addrs)
+}
+
+func getLogLevel(t *TestEnvironment) int {
+ switch t.StringParam("drand_log_level") {
+ case "info":
+ return log.LogInfo
+ case "debug":
+ return log.LogDebug
+ default:
+ return log.LogNone
+ }
+}
diff --git a/testplans/lotus-soup/testkit/role_miner.go b/testplans/lotus-soup/testkit/role_miner.go
new file mode 100644
index 000000000..8651d97f9
--- /dev/null
+++ b/testplans/lotus-soup/testkit/role_miner.go
@@ -0,0 +1,632 @@
+package testkit
+
+import (
+ "context"
+ "crypto/rand"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "path/filepath"
+ "time"
+
+ "contrib.go.opencensus.io/exporter/prometheus"
+ "github.com/filecoin-project/go-address"
+ "github.com/filecoin-project/go-jsonrpc"
+ "github.com/filecoin-project/go-jsonrpc/auth"
+ "github.com/filecoin-project/go-state-types/abi"
+ "github.com/filecoin-project/go-storedcounter"
+ "github.com/filecoin-project/lotus/api"
+ "github.com/filecoin-project/lotus/api/apistruct"
+ "github.com/filecoin-project/lotus/build"
+ "github.com/filecoin-project/lotus/chain/actors"
+ genesis_chain "github.com/filecoin-project/lotus/chain/gen/genesis"
+ "github.com/filecoin-project/lotus/chain/types"
+ "github.com/filecoin-project/lotus/chain/wallet"
+ "github.com/filecoin-project/lotus/cmd/lotus-seed/seed"
+ "github.com/filecoin-project/lotus/extern/sector-storage/stores"
+ "github.com/filecoin-project/lotus/miner"
+ "github.com/filecoin-project/lotus/node"
+ "github.com/filecoin-project/lotus/node/impl"
+ "github.com/filecoin-project/lotus/node/modules"
+ "github.com/filecoin-project/lotus/node/repo"
+ "github.com/filecoin-project/specs-actors/actors/builtin"
+ saminer "github.com/filecoin-project/specs-actors/actors/builtin/miner"
+ "github.com/google/uuid"
+ "github.com/gorilla/mux"
+ "github.com/hashicorp/go-multierror"
+ "github.com/ipfs/go-datastore"
+ libp2pcrypto "github.com/libp2p/go-libp2p-core/crypto"
+ "github.com/libp2p/go-libp2p-core/peer"
+ "github.com/testground/sdk-go/sync"
+)
+
+const (
+ sealDelay = 30 * time.Second
+)
+
+type LotusMiner struct {
+ *LotusNode
+
+ MinerRepo repo.Repo
+ NodeRepo repo.Repo
+ FullNetAddrs []peer.AddrInfo
+ GenesisMsg *GenesisMsg
+
+ t *TestEnvironment
+}
+
+func PrepareMiner(t *TestEnvironment) (*LotusMiner, error) {
+ ctx, cancel := context.WithTimeout(context.Background(), PrepareNodeTimeout)
+ defer cancel()
+
+ ApplyNetworkParameters(t)
+
+ pubsubTracer, err := GetPubsubTracerMaddr(ctx, t)
+ if err != nil {
+ return nil, err
+ }
+
+ drandOpt, err := GetRandomBeaconOpts(ctx, t)
+ if err != nil {
+ return nil, err
+ }
+
+ // first create a wallet
+ walletKey, err := wallet.GenerateKey(types.KTBLS)
+ if err != nil {
+ return nil, err
+ }
+
+ // publish the account ID/balance
+ balance := t.FloatParam("balance")
+ balanceMsg := &InitialBalanceMsg{Addr: walletKey.Address, Balance: balance}
+ t.SyncClient.Publish(ctx, BalanceTopic, balanceMsg)
+
+ // create and publish the preseal commitment
+ priv, _, err := libp2pcrypto.GenerateEd25519Key(rand.Reader)
+ if err != nil {
+ return nil, err
+ }
+
+ minerID, err := peer.IDFromPrivateKey(priv)
+ if err != nil {
+ return nil, err
+ }
+
+ // pick unique sequence number for each miner, no matter in which group they are
+ seq := t.SyncClient.MustSignalAndWait(ctx, StateMinerPickSeqNum, t.IntParam("miners"))
+
+ minerAddr, err := address.NewIDAddress(genesis_chain.MinerStart + uint64(seq-1))
+ if err != nil {
+ return nil, err
+ }
+
+ presealDir, err := ioutil.TempDir("", "preseal")
+ if err != nil {
+ return nil, err
+ }
+
+ sectors := t.IntParam("sectors")
+ genMiner, _, err := seed.PreSeal(minerAddr, abi.RegisteredSealProof_StackedDrg2KiBV1, 0, sectors, presealDir, []byte("TODO: randomize this"), &walletKey.KeyInfo, false)
+ if err != nil {
+ return nil, err
+ }
+ genMiner.PeerId = minerID
+
+ t.RecordMessage("Miner Info: Owner: %s Worker: %s", genMiner.Owner, genMiner.Worker)
+
+ presealMsg := &PresealMsg{Miner: *genMiner, Seqno: seq}
+ t.SyncClient.Publish(ctx, PresealTopic, presealMsg)
+
+ // then collect the genesis block and bootstrapper address
+ genesisMsg, err := WaitForGenesis(t, ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ // prepare the repo
+ minerRepoDir, err := ioutil.TempDir("", "miner-repo-dir")
+ if err != nil {
+ return nil, err
+ }
+
+ minerRepo, err := repo.NewFS(minerRepoDir)
+ if err != nil {
+ return nil, err
+ }
+
+ err = minerRepo.Init(repo.StorageMiner)
+ if err != nil {
+ return nil, err
+ }
+
+ {
+ lr, err := minerRepo.Lock(repo.StorageMiner)
+ if err != nil {
+ return nil, err
+ }
+
+ ks, err := lr.KeyStore()
+ if err != nil {
+ return nil, err
+ }
+
+ kbytes, err := priv.Bytes()
+ if err != nil {
+ return nil, err
+ }
+
+ err = ks.Put("libp2p-host", types.KeyInfo{
+ Type: "libp2p-host",
+ PrivateKey: kbytes,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ ds, err := lr.Datastore("/metadata")
+ if err != nil {
+ return nil, err
+ }
+
+ err = ds.Put(datastore.NewKey("miner-address"), minerAddr.Bytes())
+ if err != nil {
+ return nil, err
+ }
+
+ nic := storedcounter.New(ds, datastore.NewKey(modules.StorageCounterDSPrefix))
+ for i := 0; i < (sectors + 1); i++ {
+ _, err = nic.Next()
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ var localPaths []stores.LocalPath
+
+ b, err := json.MarshalIndent(&stores.LocalStorageMeta{
+ ID: stores.ID(uuid.New().String()),
+ Weight: 10,
+ CanSeal: true,
+ CanStore: true,
+ }, "", " ")
+ if err != nil {
+ return nil, fmt.Errorf("marshaling storage config: %w", err)
+ }
+
+ if err := ioutil.WriteFile(filepath.Join(lr.Path(), "sectorstore.json"), b, 0644); err != nil {
+ return nil, fmt.Errorf("persisting storage metadata (%s): %w", filepath.Join(lr.Path(), "sectorstore.json"), err)
+ }
+
+ localPaths = append(localPaths, stores.LocalPath{
+ Path: lr.Path(),
+ })
+
+ if err := lr.SetStorage(func(sc *stores.StorageConfig) {
+ sc.StoragePaths = append(sc.StoragePaths, localPaths...)
+ }); err != nil {
+ return nil, err
+ }
+
+ err = lr.Close()
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ minerIP := t.NetClient.MustGetDataNetworkIP().String()
+
+ // create the node
+ // we need both a full node _and_ and storage miner node
+ n := &LotusNode{}
+
+ // prepare the repo
+ nodeRepoDir, err := ioutil.TempDir("", "node-repo-dir")
+ if err != nil {
+ return nil, err
+ }
+
+ nodeRepo, err := repo.NewFS(nodeRepoDir)
+ if err != nil {
+ return nil, err
+ }
+
+ err = nodeRepo.Init(repo.FullNode)
+ if err != nil {
+ return nil, err
+ }
+
+ stop1, err := node.New(context.Background(),
+ node.FullAPI(&n.FullApi),
+ node.Online(),
+ node.Repo(nodeRepo),
+ withGenesis(genesisMsg.Genesis),
+ withApiEndpoint(fmt.Sprintf("/ip4/0.0.0.0/tcp/%s", t.PortNumber("node_rpc", "0"))),
+ withListenAddress(minerIP),
+ withBootstrapper(genesisMsg.Bootstrapper),
+ withPubsubConfig(false, pubsubTracer),
+ drandOpt,
+ )
+ if err != nil {
+ return nil, fmt.Errorf("node node.new error: %w", err)
+ }
+
+ // set the wallet
+ err = n.setWallet(ctx, walletKey)
+ if err != nil {
+ stop1(context.TODO())
+ return nil, err
+ }
+
+ minerOpts := []node.Option{
+ node.StorageMiner(&n.MinerApi),
+ node.Online(),
+ node.Repo(minerRepo),
+ node.Override(new(api.FullNode), n.FullApi),
+ withApiEndpoint(fmt.Sprintf("/ip4/0.0.0.0/tcp/%s", t.PortNumber("miner_rpc", "0"))),
+ withMinerListenAddress(minerIP),
+ }
+
+ if t.StringParam("mining_mode") != "natural" {
+ mineBlock := make(chan miner.MineReq)
+
+ minerOpts = append(minerOpts,
+ node.Override(new(*miner.Miner), miner.NewTestMiner(mineBlock, minerAddr)))
+
+ n.MineOne = func(ctx context.Context, cb miner.MineReq) error {
+ select {
+ case mineBlock <- cb:
+ return nil
+ case <-ctx.Done():
+ return ctx.Err()
+ }
+ }
+ }
+
+ stop2, err := node.New(context.Background(), minerOpts...)
+ if err != nil {
+ stop1(context.TODO())
+ return nil, fmt.Errorf("miner node.new error: %w", err)
+ }
+
+ registerAndExportMetrics(minerAddr.String())
+
+ // collect stats based on blockchain from first instance of `miner` role
+ if t.InitContext.GroupSeq == 1 && t.Role == "miner" {
+ go collectStats(t, ctx, n.FullApi)
+ }
+
+ // Start listening on the full node.
+ fullNodeNetAddrs, err := n.FullApi.NetAddrsListen(ctx)
+ if err != nil {
+ panic(err)
+ }
+
+ // set seal delay to lower value than 1 hour
+ err = n.MinerApi.SectorSetSealDelay(ctx, sealDelay)
+ if err != nil {
+ return nil, err
+ }
+
+ // set expected seal duration to 1 minute
+ err = n.MinerApi.SectorSetExpectedSealDuration(ctx, 1*time.Minute)
+ if err != nil {
+ return nil, err
+ }
+
+ // print out the admin auth token
+ token, err := n.MinerApi.AuthNew(ctx, apistruct.AllPermissions)
+ if err != nil {
+ return nil, err
+ }
+
+ t.RecordMessage("Auth token: %s", string(token))
+
+ // add local storage for presealed sectors
+ err = n.MinerApi.StorageAddLocal(ctx, presealDir)
+ if err != nil {
+ return nil, err
+ }
+
+ // set the miner PeerID
+ minerIDEncoded, err := actors.SerializeParams(&saminer.ChangePeerIDParams{NewID: abi.PeerID(minerID)})
+ if err != nil {
+ return nil, err
+ }
+
+ changeMinerID := &types.Message{
+ To: minerAddr,
+ From: genMiner.Worker,
+ Method: builtin.MethodsMiner.ChangePeerID,
+ Params: minerIDEncoded,
+ Value: types.NewInt(0),
+ }
+
+ _, err = n.FullApi.MpoolPushMessage(ctx, changeMinerID, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ t.RecordMessage("publish our address to the miners addr topic")
+ minerActor, err := n.MinerApi.ActorAddress(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ minerNetAddrs, err := n.MinerApi.NetAddrsListen(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ t.SyncClient.MustPublish(ctx, MinersAddrsTopic, MinerAddressesMsg{
+ FullNetAddrs: fullNodeNetAddrs,
+ MinerNetAddrs: minerNetAddrs,
+ MinerActorAddr: minerActor,
+ WalletAddr: walletKey.Address,
+ })
+
+ t.RecordMessage("connecting to all other miners")
+
+ // densely connect the miner's full nodes.
+ minerCh := make(chan *MinerAddressesMsg, 16)
+ sctx, cancel := context.WithCancel(ctx)
+ defer cancel()
+ t.SyncClient.MustSubscribe(sctx, MinersAddrsTopic, minerCh)
+ var fullNetAddrs []peer.AddrInfo
+ for i := 0; i < t.IntParam("miners"); i++ {
+ m := <-minerCh
+ if m.MinerActorAddr == minerActor {
+ // once I find myself, I stop connecting to others, to avoid a simopen problem.
+ break
+ }
+ err := n.FullApi.NetConnect(ctx, m.FullNetAddrs)
+ if err != nil {
+ return nil, fmt.Errorf("failed to connect to miner %s on: %v", m.MinerActorAddr, m.FullNetAddrs)
+ }
+ t.RecordMessage("connected to full node of miner %s on %v", m.MinerActorAddr, m.FullNetAddrs)
+
+ fullNetAddrs = append(fullNetAddrs, m.FullNetAddrs)
+ }
+
+ t.RecordMessage("waiting for all nodes to be ready")
+ t.SyncClient.MustSignalAndWait(ctx, StateReady, t.TestInstanceCount)
+
+ fullSrv, err := startFullNodeAPIServer(t, nodeRepo, n.FullApi)
+ if err != nil {
+ return nil, err
+ }
+
+ minerSrv, err := startStorageMinerAPIServer(t, minerRepo, n.MinerApi)
+ if err != nil {
+ return nil, err
+ }
+
+ n.StopFn = func(ctx context.Context) error {
+ var err *multierror.Error
+ err = multierror.Append(fullSrv.Shutdown(ctx))
+ err = multierror.Append(minerSrv.Shutdown(ctx))
+ err = multierror.Append(stop2(ctx))
+ err = multierror.Append(stop2(ctx))
+ err = multierror.Append(stop1(ctx))
+ return err.ErrorOrNil()
+ }
+
+ m := &LotusMiner{n, minerRepo, nodeRepo, fullNetAddrs, genesisMsg, t}
+
+ return m, nil
+}
+
+func RestoreMiner(t *TestEnvironment, m *LotusMiner) (*LotusMiner, error) {
+ ctx, cancel := context.WithTimeout(context.Background(), PrepareNodeTimeout)
+ defer cancel()
+
+ minerRepo := m.MinerRepo
+ nodeRepo := m.NodeRepo
+ fullNetAddrs := m.FullNetAddrs
+ genesisMsg := m.GenesisMsg
+
+ minerIP := t.NetClient.MustGetDataNetworkIP().String()
+
+ drandOpt, err := GetRandomBeaconOpts(ctx, t)
+ if err != nil {
+ return nil, err
+ }
+
+ // create the node
+ // we need both a full node _and_ and storage miner node
+ n := &LotusNode{}
+
+ stop1, err := node.New(context.Background(),
+ node.FullAPI(&n.FullApi),
+ node.Online(),
+ node.Repo(nodeRepo),
+ //withGenesis(genesisMsg.Genesis),
+ withApiEndpoint(fmt.Sprintf("/ip4/0.0.0.0/tcp/%s", t.PortNumber("node_rpc", "0"))),
+ withListenAddress(minerIP),
+ withBootstrapper(genesisMsg.Bootstrapper),
+ //withPubsubConfig(false, pubsubTracer),
+ drandOpt,
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ minerOpts := []node.Option{
+ node.StorageMiner(&n.MinerApi),
+ node.Online(),
+ node.Repo(minerRepo),
+ node.Override(new(api.FullNode), n.FullApi),
+ withApiEndpoint(fmt.Sprintf("/ip4/0.0.0.0/tcp/%s", t.PortNumber("miner_rpc", "0"))),
+ withMinerListenAddress(minerIP),
+ }
+
+ stop2, err := node.New(context.Background(), minerOpts...)
+ if err != nil {
+ stop1(context.TODO())
+ return nil, err
+ }
+
+ fullSrv, err := startFullNodeAPIServer(t, nodeRepo, n.FullApi)
+ if err != nil {
+ return nil, err
+ }
+
+ minerSrv, err := startStorageMinerAPIServer(t, minerRepo, n.MinerApi)
+ if err != nil {
+ return nil, err
+ }
+
+ n.StopFn = func(ctx context.Context) error {
+ var err *multierror.Error
+ err = multierror.Append(fullSrv.Shutdown(ctx))
+ err = multierror.Append(minerSrv.Shutdown(ctx))
+ err = multierror.Append(stop2(ctx))
+ err = multierror.Append(stop2(ctx))
+ err = multierror.Append(stop1(ctx))
+ return err.ErrorOrNil()
+ }
+
+ for i := 0; i < len(fullNetAddrs); i++ {
+ err := n.FullApi.NetConnect(ctx, fullNetAddrs[i])
+ if err != nil {
+ // we expect a failure since we also shutdown another miner
+ t.RecordMessage("failed to connect to miner %d on: %v", i, fullNetAddrs[i])
+ continue
+ }
+ t.RecordMessage("connected to full node of miner %d on %v", i, fullNetAddrs[i])
+ }
+
+ pm := &LotusMiner{n, minerRepo, nodeRepo, fullNetAddrs, genesisMsg, t}
+
+ return pm, err
+}
+
+func (m *LotusMiner) RunDefault() error {
+ var (
+ t = m.t
+ clients = t.IntParam("clients")
+ miners = t.IntParam("miners")
+ )
+
+ t.RecordMessage("running miner")
+ t.RecordMessage("block delay: %v", build.BlockDelaySecs)
+ t.D().Gauge("miner.block-delay").Update(float64(build.BlockDelaySecs))
+
+ ctx := context.Background()
+ myActorAddr, err := m.MinerApi.ActorAddress(ctx)
+ if err != nil {
+ return err
+ }
+
+ // mine / stop mining
+ mine := true
+ done := make(chan struct{})
+
+ if m.MineOne != nil {
+ go func() {
+ defer t.RecordMessage("shutting down mining")
+ defer close(done)
+
+ var i int
+ for i = 0; mine; i++ {
+ // synchronize all miners to mine the next block
+ t.RecordMessage("synchronizing all miners to mine next block [%d]", i)
+ stateMineNext := sync.State(fmt.Sprintf("mine-block-%d", i))
+ t.SyncClient.MustSignalAndWait(ctx, stateMineNext, miners)
+
+ ch := make(chan error)
+ const maxRetries = 100
+ success := false
+ for retries := 0; retries < maxRetries; retries++ {
+ f := func(mined bool, epoch abi.ChainEpoch, err error) {
+ if mined {
+ t.D().Counter(fmt.Sprintf("block.mine,miner=%s", myActorAddr)).Inc(1)
+ }
+ ch <- err
+ }
+ req := miner.MineReq{
+ Done: f,
+ }
+ err := m.MineOne(ctx, req)
+ if err != nil {
+ panic(err)
+ }
+
+ miningErr := <-ch
+ if miningErr == nil {
+ success = true
+ break
+ }
+ t.D().Counter("block.mine.err").Inc(1)
+ t.RecordMessage("retrying block [%d] after %d attempts due to mining error: %s",
+ i, retries, miningErr)
+ }
+ if !success {
+ panic(fmt.Errorf("failed to mine block %d after %d retries", i, maxRetries))
+ }
+ }
+
+ // signal the last block to make sure no miners are left stuck waiting for the next block signal
+ // while the others have stopped
+ stateMineLast := sync.State(fmt.Sprintf("mine-block-%d", i))
+ t.SyncClient.MustSignalEntry(ctx, stateMineLast)
+ }()
+ } else {
+ close(done)
+ }
+
+ // wait for a signal from all clients to stop mining
+ err = <-t.SyncClient.MustBarrier(ctx, StateStopMining, clients).C
+ if err != nil {
+ return err
+ }
+
+ mine = false
+ <-done
+
+ t.SyncClient.MustSignalAndWait(ctx, StateDone, t.TestInstanceCount)
+ return nil
+}
+
+func startStorageMinerAPIServer(t *TestEnvironment, repo repo.Repo, minerApi api.StorageMiner) (*http.Server, error) {
+ mux := mux.NewRouter()
+
+ rpcServer := jsonrpc.NewServer()
+ rpcServer.Register("Filecoin", minerApi)
+
+ mux.Handle("/rpc/v0", rpcServer)
+ mux.PathPrefix("/remote").HandlerFunc(minerApi.(*impl.StorageMinerAPI).ServeRemote)
+ mux.PathPrefix("/").Handler(http.DefaultServeMux) // pprof
+
+ exporter, err := prometheus.NewExporter(prometheus.Options{
+ Namespace: "lotus",
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ mux.Handle("/debug/metrics", exporter)
+
+ ah := &auth.Handler{
+ Verify: func(ctx context.Context, token string) ([]auth.Permission, error) {
+ return apistruct.AllPermissions, nil
+ },
+ Next: mux.ServeHTTP,
+ }
+
+ endpoint, err := repo.APIEndpoint()
+ if err != nil {
+ return nil, fmt.Errorf("no API endpoint in repo: %w", err)
+ }
+
+ srv := &http.Server{Handler: ah}
+
+ listenAddr, err := startServer(endpoint, srv)
+ if err != nil {
+ return nil, fmt.Errorf("failed to start storage miner API endpoint: %w", err)
+ }
+
+ t.RecordMessage("started storage miner API server at %s", listenAddr)
+ return srv, nil
+}
diff --git a/testplans/lotus-soup/testkit/role_pubsub_tracer.go b/testplans/lotus-soup/testkit/role_pubsub_tracer.go
new file mode 100644
index 000000000..5b13e6b81
--- /dev/null
+++ b/testplans/lotus-soup/testkit/role_pubsub_tracer.go
@@ -0,0 +1,79 @@
+package testkit
+
+import (
+ "context"
+ "crypto/rand"
+ "fmt"
+
+ "github.com/libp2p/go-libp2p"
+ "github.com/libp2p/go-libp2p-core/crypto"
+ "github.com/libp2p/go-libp2p-core/host"
+ "github.com/libp2p/go-libp2p-pubsub-tracer/traced"
+
+ ma "github.com/multiformats/go-multiaddr"
+)
+
+type PubsubTracer struct {
+ t *TestEnvironment
+ host host.Host
+ traced *traced.TraceCollector
+}
+
+func PreparePubsubTracer(t *TestEnvironment) (*PubsubTracer, error) {
+ ctx := context.Background()
+
+ privk, _, err := crypto.GenerateEd25519Key(rand.Reader)
+ if err != nil {
+ return nil, err
+ }
+
+ tracedIP := t.NetClient.MustGetDataNetworkIP().String()
+ tracedAddr := fmt.Sprintf("/ip4/%s/tcp/4001", tracedIP)
+
+ host, err := libp2p.New(ctx,
+ libp2p.Identity(privk),
+ libp2p.ListenAddrStrings(tracedAddr),
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ tracedDir := t.TestOutputsPath + "/traced.logs"
+ traced, err := traced.NewTraceCollector(host, tracedDir)
+ if err != nil {
+ host.Close()
+ return nil, err
+ }
+
+ tracedMultiaddrStr := fmt.Sprintf("%s/p2p/%s", tracedAddr, host.ID())
+ t.RecordMessage("I am %s", tracedMultiaddrStr)
+
+ _ = ma.StringCast(tracedMultiaddrStr)
+ tracedMsg := &PubsubTracerMsg{Multiaddr: tracedMultiaddrStr}
+ t.SyncClient.MustPublish(ctx, PubsubTracerTopic, tracedMsg)
+
+ t.RecordMessage("waiting for all nodes to be ready")
+ t.SyncClient.MustSignalAndWait(ctx, StateReady, t.TestInstanceCount)
+
+ tracer := &PubsubTracer{t: t, host: host, traced: traced}
+ return tracer, nil
+}
+
+func (tr *PubsubTracer) RunDefault() error {
+ tr.t.RecordMessage("running pubsub tracer")
+
+ defer func() {
+ err := tr.Stop()
+ if err != nil {
+ tr.t.RecordMessage("error stoping tracer: %s", err)
+ }
+ }()
+
+ tr.t.WaitUntilAllDone()
+ return nil
+}
+
+func (tr *PubsubTracer) Stop() error {
+ tr.traced.Stop()
+ return tr.host.Close()
+}
diff --git a/testplans/lotus-soup/testkit/sync.go b/testplans/lotus-soup/testkit/sync.go
new file mode 100644
index 000000000..a61e29612
--- /dev/null
+++ b/testplans/lotus-soup/testkit/sync.go
@@ -0,0 +1,69 @@
+package testkit
+
+import (
+ "github.com/filecoin-project/go-address"
+ "github.com/filecoin-project/lotus/genesis"
+ "github.com/filecoin-project/lotus/node/modules/dtypes"
+ "github.com/libp2p/go-libp2p-core/peer"
+ "github.com/testground/sdk-go/sync"
+)
+
+var (
+ GenesisTopic = sync.NewTopic("genesis", &GenesisMsg{})
+ BalanceTopic = sync.NewTopic("balance", &InitialBalanceMsg{})
+ PresealTopic = sync.NewTopic("preseal", &PresealMsg{})
+ ClientsAddrsTopic = sync.NewTopic("clients_addrs", &ClientAddressesMsg{})
+ MinersAddrsTopic = sync.NewTopic("miners_addrs", &MinerAddressesMsg{})
+ SlashedMinerTopic = sync.NewTopic("slashed_miner", &SlashedMinerMsg{})
+ PubsubTracerTopic = sync.NewTopic("pubsub_tracer", &PubsubTracerMsg{})
+ DrandConfigTopic = sync.NewTopic("drand_config", &DrandRuntimeInfo{})
+)
+
+var (
+ StateReady = sync.State("ready")
+ StateDone = sync.State("done")
+ StateStopMining = sync.State("stop-mining")
+ StateMinerPickSeqNum = sync.State("miner-pick-seq-num")
+ StateAbortTest = sync.State("abort-test")
+)
+
+type InitialBalanceMsg struct {
+ Addr address.Address
+ Balance float64
+}
+
+type PresealMsg struct {
+ Miner genesis.Miner
+ Seqno int64
+}
+
+type GenesisMsg struct {
+ Genesis []byte
+ Bootstrapper []byte
+}
+
+type ClientAddressesMsg struct {
+ PeerNetAddr peer.AddrInfo
+ WalletAddr address.Address
+ GroupSeq int64
+}
+
+type MinerAddressesMsg struct {
+ FullNetAddrs peer.AddrInfo
+ MinerNetAddrs peer.AddrInfo
+ MinerActorAddr address.Address
+ WalletAddr address.Address
+}
+
+type SlashedMinerMsg struct {
+ MinerActorAddr address.Address
+}
+
+type PubsubTracerMsg struct {
+ Multiaddr string
+}
+
+type DrandRuntimeInfo struct {
+ Config dtypes.DrandConfig
+ GossipBootstrap dtypes.DrandBootstrap
+}
diff --git a/testplans/lotus-soup/testkit/testenv.go b/testplans/lotus-soup/testkit/testenv.go
new file mode 100644
index 000000000..63c297b03
--- /dev/null
+++ b/testplans/lotus-soup/testkit/testenv.go
@@ -0,0 +1,88 @@
+package testkit
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "strings"
+ "time"
+
+ "github.com/davecgh/go-spew/spew"
+ "github.com/testground/sdk-go/run"
+ "github.com/testground/sdk-go/runtime"
+)
+
+type TestEnvironment struct {
+ *runtime.RunEnv
+ *run.InitContext
+
+ Role string
+}
+
+// workaround for default params being wrapped in quote chars
+func (t *TestEnvironment) StringParam(name string) string {
+ return strings.Trim(t.RunEnv.StringParam(name), "\"")
+}
+
+func (t *TestEnvironment) DurationParam(name string) time.Duration {
+ d, err := time.ParseDuration(t.StringParam(name))
+ if err != nil {
+ panic(fmt.Errorf("invalid duration value for param '%s': %w", name, err))
+ }
+ return d
+}
+
+func (t *TestEnvironment) DurationRangeParam(name string) DurationRange {
+ var r DurationRange
+ t.JSONParam(name, &r)
+ return r
+}
+
+func (t *TestEnvironment) FloatRangeParam(name string) FloatRange {
+ r := FloatRange{}
+ t.JSONParam(name, &r)
+ return r
+}
+
+func (t *TestEnvironment) DebugSpew(format string, args ...interface{}) {
+ t.RecordMessage(spew.Sprintf(format, args...))
+}
+
+func (t *TestEnvironment) DumpJSON(filename string, v interface{}) {
+ b, err := json.Marshal(v)
+ if err != nil {
+ t.RecordMessage("unable to marshal object to JSON: %s", err)
+ return
+ }
+ f, err := t.CreateRawAsset(filename)
+ if err != nil {
+ t.RecordMessage("unable to create asset file: %s", err)
+ return
+ }
+ defer f.Close()
+
+ _, err = f.Write(b)
+ if err != nil {
+ t.RecordMessage("error writing json object dump: %s", err)
+ }
+}
+
+// WaitUntilAllDone waits until all instances in the test case are done.
+func (t *TestEnvironment) WaitUntilAllDone() {
+ ctx := context.Background()
+ t.SyncClient.MustSignalAndWait(ctx, StateDone, t.TestInstanceCount)
+}
+
+// WrapTestEnvironment takes a test case function that accepts a
+// *TestEnvironment, and adapts it to the original unwrapped SDK style
+// (run.InitializedTestCaseFn).
+func WrapTestEnvironment(f func(t *TestEnvironment) error) run.InitializedTestCaseFn {
+ return func(runenv *runtime.RunEnv, initCtx *run.InitContext) error {
+ t := &TestEnvironment{RunEnv: runenv, InitContext: initCtx}
+ t.Role = t.StringParam("role")
+
+ t.DumpJSON("test-parameters.json", t.TestInstanceParams)
+
+ return f(t)
+ }
+}
diff --git a/testplans/lotus-soup/testkit/testenv_ranges.go b/testplans/lotus-soup/testkit/testenv_ranges.go
new file mode 100644
index 000000000..110ce60d1
--- /dev/null
+++ b/testplans/lotus-soup/testkit/testenv_ranges.go
@@ -0,0 +1,77 @@
+package testkit
+
+import (
+ "encoding/json"
+ "fmt"
+ "math/rand"
+ "time"
+
+ "github.com/testground/sdk-go/ptypes"
+)
+
+// DurationRange is a Testground parameter type that represents a duration
+// range, suitable use in randomized tests. This type is encoded as a JSON array
+// of length 2 of element type ptypes.Duration, e.g. ["10s", "10m"].
+type DurationRange struct {
+ Min time.Duration
+ Max time.Duration
+}
+
+func (r *DurationRange) ChooseRandom() time.Duration {
+ i := int64(r.Min) + rand.Int63n(int64(r.Max)-int64(r.Min))
+ return time.Duration(i)
+}
+
+func (r *DurationRange) UnmarshalJSON(b []byte) error {
+ var s []ptypes.Duration
+ if err := json.Unmarshal(b, &s); err != nil {
+ return err
+ }
+ if len(s) != 2 {
+ return fmt.Errorf("expected two-element array of duration strings, got array of length %d", len(s))
+ }
+ if s[0].Duration > s[1].Duration {
+ return fmt.Errorf("expected first element to be <= second element")
+ }
+ r.Min = s[0].Duration
+ r.Max = s[1].Duration
+ return nil
+}
+
+func (r *DurationRange) MarshalJSON() ([]byte, error) {
+ s := []ptypes.Duration{{r.Min}, {r.Max}}
+ return json.Marshal(s)
+}
+
+// FloatRange is a Testground parameter type that represents a float
+// range, suitable use in randomized tests. This type is encoded as a JSON array
+// of length 2 of element type float32, e.g. [1.45, 10.675].
+type FloatRange struct {
+ Min float32
+ Max float32
+}
+
+func (r *FloatRange) ChooseRandom() float32 {
+ return r.Min + rand.Float32()*(r.Max-r.Min)
+}
+
+func (r *FloatRange) UnmarshalJSON(b []byte) error {
+ var s []float32
+ if err := json.Unmarshal(b, &s); err != nil {
+ return err
+ }
+ if len(s) != 2 {
+ return fmt.Errorf("expected two-element array of floats, got array of length %d", len(s))
+ }
+ if s[0] > s[1] {
+ return fmt.Errorf("expected first element to be <= second element")
+ }
+ r.Min = s[0]
+ r.Max = s[1]
+ return nil
+}
+
+func (r *FloatRange) MarshalJSON() ([]byte, error) {
+ s := []float32{r.Min, r.Max}
+ return json.Marshal(s)
+}
diff --git a/testplans/notes/.empty b/testplans/notes/.empty
new file mode 100644
index 000000000..e69de29bb
diff --git a/testplans/notes/raulk.md b/testplans/notes/raulk.md
new file mode 100644
index 000000000..88476f2a3
--- /dev/null
+++ b/testplans/notes/raulk.md
@@ -0,0 +1,55 @@
+# Raúl's notes
+
+## Storage mining
+
+The Storage Mining System is the part of the Filecoin Protocol that deals with
+storing Client’s data, producing proof artifacts that demonstrate correct
+storage behavior, and managing the work involved.
+
+## Preseals
+
+In the Filecoin consensus protocol, the miners' probability of being eligible
+to mine a block in a given epoch is directly correlated with their power in the
+network. This creates a chicken-and-egg problem at genesis. Since there are no
+miners, there is no power in the network, therefore no miner is eligible to mine
+and advance the chain.
+
+Preseals are sealed sectors that are blessed at genesis, thus conferring
+their miners the possibility to win round elections and successfully mine a
+block. Without preseals, the chain would be dead on arrival.
+
+Preseals work with fauxrep and faux sealing, which are special-case
+implementations of PoRep and the sealing logic that do not depend on slow
+sealing.
+
+### Not implemented things
+
+**Sector Resealing:** Miners should be able to ’re-seal’ sectors, to allow them
+to take a set of sectors with mostly expired pieces, and combine the
+not-yet-expired pieces into a single (or multiple) sectors.
+
+**Sector Transfer:** Miners should be able to re-delegate the responsibility of
+storing data to another miner. This is tricky for many reasons, and will not be
+implemented in the initial release of Filecoin, but could provide interesting
+capabilities down the road.
+
+## Catch-up/rush mining
+
+In catch-up or rush mining, miners make up for chain history that does not
+exist. It's a recovery/healing procedure. The chain runs at at constant
+25 second epoch time. When in the network mining halts for some reason
+(consensus/liveness bug, drand availability issues, etc.), upon a restart miners
+will go and backfill the chain history by mining backdated blocks in
+the appropriate timestamps.
+
+There are a few things worth highlighting:
+ * mining runs in a hot loop, and there is no time for miners to gossip about
+ their blocks; therefore they end up building the chain solo, as they can't
+ incorprate other blocks into tipsets.
+ * the miner with most power will mine most blocks.
+ * presumably, as many forks in the network will appear as miners who mined a
+ block + a fork filled with null rounds only (for miners that didn't win a
+ round).
+ * at the end of the catch-up, the heaviest fork will win the race, and it may
+ be possible for the most powerful miner pre-disruption to affect the
+ outcome by choosing the messages that go in their blocks.