From 9e34cce1f2f3984573b33f2fef839e81c34ca1ee Mon Sep 17 00:00:00 2001 From: Mike Greenberg Date: Tue, 20 Jul 2021 17:19:28 -0400 Subject: [PATCH 01/31] feat(config): support configurable CC sector expiration --- extern/storage-sealing/sealiface/config.go | 2 ++ node/config/def.go | 6 ++++++ storage/miner.go | 18 ++++++++++++++---- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/extern/storage-sealing/sealiface/config.go b/extern/storage-sealing/sealiface/config.go index e33b36263..59c3d8c4f 100644 --- a/extern/storage-sealing/sealiface/config.go +++ b/extern/storage-sealing/sealiface/config.go @@ -20,6 +20,8 @@ type Config struct { WaitDealsDelay time.Duration + CommittedCapacityDefaultLifetime time.Duration + AlwaysKeepUnsealedCopy bool FinalizeEarly bool diff --git a/node/config/def.go b/node/config/def.go index 7042cf644..bf6526e4d 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -131,6 +131,12 @@ type SealingConfig struct { WaitDealsDelay Duration + // CommittedCapacityDefaultLifetime is the default duration a Committed Capacity (CC) + // sector will live before it must be extended or converted into sector containing deals + // before it is terminated. + // Value must be between 180-540 days inclusive. + CommittedCapacityDefaultLifetime Duration + AlwaysKeepUnsealedCopy bool // Run sector finalization before submitting sector proof to the chain diff --git a/storage/miner.go b/storage/miner.go index cdacc2734..06ee874a1 100644 --- a/storage/miner.go +++ b/storage/miner.go @@ -178,8 +178,11 @@ func (m *Miner) Run(ctx context.Context) error { // provides extra methods. adaptedAPI = NewSealingAPIAdapter(m.api) + // sealing configuration. + cfg = sealing.GetSealingConfigFunc(m.getSealConfig) + // Instantiate a precommit policy. - defaultDuration = policy.GetMaxSectorExpirationExtension() - (md.WPoStProvingPeriod * 2) + defaultDuration = getDefaultSectorExpirationExtension(cfg) - (md.WPoStProvingPeriod * 2) provingBoundary = md.PeriodStart % md.WPoStProvingPeriod // TODO: Maybe we update this policy after actor upgrades? @@ -189,9 +192,6 @@ func (m *Miner) Run(ctx context.Context) error { as = func(ctx context.Context, mi miner.MinerInfo, use api.AddrUse, goodFunds, minFunds abi.TokenAmount) (address.Address, abi.TokenAmount, error) { return m.addrSel.AddressFor(ctx, m.api, mi, use, goodFunds, minFunds) } - - // sealing configuration. - cfg = sealing.GetSealingConfigFunc(m.getSealConfig) ) // Instantiate the sealing FSM. @@ -203,6 +203,16 @@ func (m *Miner) Run(ctx context.Context) error { return nil } +func getDefaultSectorExpirationExtension(cfg sealing.GetSealingConfigFunc) abi.ChainEpoch { + c, err := cfg() + if err != nil { + log.Warnf("failed to load sealing config, using default sector extension expiration") + log.Errorf("sealing config load error: %s", err.Error()) + return policy.GetMaxSectorExpirationExtension() + } + return abi.ChainEpoch(c.CommittedCapacityDefaultLifetime.Truncate(builtin.EpochDurationSeconds)) +} + func (m *Miner) handleSealingNotifications(before, after sealing.SectorInfo) { m.journal.RecordEvent(m.sealingEvtType, func() interface{} { return SealingStateEvt{ From adb62a3fff61d74aa07bd450ea4daa6044366546 Mon Sep 17 00:00:00 2001 From: Mike Greenberg Date: Thu, 22 Jul 2021 08:37:35 -0400 Subject: [PATCH 02/31] chore: Move cfg getter into PCPolicy; Clamp values on get --- chain/actors/policy/policy.go | 4 ++ extern/storage-sealing/precommit_policy.go | 48 +++++++++++++++++----- extern/storage-sealing/sealiface/config.go | 2 +- node/config/def.go | 13 +++--- storage/miner.go | 18 +------- 5 files changed, 53 insertions(+), 32 deletions(-) diff --git a/chain/actors/policy/policy.go b/chain/actors/policy/policy.go index c06c85d38..06db09ea8 100644 --- a/chain/actors/policy/policy.go +++ b/chain/actors/policy/policy.go @@ -301,6 +301,10 @@ func GetWinningPoStSectorSetLookback(nwVer network.Version) abi.ChainEpoch { return ChainFinality } +func GetMinSectorExpiration() abi.ChainEpoch { + return miner5.MinSectorExpiration +} + func GetMaxSectorExpirationExtension() abi.ChainEpoch { return miner5.MaxSectorExpirationExtension } diff --git a/extern/storage-sealing/precommit_policy.go b/extern/storage-sealing/precommit_policy.go index a6add5693..bc5a0a530 100644 --- a/extern/storage-sealing/precommit_policy.go +++ b/extern/storage-sealing/precommit_policy.go @@ -3,11 +3,13 @@ package sealing import ( "context" - "github.com/filecoin-project/lotus/chain/actors/builtin/miner" - - "github.com/filecoin-project/go-state-types/network" + "golang.org/x/xerrors" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/network" + "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/actors/policy" ) type PreCommitPolicy interface { @@ -34,21 +36,21 @@ type Chain interface { // If we're in Mode 2: The pre-commit expiration epoch will be set to the // current epoch + the provided default duration. type BasicPreCommitPolicy struct { - api Chain + api Chain + getSealingConfig GetSealingConfigFunc provingBoundary abi.ChainEpoch - duration abi.ChainEpoch } // NewBasicPreCommitPolicy produces a BasicPreCommitPolicy. // // The provided duration is used as the default sector expiry when the sector // contains no deals. The proving boundary is used to adjust/align the sector's expiration. -func NewBasicPreCommitPolicy(api Chain, duration abi.ChainEpoch, provingBoundary abi.ChainEpoch) BasicPreCommitPolicy { +func NewBasicPreCommitPolicy(api Chain, cfgGetter GetSealingConfigFunc, provingBoundary abi.ChainEpoch) BasicPreCommitPolicy { return BasicPreCommitPolicy{ - api: api, - provingBoundary: provingBoundary, - duration: duration, + api: api, + getSealingConfig: cfgGetter, + provingBoundary: provingBoundary, } } @@ -79,7 +81,13 @@ func (p *BasicPreCommitPolicy) Expiration(ctx context.Context, ps ...Piece) (abi } if end == nil { - tmp := epoch + p.duration + // no deal pieces, get expiration for committed capacity sector + expirationDuration, err := p.getCCSectorLifetime() + if err != nil { + return 0, err + } + + tmp := epoch + expirationDuration end = &tmp } @@ -87,3 +95,23 @@ func (p *BasicPreCommitPolicy) Expiration(ctx context.Context, ps ...Piece) (abi return *end, nil } + +func (p *BasicPreCommitPolicy) getCCSectorLifetime() (abi.ChainEpoch, error) { + c, err := p.getSealingConfig() + if err != nil { + return 0, xerrors.Errorf("sealing config load error: %w", err) + } + + sectorExpiration := abi.ChainEpoch(c.CommittedCapacitySectorLifetime.Truncate(builtin.EpochDurationSeconds) / builtin.EpochDurationSeconds) + + if minExpiration := policy.GetMinSectorExpiration(); sectorExpiration < minExpiration { + log.Warnf("value for CommittedCapacitySectorLiftime is too short, using default minimum (%d epochs)", minExpiration) + return minExpiration, nil + } + if maxExpiration := policy.GetMaxSectorExpirationExtension(); sectorExpiration > maxExpiration { + log.Warnf("value for CommittedCapacitySectorLiftime is too long, using default maximum (%d epochs)", maxExpiration) + return maxExpiration, nil + } + + return sectorExpiration, nil +} diff --git a/extern/storage-sealing/sealiface/config.go b/extern/storage-sealing/sealiface/config.go index 59c3d8c4f..95b851609 100644 --- a/extern/storage-sealing/sealiface/config.go +++ b/extern/storage-sealing/sealiface/config.go @@ -20,7 +20,7 @@ type Config struct { WaitDealsDelay time.Duration - CommittedCapacityDefaultLifetime time.Duration + CommittedCapacitySectorLifetime time.Duration AlwaysKeepUnsealedCopy bool diff --git a/node/config/def.go b/node/config/def.go index bf6526e4d..17f43ffc3 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -10,6 +10,8 @@ import ( "github.com/filecoin-project/go-state-types/big" miner5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/chain/types" sectorstorage "github.com/filecoin-project/lotus/extern/sector-storage" ) @@ -131,11 +133,10 @@ type SealingConfig struct { WaitDealsDelay Duration - // CommittedCapacityDefaultLifetime is the default duration a Committed Capacity (CC) - // sector will live before it must be extended or converted into sector containing deals - // before it is terminated. - // Value must be between 180-540 days inclusive. - CommittedCapacityDefaultLifetime Duration + // CommittedCapacitySectorLifetime is the duration a Committed Capacity (CC) sector will + // live before it must be extended or converted into sector containing deals before it is + // terminated. Value must be between 180-540 days inclusive. + CommittedCapacitySectorLifetime Duration AlwaysKeepUnsealedCopy bool @@ -358,6 +359,8 @@ func DefaultStorageMiner() *StorageMiner { PreCommitBatchWait: Duration(24 * time.Hour), // this should be less than 31.5 hours, which is the expiration of a precommit ticket PreCommitBatchSlack: Duration(3 * time.Hour), // time buffer for forceful batch submission before sectors/deals in batch would start expiring, higher value will lower the chances for message fail due to expiration + CommittedCapacitySectorLifetime: Duration(builtin.EpochDurationSeconds * policy.GetMaxSectorExpirationExtension()), + AggregateCommits: true, MinCommitBatch: miner5.MinAggregatedSectors, // per FIP13, we must have at least four proofs to aggregate, where 4 is the cross over point where aggregation wins out on single provecommit gas costs MaxCommitBatch: miner5.MaxAggregatedSectors, // maximum 819 sectors, this is the maximum aggregation per FIP13 diff --git a/storage/miner.go b/storage/miner.go index 06ee874a1..c38671c8c 100644 --- a/storage/miner.go +++ b/storage/miner.go @@ -27,7 +27,6 @@ import ( "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" - "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/chain/events" "github.com/filecoin-project/lotus/chain/gen" "github.com/filecoin-project/lotus/chain/types" @@ -178,15 +177,12 @@ func (m *Miner) Run(ctx context.Context) error { // provides extra methods. adaptedAPI = NewSealingAPIAdapter(m.api) - // sealing configuration. - cfg = sealing.GetSealingConfigFunc(m.getSealConfig) - // Instantiate a precommit policy. - defaultDuration = getDefaultSectorExpirationExtension(cfg) - (md.WPoStProvingPeriod * 2) + cfg = sealing.GetSealingConfigFunc(m.getSealConfig) provingBoundary = md.PeriodStart % md.WPoStProvingPeriod // TODO: Maybe we update this policy after actor upgrades? - pcp = sealing.NewBasicPreCommitPolicy(adaptedAPI, defaultDuration, provingBoundary) + pcp = sealing.NewBasicPreCommitPolicy(adaptedAPI, cfg, provingBoundary) // address selector. as = func(ctx context.Context, mi miner.MinerInfo, use api.AddrUse, goodFunds, minFunds abi.TokenAmount) (address.Address, abi.TokenAmount, error) { @@ -203,16 +199,6 @@ func (m *Miner) Run(ctx context.Context) error { return nil } -func getDefaultSectorExpirationExtension(cfg sealing.GetSealingConfigFunc) abi.ChainEpoch { - c, err := cfg() - if err != nil { - log.Warnf("failed to load sealing config, using default sector extension expiration") - log.Errorf("sealing config load error: %s", err.Error()) - return policy.GetMaxSectorExpirationExtension() - } - return abi.ChainEpoch(c.CommittedCapacityDefaultLifetime.Truncate(builtin.EpochDurationSeconds)) -} - func (m *Miner) handleSealingNotifications(before, after sealing.SectorInfo) { m.journal.RecordEvent(m.sealingEvtType, func() interface{} { return SealingStateEvt{ From 7ee46ad4e0638822c7dfcdf6fb4aa306ec3f9f40 Mon Sep 17 00:00:00 2001 From: Mike Greenberg Date: Thu, 22 Jul 2021 14:44:39 -0400 Subject: [PATCH 03/31] fix: PreCommitPolicy unit tests --- extern/storage-sealing/precommit_policy.go | 16 +++-- .../storage-sealing/precommit_policy_test.go | 63 ++++++++++++++----- storage/miner.go | 3 +- 3 files changed, 61 insertions(+), 21 deletions(-) diff --git a/extern/storage-sealing/precommit_policy.go b/extern/storage-sealing/precommit_policy.go index bc5a0a530..6d1fddc47 100644 --- a/extern/storage-sealing/precommit_policy.go +++ b/extern/storage-sealing/precommit_policy.go @@ -40,17 +40,19 @@ type BasicPreCommitPolicy struct { getSealingConfig GetSealingConfigFunc provingBoundary abi.ChainEpoch + provingBuffer abi.ChainEpoch } // NewBasicPreCommitPolicy produces a BasicPreCommitPolicy. // // The provided duration is used as the default sector expiry when the sector // contains no deals. The proving boundary is used to adjust/align the sector's expiration. -func NewBasicPreCommitPolicy(api Chain, cfgGetter GetSealingConfigFunc, provingBoundary abi.ChainEpoch) BasicPreCommitPolicy { +func NewBasicPreCommitPolicy(api Chain, cfgGetter GetSealingConfigFunc, provingBoundary abi.ChainEpoch, provingBuffer abi.ChainEpoch) BasicPreCommitPolicy { return BasicPreCommitPolicy{ api: api, getSealingConfig: cfgGetter, provingBoundary: provingBoundary, + provingBuffer: provingBuffer, } } @@ -102,16 +104,20 @@ func (p *BasicPreCommitPolicy) getCCSectorLifetime() (abi.ChainEpoch, error) { return 0, xerrors.Errorf("sealing config load error: %w", err) } - sectorExpiration := abi.ChainEpoch(c.CommittedCapacitySectorLifetime.Truncate(builtin.EpochDurationSeconds) / builtin.EpochDurationSeconds) + var ccLifetimeEpochs = abi.ChainEpoch(uint64(c.CommittedCapacitySectorLifetime.Truncate(builtin.EpochDurationSeconds).Seconds()) / builtin.EpochDurationSeconds) + // if zero value in config, assume maximum sector extension + if ccLifetimeEpochs == 0 { + ccLifetimeEpochs = policy.GetMaxSectorExpirationExtension() + } - if minExpiration := policy.GetMinSectorExpiration(); sectorExpiration < minExpiration { + if minExpiration := policy.GetMinSectorExpiration(); ccLifetimeEpochs < minExpiration { log.Warnf("value for CommittedCapacitySectorLiftime is too short, using default minimum (%d epochs)", minExpiration) return minExpiration, nil } - if maxExpiration := policy.GetMaxSectorExpirationExtension(); sectorExpiration > maxExpiration { + if maxExpiration := policy.GetMaxSectorExpirationExtension(); ccLifetimeEpochs > maxExpiration { log.Warnf("value for CommittedCapacitySectorLiftime is too long, using default maximum (%d epochs)", maxExpiration) return maxExpiration, nil } - return sectorExpiration, nil + return (ccLifetimeEpochs - p.provingBuffer), nil } diff --git a/extern/storage-sealing/precommit_policy_test.go b/extern/storage-sealing/precommit_policy_test.go index a6c17d3fd..aa56f1e6f 100644 --- a/extern/storage-sealing/precommit_policy_test.go +++ b/extern/storage-sealing/precommit_policy_test.go @@ -3,10 +3,15 @@ package sealing_test import ( "context" "testing" + "time" "github.com/filecoin-project/go-state-types/network" api "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/actors/policy" + sealing "github.com/filecoin-project/lotus/extern/storage-sealing" + "github.com/filecoin-project/lotus/extern/storage-sealing/sealiface" "github.com/ipfs/go-cid" "github.com/stretchr/testify/assert" @@ -14,14 +19,31 @@ import ( commcid "github.com/filecoin-project/go-fil-commcid" "github.com/filecoin-project/go-state-types/abi" - - sealing "github.com/filecoin-project/lotus/extern/storage-sealing" ) type fakeChain struct { h abi.ChainEpoch } +func fakeConfigGetter(stub interface{}, t *testing.T) sealing.GetSealingConfigFunc { + return func() (sealiface.Config, error) { + if stub == nil { + return sealiface.Config{}, nil + } + + castStub, ok := stub.(struct { + CCSectorLifetime time.Duration + }) + if !ok { + t.Fatal("failed to cast fakeConfig") + } + + return sealiface.Config{ + CommittedCapacitySectorLifetime: castStub.CCSectorLifetime, + }, nil + } +} + func (f *fakeChain) StateNetworkVersion(ctx context.Context, tok sealing.TipSetToken) (network.Version, error) { return build.NewestNetworkVersion, nil } @@ -38,21 +60,29 @@ func fakePieceCid(t *testing.T) cid.Cid { } func TestBasicPolicyEmptySector(t *testing.T) { - policy := sealing.NewBasicPreCommitPolicy(&fakeChain{ - h: abi.ChainEpoch(55), - }, 10, 0) + cfg := fakeConfigGetter(nil, t) + h := abi.ChainEpoch(55) + pBoundary := abi.ChainEpoch(0) + pBuffer := abi.ChainEpoch(2) + pcp := sealing.NewBasicPreCommitPolicy(&fakeChain{h: h}, cfg, pBoundary, pBuffer) + exp, err := pcp.Expiration(context.Background()) - exp, err := policy.Expiration(context.Background()) require.NoError(t, err) - assert.Equal(t, 2879, int(exp)) + // as set when there are no deal pieces + expected := h + policy.GetMaxSectorExpirationExtension() - (pBuffer * 2) + // as set just before returning within Expiration() + expected += miner.WPoStProvingPeriod - (expected % miner.WPoStProvingPeriod) + pBoundary - 1 + assert.Equal(t, int(expected), int(exp)) } func TestBasicPolicyMostConstrictiveSchedule(t *testing.T) { + cfg := fakeConfigGetter(nil, t) + pPeriod := abi.ChainEpoch(11) policy := sealing.NewBasicPreCommitPolicy(&fakeChain{ h: abi.ChainEpoch(55), - }, 100, 11) - + }, cfg, pPeriod, 2) + longestDealEpochEnd := abi.ChainEpoch(100) pieces := []sealing.Piece{ { Piece: abi.PieceInfo{ @@ -76,7 +106,7 @@ func TestBasicPolicyMostConstrictiveSchedule(t *testing.T) { DealID: abi.DealID(43), DealSchedule: api.DealSchedule{ StartEpoch: abi.ChainEpoch(80), - EndEpoch: abi.ChainEpoch(100), + EndEpoch: longestDealEpochEnd, }, }, }, @@ -85,13 +115,15 @@ func TestBasicPolicyMostConstrictiveSchedule(t *testing.T) { exp, err := policy.Expiration(context.Background(), pieces...) require.NoError(t, err) - assert.Equal(t, 2890, int(exp)) + expected := longestDealEpochEnd + miner.WPoStProvingPeriod - (longestDealEpochEnd % miner.WPoStProvingPeriod) + pPeriod - 1 + assert.Equal(t, int(expected), int(exp)) } func TestBasicPolicyIgnoresExistingScheduleIfExpired(t *testing.T) { + cfg := fakeConfigGetter(nil, t) policy := sealing.NewBasicPreCommitPolicy(&fakeChain{ h: abi.ChainEpoch(55), - }, 100, 0) + }, cfg, 0, 0) pieces := []sealing.Piece{ { @@ -112,13 +144,14 @@ func TestBasicPolicyIgnoresExistingScheduleIfExpired(t *testing.T) { exp, err := policy.Expiration(context.Background(), pieces...) require.NoError(t, err) - assert.Equal(t, 2879, int(exp)) + assert.Equal(t, 1558079, int(exp)) } func TestMissingDealIsIgnored(t *testing.T) { + cfg := fakeConfigGetter(nil, t) policy := sealing.NewBasicPreCommitPolicy(&fakeChain{ h: abi.ChainEpoch(55), - }, 100, 11) + }, cfg, 11, 0) pieces := []sealing.Piece{ { @@ -146,5 +179,5 @@ func TestMissingDealIsIgnored(t *testing.T) { exp, err := policy.Expiration(context.Background(), pieces...) require.NoError(t, err) - assert.Equal(t, 2890, int(exp)) + assert.Equal(t, 1558090, int(exp)) } diff --git a/storage/miner.go b/storage/miner.go index c38671c8c..59c64eb41 100644 --- a/storage/miner.go +++ b/storage/miner.go @@ -180,9 +180,10 @@ func (m *Miner) Run(ctx context.Context) error { // Instantiate a precommit policy. cfg = sealing.GetSealingConfigFunc(m.getSealConfig) provingBoundary = md.PeriodStart % md.WPoStProvingPeriod + provingBuffer = md.WPoStProvingPeriod * 2 // TODO: Maybe we update this policy after actor upgrades? - pcp = sealing.NewBasicPreCommitPolicy(adaptedAPI, cfg, provingBoundary) + pcp = sealing.NewBasicPreCommitPolicy(adaptedAPI, cfg, provingBoundary, provingBuffer) // address selector. as = func(ctx context.Context, mi miner.MinerInfo, use api.AddrUse, goodFunds, minFunds abi.TokenAmount) (address.Address, abi.TokenAmount, error) { From 123a976f6247ec84e3bb3ab0ddaa9fe7d1a6532a Mon Sep 17 00:00:00 2001 From: Mike Greenberg Date: Thu, 22 Jul 2021 14:54:52 -0400 Subject: [PATCH 04/31] chore: Add unit test for custom CC lifetime value expiration --- .../storage-sealing/precommit_policy_test.go | 44 +++++++++++++------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/extern/storage-sealing/precommit_policy_test.go b/extern/storage-sealing/precommit_policy_test.go index aa56f1e6f..7c92cd061 100644 --- a/extern/storage-sealing/precommit_policy_test.go +++ b/extern/storage-sealing/precommit_policy_test.go @@ -8,6 +8,7 @@ import ( "github.com/filecoin-project/go-state-types/network" api "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/actors/policy" sealing "github.com/filecoin-project/lotus/extern/storage-sealing" @@ -25,21 +26,18 @@ type fakeChain struct { h abi.ChainEpoch } -func fakeConfigGetter(stub interface{}, t *testing.T) sealing.GetSealingConfigFunc { +type fakeConfigStub struct { + CCSectorLifetime time.Duration +} + +func fakeConfigGetter(stub *fakeConfigStub) sealing.GetSealingConfigFunc { return func() (sealiface.Config, error) { if stub == nil { return sealiface.Config{}, nil } - castStub, ok := stub.(struct { - CCSectorLifetime time.Duration - }) - if !ok { - t.Fatal("failed to cast fakeConfig") - } - return sealiface.Config{ - CommittedCapacitySectorLifetime: castStub.CCSectorLifetime, + CommittedCapacitySectorLifetime: stub.CCSectorLifetime, }, nil } } @@ -60,7 +58,7 @@ func fakePieceCid(t *testing.T) cid.Cid { } func TestBasicPolicyEmptySector(t *testing.T) { - cfg := fakeConfigGetter(nil, t) + cfg := fakeConfigGetter(nil) h := abi.ChainEpoch(55) pBoundary := abi.ChainEpoch(0) pBuffer := abi.ChainEpoch(2) @@ -76,8 +74,28 @@ func TestBasicPolicyEmptySector(t *testing.T) { assert.Equal(t, int(expected), int(exp)) } +func TestCustomCCSectorConfig(t *testing.T) { + customLifetime := 200 * 24 * time.Hour + customLifetimeEpochs := abi.ChainEpoch(int64(customLifetime.Truncate(builtin.EpochDurationSeconds).Seconds()) / builtin.EpochDurationSeconds) + cfgStub := fakeConfigStub{CCSectorLifetime: customLifetime} + cfg := fakeConfigGetter(&cfgStub) + h := abi.ChainEpoch(55) + pBoundary := abi.ChainEpoch(0) + pBuffer := abi.ChainEpoch(2) + pcp := sealing.NewBasicPreCommitPolicy(&fakeChain{h: h}, cfg, pBoundary, pBuffer) + exp, err := pcp.Expiration(context.Background()) + + require.NoError(t, err) + + // as set when there are no deal pieces + expected := h + customLifetimeEpochs - (pBuffer * 2) + // as set just before returning within Expiration() + expected += miner.WPoStProvingPeriod - (expected % miner.WPoStProvingPeriod) + pBoundary - 1 + assert.Equal(t, int(expected), int(exp)) +} + func TestBasicPolicyMostConstrictiveSchedule(t *testing.T) { - cfg := fakeConfigGetter(nil, t) + cfg := fakeConfigGetter(nil) pPeriod := abi.ChainEpoch(11) policy := sealing.NewBasicPreCommitPolicy(&fakeChain{ h: abi.ChainEpoch(55), @@ -120,7 +138,7 @@ func TestBasicPolicyMostConstrictiveSchedule(t *testing.T) { } func TestBasicPolicyIgnoresExistingScheduleIfExpired(t *testing.T) { - cfg := fakeConfigGetter(nil, t) + cfg := fakeConfigGetter(nil) policy := sealing.NewBasicPreCommitPolicy(&fakeChain{ h: abi.ChainEpoch(55), }, cfg, 0, 0) @@ -148,7 +166,7 @@ func TestBasicPolicyIgnoresExistingScheduleIfExpired(t *testing.T) { } func TestMissingDealIsIgnored(t *testing.T) { - cfg := fakeConfigGetter(nil, t) + cfg := fakeConfigGetter(nil) policy := sealing.NewBasicPreCommitPolicy(&fakeChain{ h: abi.ChainEpoch(55), }, cfg, 11, 0) From 3829d6bd9adb22e8ac42f15b7c54e4440308c793 Mon Sep 17 00:00:00 2001 From: Mike Greenberg Date: Thu, 22 Jul 2021 15:05:03 -0400 Subject: [PATCH 05/31] fix: Remove actor method from autogen source; Move inline to pkg --- chain/actors/policy/policy.go | 4 ---- extern/storage-sealing/precommit_policy.go | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/chain/actors/policy/policy.go b/chain/actors/policy/policy.go index 06db09ea8..c06c85d38 100644 --- a/chain/actors/policy/policy.go +++ b/chain/actors/policy/policy.go @@ -301,10 +301,6 @@ func GetWinningPoStSectorSetLookback(nwVer network.Version) abi.ChainEpoch { return ChainFinality } -func GetMinSectorExpiration() abi.ChainEpoch { - return miner5.MinSectorExpiration -} - func GetMaxSectorExpirationExtension() abi.ChainEpoch { return miner5.MaxSectorExpirationExtension } diff --git a/extern/storage-sealing/precommit_policy.go b/extern/storage-sealing/precommit_policy.go index 6d1fddc47..3856fd548 100644 --- a/extern/storage-sealing/precommit_policy.go +++ b/extern/storage-sealing/precommit_policy.go @@ -110,7 +110,7 @@ func (p *BasicPreCommitPolicy) getCCSectorLifetime() (abi.ChainEpoch, error) { ccLifetimeEpochs = policy.GetMaxSectorExpirationExtension() } - if minExpiration := policy.GetMinSectorExpiration(); ccLifetimeEpochs < minExpiration { + if minExpiration := abi.ChainEpoch(miner.MinSectorExpiration); ccLifetimeEpochs < minExpiration { log.Warnf("value for CommittedCapacitySectorLiftime is too short, using default minimum (%d epochs)", minExpiration) return minExpiration, nil } From 41bce7925a1852d2eba72b72357ec5d342402cb0 Mon Sep 17 00:00:00 2001 From: Mike Greenberg Date: Fri, 23 Jul 2021 20:12:30 -0400 Subject: [PATCH 06/31] fix: Remove unnecessary Truncate --- extern/storage-sealing/precommit_policy.go | 2 +- extern/storage-sealing/precommit_policy_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extern/storage-sealing/precommit_policy.go b/extern/storage-sealing/precommit_policy.go index 3856fd548..c911ccc8c 100644 --- a/extern/storage-sealing/precommit_policy.go +++ b/extern/storage-sealing/precommit_policy.go @@ -104,7 +104,7 @@ func (p *BasicPreCommitPolicy) getCCSectorLifetime() (abi.ChainEpoch, error) { return 0, xerrors.Errorf("sealing config load error: %w", err) } - var ccLifetimeEpochs = abi.ChainEpoch(uint64(c.CommittedCapacitySectorLifetime.Truncate(builtin.EpochDurationSeconds).Seconds()) / builtin.EpochDurationSeconds) + var ccLifetimeEpochs = abi.ChainEpoch(uint64(c.CommittedCapacitySectorLifetime.Seconds()) / builtin.EpochDurationSeconds) // if zero value in config, assume maximum sector extension if ccLifetimeEpochs == 0 { ccLifetimeEpochs = policy.GetMaxSectorExpirationExtension() diff --git a/extern/storage-sealing/precommit_policy_test.go b/extern/storage-sealing/precommit_policy_test.go index 7c92cd061..7f5aff0df 100644 --- a/extern/storage-sealing/precommit_policy_test.go +++ b/extern/storage-sealing/precommit_policy_test.go @@ -76,7 +76,7 @@ func TestBasicPolicyEmptySector(t *testing.T) { func TestCustomCCSectorConfig(t *testing.T) { customLifetime := 200 * 24 * time.Hour - customLifetimeEpochs := abi.ChainEpoch(int64(customLifetime.Truncate(builtin.EpochDurationSeconds).Seconds()) / builtin.EpochDurationSeconds) + customLifetimeEpochs := abi.ChainEpoch(int64(customLifetime.Seconds()) / builtin.EpochDurationSeconds) cfgStub := fakeConfigStub{CCSectorLifetime: customLifetime} cfg := fakeConfigGetter(&cfgStub) h := abi.ChainEpoch(55) From e68c59f984b67bacd234a3d27393c96894f7e8d8 Mon Sep 17 00:00:00 2001 From: Mike Greenberg Date: Fri, 23 Jul 2021 21:05:50 -0400 Subject: [PATCH 07/31] fix: Update node config docs --- node/config/doc_gen.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/node/config/doc_gen.go b/node/config/doc_gen.go index ea68dc344..5163ac409 100644 --- a/node/config/doc_gen.go +++ b/node/config/doc_gen.go @@ -562,6 +562,14 @@ Note that setting this number too high in relation to deal ingestion rate may re Comment: `Upper bound on how many sectors can be sealing at the same time when creating new sectors with deals (0 = unlimited)`, }, + { + Name: "CommittedCapacitySectorLifetime", + Type: "Duration", + + Comment: `CommittedCapacitySectorLifetime is the duration a Committed Capacity (CC) sector will +live before it must be extended or converted into sector containing deals before it is +terminated. Value must be between 180-540 days inclusive`, + }, { Name: "WaitDealsDelay", Type: "Duration", From ac8937245c41259af1caddc21aebcd08bbcca557 Mon Sep 17 00:00:00 2001 From: hunjixin <1084400399@qq.com> Date: Tue, 27 Jul 2021 14:51:45 +0800 Subject: [PATCH 08/31] fix ticket check --- extern/storage-sealing/checks.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/extern/storage-sealing/checks.go b/extern/storage-sealing/checks.go index 5ba23026d..115eedea5 100644 --- a/extern/storage-sealing/checks.go +++ b/extern/storage-sealing/checks.go @@ -93,27 +93,29 @@ func checkPrecommit(ctx context.Context, maddr address.Address, si SectorInfo, t return &ErrBadCommD{xerrors.Errorf("on chain CommD differs from sector: %s != %s", commD, si.CommD)} } - ticketEarliest := height - policy.MaxPreCommitRandomnessLookback - - if si.TicketEpoch < ticketEarliest { - return &ErrExpiredTicket{xerrors.Errorf("ticket expired: seal height: %d, head: %d", si.TicketEpoch+policy.SealRandomnessLookback, height)} - } - pci, err := api.StateSectorPreCommitInfo(ctx, maddr, si.SectorNumber, tok) if err != nil { if err == ErrSectorAllocated { + //committed P2 message but commit C2 message too late, pci should be null in this case return &ErrSectorNumberAllocated{err} } return &ErrApi{xerrors.Errorf("getting precommit info: %w", err)} } if pci != nil { + // committed P2 message if pci.Info.SealRandEpoch != si.TicketEpoch { return &ErrBadTicket{xerrors.Errorf("bad ticket epoch: %d != %d", pci.Info.SealRandEpoch, si.TicketEpoch)} } return &ErrPrecommitOnChain{xerrors.Errorf("precommit already on chain")} } + //never commit P2 message before, check ticket expiration + ticketEarliest := height - policy.MaxPreCommitRandomnessLookback + + if si.TicketEpoch < ticketEarliest { + return &ErrExpiredTicket{xerrors.Errorf("ticket expired: seal height: %d, head: %d", si.TicketEpoch+policy.SealRandomnessLookback, height)} + } return nil } From 01c786c141ffd61b274271e770ba464d4704eb80 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 27 Jul 2021 18:48:08 -0700 Subject: [PATCH 09/31] chore: fixup issue templates 1. Render logs/version as text, body as markdown. Previously, the _body_ was rendered as bash. 2. Fix spelling of "required". --- .github/ISSUE_TEMPLATE/bug_report.yml | 9 ++++----- .github/ISSUE_TEMPLATE/m1_bug_report_deal.yml | 8 ++++---- .github/ISSUE_TEMPLATE/m1_bug_report_non_deal.yml | 9 ++++----- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 7876715e2..244be5078 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -43,13 +43,14 @@ body: id: version attributes: label: Lotus Version + render: text description: Enter the output of `lotus version` and `lotus-miner version` if applicable. placeholder: | e.g. Daemon:1.11.0-rc2+debug+git.0519cd371.dirty+api1.3.0 Local: lotus version 1.11.0-rc2+debug+git.0519cd371.dirty validations: - reuiqred: true + required: true - type: textarea id: Description attributes: @@ -62,19 +63,18 @@ body: * For sealing issues, include the output of `lotus-miner sectors status --log ` for the failed sector(s). * For proving issues, include the output of `lotus-miner proving` info. * For deal making issues, include the output of `lotus client list-deals -v` and/or `lotus-miner storage-deals|retrieval-deals|data-transfers list [-v]` commands for the deal(s) in question. - render: bash validations: required: true - type: textarea id: extraInfo attributes: label: Logging Information + render: text description: | Please provide debug logs of the problem, remember you can get set log level control for: * lotus: use `lotus log list` to get all log systems available and set level by `lotus log set-level`. An example can be found [here](https://docs.filecoin.io/get-started/lotus/configuration-and-advanced-usage/#log-level-control). * lotus-miner:`lotus-miner log list` to get all log systems available and set level by `lotus-miner log set-level If you don't provide detailed logs when you raise the issue it will almost certainly be the first request I make before furthur diagnosing the problem. - render: bash validations: required: true - type: textarea @@ -87,6 +87,5 @@ body: 2. Do '...' 3. See error '...' ... - render: bash validations: - required: false \ No newline at end of file + required: false diff --git a/.github/ISSUE_TEMPLATE/m1_bug_report_deal.yml b/.github/ISSUE_TEMPLATE/m1_bug_report_deal.yml index 4402e97da..3a24d9564 100644 --- a/.github/ISSUE_TEMPLATE/m1_bug_report_deal.yml +++ b/.github/ISSUE_TEMPLATE/m1_bug_report_deal.yml @@ -35,10 +35,11 @@ body: - type: textarea id: version attributes: + render: text label: Lotus Tag and Version description: Enter the lotus tag, output of `lotus version` and `lotus-miner version`. validations: - reuiqred: true + required: true - type: textarea id: Description attributes: @@ -48,7 +49,6 @@ body: * What you were doding when you experienced the bug? * Any *error* messages you saw, *where* you saw them, and what you believe may have caused them (if you have any ideas). * What is the expected behaviour? - render: bash validations: required: true - type: textarea @@ -72,6 +72,7 @@ body: - type: textarea id: logging attributes: + render: text label: Logging Information description: Please link to the whole of the miner logs on your side of the transaction. You can upload the logs to a [gist](https://gist.github.com). validations: @@ -86,6 +87,5 @@ body: 2. Do '...' 3. See error '...' ... - render: bash validations: - required: false \ No newline at end of file + required: false diff --git a/.github/ISSUE_TEMPLATE/m1_bug_report_non_deal.yml b/.github/ISSUE_TEMPLATE/m1_bug_report_non_deal.yml index ede3593e5..363c3a1ab 100644 --- a/.github/ISSUE_TEMPLATE/m1_bug_report_non_deal.yml +++ b/.github/ISSUE_TEMPLATE/m1_bug_report_non_deal.yml @@ -36,10 +36,11 @@ body: - type: textarea id: version attributes: + render: text label: Lotus Tag and Version description: Enter the lotus tag, output of `lotus version` and `lotus-miner version`. validations: - reuiqred: true + required: true - type: textarea id: Description attributes: @@ -51,19 +52,18 @@ body: * What is the expected behaviour? * For sealing issues, include the output of `lotus-miner sectors status --log ` for the failed sector(s). * For proving issues, include the output of `lotus-miner proving` info. - render: bash validations: required: true - type: textarea id: extraInfo attributes: label: Logging Information + render: text description: | Please provide debug logs of the problem, remember you can get set log level control for: * lotus: use `lotus log list` to get all log systems available and set level by `lotus log set-level`. An example can be found [here](https://docs.filecoin.io/get-started/lotus/configuration-and-advanced-usage/#log-level-control). * lotus-miner:`lotus-miner log list` to get all log systems available and set level by `lotus-miner log set-level If you don't provide detailed logs when you raise the issue it will almost certainly be the first request I make before furthur diagnosing the problem. - render: bash validations: required: true - type: textarea @@ -76,6 +76,5 @@ body: 2. Do '...' 3. See error '...' ... - render: bash validations: - required: false \ No newline at end of file + required: false From e49c77e29c0448dca5b0d763909c70dc4a2d0471 Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Tue, 27 Jul 2021 22:54:23 -0400 Subject: [PATCH 10/31] bump master version to v1.11.2-dev --- build/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/version.go b/build/version.go index c6a1be3e2..4e155c98a 100644 --- a/build/version.go +++ b/build/version.go @@ -34,7 +34,7 @@ func buildType() string { } // BuildVersion is the local build version, set by build system -const BuildVersion = "1.11.1-dev" +const BuildVersion = "1.11.2-dev" func UserVersion() string { if os.Getenv("LOTUS_VERSION_IGNORE_COMMIT") == "1" { From f9595b58ee89567a6a9ed1728ae801a3d4f160b4 Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Tue, 27 Jul 2021 23:02:54 -0400 Subject: [PATCH 11/31] make gen --- build/openrpc/full.json.gz | Bin 25240 -> 25240 bytes build/openrpc/miner.json.gz | Bin 9479 -> 9479 bytes build/openrpc/worker.json.gz | Bin 2710 -> 2710 bytes documentation/en/cli-lotus-miner.md | 2 +- documentation/en/cli-lotus-worker.md | 2 +- documentation/en/cli-lotus.md | 2 +- 6 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index c940d921b0af50de4f243bc595a80f87b709ce86..4708ec159750bc2464b61b01bbcdfb1e270b2780 100644 GIT binary patch delta 25105 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 delta 25059 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�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 E0APQLuK)l5 diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index 41e51a13ec002908072c639cd2136778bea75f3e..58a76714dc5e83fc937928a04c9da6ad108678c7 100644 GIT binary patch delta 21 ccmZqoYWJGZ%J^YpTcrvIXKC Date: Wed, 28 Jul 2021 15:59:16 +0300 Subject: [PATCH 12/31] update go-libp2p-pubsub to v0.5.3 --- go.mod | 22 ++++----- go.sum | 146 ++++++++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 119 insertions(+), 49 deletions(-) diff --git a/go.mod b/go.mod index 5f968f6e0..080403c20 100644 --- a/go.mod +++ b/go.mod @@ -56,7 +56,7 @@ require ( github.com/gdamore/tcell/v2 v2.2.0 github.com/go-kit/kit v0.10.0 github.com/go-ole/go-ole v1.2.4 // indirect - github.com/golang/mock v1.5.0 + github.com/golang/mock v1.6.0 github.com/google/uuid v1.1.2 github.com/gorilla/mux v1.7.4 github.com/gorilla/websocket v1.4.2 @@ -105,17 +105,17 @@ require ( github.com/libp2p/go-eventbus v0.2.1 github.com/libp2p/go-libp2p v0.14.2 github.com/libp2p/go-libp2p-connmgr v0.2.4 - github.com/libp2p/go-libp2p-core v0.8.5 - github.com/libp2p/go-libp2p-discovery v0.5.0 + github.com/libp2p/go-libp2p-core v0.8.6 + github.com/libp2p/go-libp2p-discovery v0.5.1 github.com/libp2p/go-libp2p-kad-dht v0.11.0 github.com/libp2p/go-libp2p-mplex v0.4.1 github.com/libp2p/go-libp2p-noise v0.2.0 - github.com/libp2p/go-libp2p-peerstore v0.2.7 - github.com/libp2p/go-libp2p-pubsub v0.5.0 - github.com/libp2p/go-libp2p-quic-transport v0.10.0 + github.com/libp2p/go-libp2p-peerstore v0.2.8 + github.com/libp2p/go-libp2p-pubsub v0.5.3 + github.com/libp2p/go-libp2p-quic-transport v0.11.2 github.com/libp2p/go-libp2p-record v0.1.3 github.com/libp2p/go-libp2p-routing-helpers v0.2.3 - github.com/libp2p/go-libp2p-swarm v0.5.0 + github.com/libp2p/go-libp2p-swarm v0.5.3 github.com/libp2p/go-libp2p-tls v0.1.3 github.com/libp2p/go-libp2p-yamux v0.5.4 github.com/libp2p/go-maddr-filter v0.1.0 @@ -124,14 +124,14 @@ require ( github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 github.com/mitchellh/go-homedir v1.1.0 github.com/multiformats/go-base32 v0.0.3 - github.com/multiformats/go-multiaddr v0.3.1 + github.com/multiformats/go-multiaddr v0.3.3 github.com/multiformats/go-multiaddr-dns v0.3.1 github.com/multiformats/go-multibase v0.0.3 github.com/multiformats/go-multihash v0.0.15 github.com/open-rpc/meta-schema v0.0.0-20201029221707-1b72ef2ea333 github.com/opentracing/opentracing-go v1.2.0 github.com/polydawn/refmt v0.0.0-20190809202753-05966cbd336a - github.com/prometheus/client_golang v1.6.0 + github.com/prometheus/client_golang v1.10.0 github.com/raulk/clock v1.1.0 github.com/raulk/go-watchdog v1.0.1 github.com/streadway/quantile v0.0.0-20150917103942-b0c588724d25 @@ -150,9 +150,9 @@ require ( go.uber.org/fx v1.9.0 go.uber.org/multierr v1.6.0 go.uber.org/zap v1.16.0 - golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6 + golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20210510120138-977fb7262007 + golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744 golang.org/x/time v0.0.0-20191024005414-555d28b269f0 golang.org/x/tools v0.1.5 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 diff --git a/go.sum b/go.sum index b22f3dc15..69dabfd3b 100644 --- a/go.sum +++ b/go.sum @@ -27,8 +27,9 @@ dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1 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/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= @@ -199,8 +200,9 @@ github.com/detailyang/go-fallocate v0.0.0-20180908115635-432fa640bd2e/go.mod h1: 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 v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8= +github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE= 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= @@ -397,6 +399,8 @@ github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG 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/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/godbus/dbus v0.0.0-20190402143921-271e53dc4968 h1:s+PDl6lozQ+dEUtUtQnO7+A2iPG3sK1pI4liU+jxn90= github.com/godbus/dbus v0.0.0-20190402143921-271e53dc4968/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME= @@ -429,8 +433,8 @@ github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFU github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= 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= @@ -443,8 +447,10 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W 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/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 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= @@ -457,8 +463,10 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3 h1:x95R7cp+rSeeqAMI2knLtQ0DKlaBhv2NrtrOvafPHRo= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/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= @@ -609,8 +617,9 @@ github.com/ipfs/go-ds-badger v0.0.2/go.mod h1:Y3QpeSFWQf6MopLTiZD+VT6IC1yZqaGmjv 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-badger v0.2.7 h1:ju5REfIm+v+wgVnQ19xGLYPHYHbYLR6qJfmMbCDSK1I= +github.com/ipfs/go-ds-badger v0.2.7/go.mod h1:02rnztVKA4aZwDuaRPTf8mpqcKmXP7mLl6JPxd14JHA= 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= @@ -698,8 +707,9 @@ github.com/ipfs/go-log v1.0.0/go.mod h1:JO7RzlMK6rA+CIxFMLOuB6Wf5b81GDiKElL7UPSI 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 v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8= +github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo= 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= @@ -800,12 +810,14 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV 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/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/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 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= @@ -819,6 +831,8 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 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.11.7 h1:0hzRabrMN4tSTvMfnL3SCv1ZGeAP23ynzodBgaHeMeg= +github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/cpuid/v2 v2.0.4 h1:g0I61F2K2DjRHz1cnxlkNSBIaePVoJIjjnHui8QHbiw= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -838,8 +852,9 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY= 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-addr-util v0.1.0 h1:acKsntI33w2bTU7tC9a0SaPimJGfSI0bFKC18ChxeVI= +github.com/libp2p/go-addr-util v0.1.0/go.mod h1:6I3ZYuFr2O/9D+SoyM0zEw0EF3YkldtTX406BpdQMqw= 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= @@ -940,9 +955,9 @@ github.com/libp2p/go-libp2p-core v0.7.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJB github.com/libp2p/go-libp2p-core v0.8.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= github.com/libp2p/go-libp2p-core v0.8.1/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= github.com/libp2p/go-libp2p-core v0.8.2/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.8.3/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.8.5 h1:aEgbIcPGsKy6zYcC+5AJivYFedhYa4sW7mIpWpUaLKw= github.com/libp2p/go-libp2p-core v0.8.5/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= +github.com/libp2p/go-libp2p-core v0.8.6 h1:3S8g006qG6Tjpj1JdRK2S+TWc2DJQKX/RG9fdLeiLSU= +github.com/libp2p/go-libp2p-core v0.8.6/go.mod h1:dgHr0l0hIKfWpGpqAMbpo19pen9wJfdCGv51mTmdpmM= 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= @@ -954,8 +969,9 @@ github.com/libp2p/go-libp2p-discovery v0.1.0/go.mod h1:4F/x+aldVHjHDHuX85x1zWoFT 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-discovery v0.5.1 h1:CJylx+h2+4+s68GvrM4pGNyfNhOYviWBPtVv5PA7sfo= +github.com/libp2p/go-libp2p-discovery v0.5.1/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= @@ -1009,20 +1025,22 @@ github.com/libp2p/go-libp2p-peerstore v0.2.2/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRj 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/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= -github.com/libp2p/go-libp2p-peerstore v0.2.7 h1:83JoLxyR9OYTnNfB5vvFqvMUv/xDNa6NoPHnENhBsGw= github.com/libp2p/go-libp2p-peerstore v0.2.7/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= +github.com/libp2p/go-libp2p-peerstore v0.2.8 h1:nJghUlUkFVvyk7ccsM67oFA6kqUkwyCM1G4WPVMCWYA= +github.com/libp2p/go-libp2p-peerstore v0.2.8/go.mod h1:gGiPlXdz7mIHd2vfAsHzBNAMqSDkt2UBFwgcITgw1lA= 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.5.0 h1:OzcIuCWyJpOrWH0PTOfvxTzqFur4tiXpY5jXC8OxjyE= -github.com/libp2p/go-libp2p-pubsub v0.5.0/go.mod h1:MKnrsQkFgPcrQs1KVmOXy6Uz2RDQ1xO7dQo/P0Ba+ig= +github.com/libp2p/go-libp2p-pubsub v0.5.3 h1:XCn5xvgA/AKpbbaeqbomfKtQCbT9QsU39tYsVj0IndQ= +github.com/libp2p/go-libp2p-pubsub v0.5.3/go.mod h1:gVOzwebXVdSMDQBTfH8ACO5EJ4SQrvsHqCmYsCZpD0E= 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.10.0 h1:koDCbWD9CCHwcHZL3/WEvP2A+e/o5/W5L3QS/2SPMA0= github.com/libp2p/go-libp2p-quic-transport v0.10.0/go.mod h1:RfJbZ8IqXIhxBRm5hqUEJqjiiY8xmEuq3HUDS993MkA= +github.com/libp2p/go-libp2p-quic-transport v0.11.2 h1:p1YQDZRHH4Cv2LPtHubqlQ9ggz4CKng/REZuXZbZMhM= +github.com/libp2p/go-libp2p-quic-transport v0.11.2/go.mod h1:wlanzKtIh6pHrq+0U3p3DY9PJfGqxMgPaGKaK5LifwQ= 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= @@ -1050,9 +1068,9 @@ github.com/libp2p/go-libp2p-swarm v0.2.7/go.mod h1:ZSJ0Q+oq/B1JgfPHJAT2HTall+xYR 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/go.mod h1:hdv95GWCTmzkgeJpP+GK/9D9puJegb7H57B5hWQR5Kk= -github.com/libp2p/go-libp2p-swarm v0.4.3/go.mod h1:mmxP1pGBSc1Arw4F5DIjcpjFAmsRzA1KADuMtMuCT4g= -github.com/libp2p/go-libp2p-swarm v0.5.0 h1:HIK0z3Eqoo8ugmN8YqWAhD2RORgR+3iNXYG4U2PFd1E= github.com/libp2p/go-libp2p-swarm v0.5.0/go.mod h1:sU9i6BoHE0Ve5SKz3y9WfKrh8dUat6JknzUehFx8xW4= +github.com/libp2p/go-libp2p-swarm v0.5.3 h1:hsYaD/y6+kZff1o1Mc56NcuwSg80lIphTS/zDk3mO4M= +github.com/libp2p/go-libp2p-swarm v0.5.3/go.mod h1:NBn7eNW2lu568L7Ns9wdFrOhgRlkRnIDg0FLKbuu3i8= 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= @@ -1061,8 +1079,9 @@ github.com/libp2p/go-libp2p-testing v0.1.0/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eq 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/go.mod h1:Qy8sAncLKpwXtS2dSnDOP8ktexIAHKu+J+pnZOFZLTc= github.com/libp2p/go-libp2p-testing v0.3.0/go.mod h1:efZkql4UZ7OVsEfaxNHZPzIehtsBXMrXnCfJIgDti5g= -github.com/libp2p/go-libp2p-testing v0.4.0 h1:PrwHRi0IGqOwVQWR3xzgigSlhlLfxgfXgkHxr77EghQ= github.com/libp2p/go-libp2p-testing v0.4.0/go.mod h1:Q+PFXYoiYFN5CAEG2w3gLPEzotlKsNSbKQ/lImlOWF0= +github.com/libp2p/go-libp2p-testing v0.4.2 h1:IOiA5mMigi+eEjf4J+B7fepDhsjtsoWA9QbsCqbNp5U= +github.com/libp2p/go-libp2p-testing v0.4.2/go.mod h1:Q+PFXYoiYFN5CAEG2w3gLPEzotlKsNSbKQ/lImlOWF0= 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= @@ -1073,8 +1092,9 @@ github.com/libp2p/go-libp2p-transport-upgrader v0.0.4/go.mod h1:RGq+tupk+oj7PzL2 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/go.mod h1:i+SKzbRnvXdVbU3D1dwydnTmKRPXiAR/fyvi1dXuL4o= -github.com/libp2p/go-libp2p-transport-upgrader v0.4.2 h1:4JsnbfJzgZeRS9AWN7B9dPqn/LY/HoQTlO9gtdJTIYM= github.com/libp2p/go-libp2p-transport-upgrader v0.4.2/go.mod h1:NR8ne1VwfreD5VIWIU62Agt/J18ekORFU/j1i2y8zvk= +github.com/libp2p/go-libp2p-transport-upgrader v0.4.6 h1:SHt3g0FslnqIkEWF25YOB8UCOCTpGAVvHRWQYJ+veiI= +github.com/libp2p/go-libp2p-transport-upgrader v0.4.6/go.mod h1:JE0WQuQdy+uLZ5zOaI3Nw9dWGYJIA7mywEtP2lMvnyk= github.com/libp2p/go-libp2p-yamux v0.5.1 h1:sX4WQPHMhRxJE5UZTfjEuBvlQWXB5Bo3A2JK9ZJ9EM0= github.com/libp2p/go-libp2p-yamux v0.5.1/go.mod h1:dowuvDu8CRWmr0iqySMiSxK+W0iL5cMVO9S94Y6gkv4= github.com/libp2p/go-maddr-filter v0.0.1/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q= @@ -1103,6 +1123,7 @@ 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/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= +github.com/libp2p/go-netroute v0.1.5/go.mod h1:V1SR3AaECRkEQCoFFzYwVYWvYIEtlxx89+O3qcpCl4A= github.com/libp2p/go-netroute v0.1.6 h1:ruPJStbYyXVYGQ81uzEDzuvbYRLKRrLvTYd33yomC38= github.com/libp2p/go-netroute v0.1.6/go.mod h1:AqhkMh0VuWmfgtxKPp3Oc1LdU5QSWS7wl0QLhSZqXxQ= github.com/libp2p/go-openssl v0.0.2/go.mod h1:v8Zw2ijCSWBQi8Pq5GAixw6DbFfa9u6VIYDXnvOXkc0= @@ -1117,8 +1138,9 @@ github.com/libp2p/go-reuseport v0.0.2/go.mod h1:SPD+5RwGC7rcnzngoYC86GjPzjSywuQy 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-reuseport-transport v0.0.5 h1:lJzi+vSYbyJj2faPKLxNGWEIBcaV/uJmyvsUxXy2mLw= +github.com/libp2p/go-reuseport-transport v0.0.5/go.mod h1:TC62hhPc8qs5c/RoXDZG6YmjK+/YWUPC0yYmeUecbjc= github.com/libp2p/go-sockaddr v0.0.2/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= github.com/libp2p/go-sockaddr v0.1.0/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= github.com/libp2p/go-sockaddr v0.1.1 h1:yD80l2ZOdGksnOyHrhxDdTDFrf7Oy+v3FMVArIRgZxQ= @@ -1134,8 +1156,9 @@ github.com/libp2p/go-tcp-transport v0.0.4/go.mod h1:+E8HvC8ezEVOxIo3V5vCK9l1y/19 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-tcp-transport v0.2.7 h1:Z8Kc/Kb8tD84WiaH55xAlaEnkqzrp88jSEySCKV4+gg= +github.com/libp2p/go-tcp-transport v0.2.7/go.mod h1:lue9p1b3VmZj1MhhEGB/etmvF/nBQ0X9CW2DutBT3MM= 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= @@ -1156,8 +1179,9 @@ github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-b 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.19.3 h1:eCDQqvGBB+kCTkA0XrAFtNe81FMa0/fn4QSoeAbmiF4= github.com/lucas-clemente/quic-go v0.19.3/go.mod h1:ADXpNbTQjq1hIzCpB+y/k5iz4n4z4IwqoLb94Kh5Hu8= +github.com/lucas-clemente/quic-go v0.21.2 h1:8LqqL7nBQFDUINadW0fHV/xSaCQJgmJC0Gv+qUnjd78= +github.com/lucas-clemente/quic-go v0.21.2/go.mod h1:vF5M1XqhBAHgbjKcJOXY3JZz3GP0T3FQhz/uyOUS38Q= github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lufia/iostat v1.1.0/go.mod h1:rEPNA0xXgjHQjuI5Cy05sLlS2oRcSlWHRLrvh/AQ+Pg= @@ -1175,10 +1199,17 @@ github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGD github.com/marten-seemann/qpack v0.2.1/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.1 h1:LIH6K34bPVttyXnUWixk0bzH6/N07VxbSabxn5A5gZQ= github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= +github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= +github.com/marten-seemann/qtls-go1-15 v0.1.5 h1:Ci4EIUN6Rlb+D6GmLdej/bCQ4nPYNtVXQB+xjiXE1nk= +github.com/marten-seemann/qtls-go1-15 v0.1.5/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= +github.com/marten-seemann/qtls-go1-16 v0.1.4 h1:xbHbOGGhrenVtII6Co8akhLEdrawwB2iHl5yhJRpnco= +github.com/marten-seemann/qtls-go1-16 v0.1.4/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk= +github.com/marten-seemann/qtls-go1-17 v0.1.0-rc.1 h1:/rpmWuGvceLwwWuaKPdjpR4JJEUH0tq64/I3hvzaNLM= +github.com/marten-seemann/qtls-go1-17 v0.1.0-rc.1/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8= +github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= +github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= 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= @@ -1215,6 +1246,12 @@ github.com/miekg/dns v1.1.28/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7 github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= +github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s= 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= @@ -1256,8 +1293,9 @@ github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y9 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 v0.3.3 h1:vo2OTSAqnENB2rLk79pLtr+uhj+VAzSe3uef5q0lRSs= +github.com/multiformats/go-multiaddr v0.3.3/go.mod h1:lCKNGP1EQ1eZ35Za2wlqnabm9xQkib3fyB+nZXHLag0= 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= @@ -1305,6 +1343,7 @@ github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXS 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/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/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= @@ -1319,8 +1358,9 @@ github.com/nikkolasg/hexjson v0.0.0-20181101101858-78e39397e00c h1:5bFTChQxSKNwy github.com/nikkolasg/hexjson v0.0.0-20181101101858-78e39397e00c/go.mod h1:7qN3Y0BvzRUf4LofcoJplQL10lsFDb4PYlePTVwrP28= github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229 h1:E2B8qYyeSgv5MXpmzZXRNp8IAQ4vjxIjhpAf5hv/tAg= 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/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= 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= @@ -1330,16 +1370,19 @@ github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W 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/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= 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.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/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak= +github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/open-rpc/meta-schema v0.0.0-20201029221707-1b72ef2ea333 h1:CznVS40zms0Dj5he4ERo+fRPtO0qxUk8lA8Xu3ddet0= github.com/open-rpc/meta-schema v0.0.0-20201029221707-1b72ef2ea333/go.mod h1:Ag6rSXkHIckQmjFBCweJEEt1mrTPBv8b9W4aU/NQWfI= @@ -1390,8 +1433,11 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn 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 h1:YVPodQOcK15POxhgARIvnDRVpLcuK8mglnMrWfyrw6A= github.com/prometheus/client_golang v1.6.0/go.mod h1:ZLOG9ck3JLRdB5MgO8f+lLTe83AXG6ro35rLTxvnIl4= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU= +github.com/prometheus/client_golang v1.10.0 h1:/o0BDeWzLWXNZ+4q5gXltUvaMpJqckTa+jTNoB+z4cg= +github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= 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= @@ -1406,8 +1452,10 @@ github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 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/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/common v0.18.0 h1:WCVKW7aL6LEe1uryfI9dnEc2ZqNB1Fn0ok930v0iL1Y= +github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= 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= @@ -1418,8 +1466,11 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT 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 h1:jhMy6QXfi3y2HEzFoyuCj40z4OZIIHHPtFyCMftmvKA= github.com/prometheus/procfs v0.1.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 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/raulk/go-watchdog v1.0.1 h1:qgm3DIJAeb+2byneLrQJ7kvmDLGxN2vy3apXyGaDKN4= @@ -1661,8 +1712,9 @@ 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/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= +go.uber.org/goleak v1.1.10/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= @@ -1714,8 +1766,9 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf h1:B2n+Zi5QeYRDAEodEu72OS36gmTWjgpXr2+cWcBW90o= +golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= 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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1798,10 +1851,13 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201022231255-08b38378de70/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6 h1:0PC75Fz/kyMGhL0e1QnypqK2kQMqKt9csD1GnMJR+Zk= golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= 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= @@ -1819,6 +1875,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ 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/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180202135801-37707fdb30a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1884,17 +1941,25 @@ golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7w 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/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210317225723-c4fcb01b228e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744 h1:yhBbb4IRs2HS9PPlAg6DMC6mUOKexJBNsLf4Z+6En1Q= +golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M= @@ -1953,7 +2018,9 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY 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/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2035,8 +2102,10 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 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= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 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= @@ -2063,8 +2132,9 @@ 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.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 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= From de4a847078561d4bab53f15bc2474063a1a01140 Mon Sep 17 00:00:00 2001 From: Anton Evangelatov Date: Wed, 28 Jul 2021 12:10:17 +0300 Subject: [PATCH 13/31] add RuntimeSubsystems API method; use it in `lotus-miner info` --- api/api_storage.go | 2 + api/api_subsystems.go | 63 +++ api/docgen/docgen.go | 11 +- api/proxy_gen.go | 13 + build/openrpc/full.json.gz | Bin 25240 -> 25240 bytes build/openrpc/miner.json.gz | Bin 9479 -> 9540 bytes build/openrpc/worker.json.gz | Bin 2710 -> 2710 bytes cmd/lotus-miner/info.go | 562 ++++++++++++----------- documentation/en/api-v0-methods-miner.md | 14 + node/builder_miner.go | 2 + node/impl/storminer.go | 6 + node/modules/storageminer.go | 17 + 12 files changed, 410 insertions(+), 280 deletions(-) create mode 100644 api/api_subsystems.go diff --git a/api/api_storage.go b/api/api_storage.go index 154abcea7..d52032650 100644 --- a/api/api_storage.go +++ b/api/api_storage.go @@ -166,6 +166,8 @@ type StorageMiner interface { MarketPendingDeals(ctx context.Context) (PendingDealInfo, error) //perm:write MarketPublishPendingDeals(ctx context.Context) error //perm:admin + RuntimeSubsystems(ctx context.Context) (MinerSubsystems, error) //perm:read + DealsImportData(ctx context.Context, dealPropCid cid.Cid, file string) error //perm:admin DealsList(ctx context.Context) ([]MarketDeal, error) //perm:admin DealsConsiderOnlineStorageDeals(context.Context) (bool, error) //perm:admin diff --git a/api/api_subsystems.go b/api/api_subsystems.go new file mode 100644 index 000000000..1894bbdd8 --- /dev/null +++ b/api/api_subsystems.go @@ -0,0 +1,63 @@ +package api + +import ( + "bytes" + "encoding/json" +) + +type MinerSubsystems []MinerSubsystem + +func (ms MinerSubsystems) Has(entry MinerSubsystem) bool { + for _, v := range ms { + if v == entry { + return true + } + + } + return false +} + +type MinerSubsystem int + +const ( + MarketsSubsystem MinerSubsystem = iota + MiningSubsystem + SealingSubsystem + SectorStorageSubsystem +) + +func (ms MinerSubsystem) String() string { + return MinerSubsystemToString[ms] +} + +var MinerSubsystemToString = map[MinerSubsystem]string{ + MarketsSubsystem: "Markets", + MiningSubsystem: "Mining", + SealingSubsystem: "Sealing", + SectorStorageSubsystem: "SectorStorage", +} + +var MinerSubsystemToID = map[string]MinerSubsystem{ + "Markets": MarketsSubsystem, + "Mining": MiningSubsystem, + "Sealing": SealingSubsystem, + "SectorStorage": SectorStorageSubsystem, +} + +func (ms MinerSubsystem) MarshalJSON() ([]byte, error) { + buffer := bytes.NewBufferString(`"`) + buffer.WriteString(MinerSubsystemToString[ms]) + buffer.WriteString(`"`) + return buffer.Bytes(), nil +} + +func (ms *MinerSubsystem) UnmarshalJSON(b []byte) error { + var j string + err := json.Unmarshal(b, &j) + if err != nil { + return err + } + // TODO: handle zero value + *ms = MinerSubsystemToID[j] + return nil +} diff --git a/api/docgen/docgen.go b/api/docgen/docgen.go index 39980023f..1e712a0ae 100644 --- a/api/docgen/docgen.go +++ b/api/docgen/docgen.go @@ -46,11 +46,12 @@ import ( ) var ExampleValues = map[reflect.Type]interface{}{ - reflect.TypeOf(auth.Permission("")): auth.Permission("write"), - reflect.TypeOf(""): "string value", - reflect.TypeOf(uint64(42)): uint64(42), - reflect.TypeOf(byte(7)): byte(7), - reflect.TypeOf([]byte{}): []byte("byte array"), + reflect.TypeOf(api.MinerSubsystem(0)): api.MinerSubsystem(1), + reflect.TypeOf(auth.Permission("")): auth.Permission("write"), + reflect.TypeOf(""): "string value", + reflect.TypeOf(uint64(42)): uint64(42), + reflect.TypeOf(byte(7)): byte(7), + reflect.TypeOf([]byte{}): []byte("byte array"), } func addExample(v interface{}) { diff --git a/api/proxy_gen.go b/api/proxy_gen.go index 7d96425ff..a4feb7be1 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -699,6 +699,8 @@ type StorageMinerStruct struct { ReturnUnsealPiece func(p0 context.Context, p1 storiface.CallID, p2 *storiface.CallError) error `perm:"admin"` + RuntimeSubsystems func(p0 context.Context) (MinerSubsystems, error) `perm:"read"` + SealingAbort func(p0 context.Context, p1 storiface.CallID) error `perm:"admin"` SealingSchedDiag func(p0 context.Context, p1 bool) (interface{}, error) `perm:"admin"` @@ -4095,6 +4097,17 @@ func (s *StorageMinerStub) ReturnUnsealPiece(p0 context.Context, p1 storiface.Ca return ErrNotSupported } +func (s *StorageMinerStruct) RuntimeSubsystems(p0 context.Context) (MinerSubsystems, error) { + if s.Internal.RuntimeSubsystems == nil { + return *new(MinerSubsystems), ErrNotSupported + } + return s.Internal.RuntimeSubsystems(p0) +} + +func (s *StorageMinerStub) RuntimeSubsystems(p0 context.Context) (MinerSubsystems, error) { + return *new(MinerSubsystems), ErrNotSupported +} + func (s *StorageMinerStruct) SealingAbort(p0 context.Context, p1 storiface.CallID) error { if s.Internal.SealingAbort == nil { return ErrNotSupported diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 4708ec159750bc2464b61b01bbcdfb1e270b2780..db41fb2d3a18922fe3b5b9960853ec350fb7ee91 100644 GIT binary patch delta 20847 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 KonJWA>jVIUfNw_t delta 20863 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 acE!D4?FyCb=l=@;0RR88YkDKp>jVICf>(b4 diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index 58a76714dc5e83fc937928a04c9da6ad108678c7..69d471dbd5673f06dc006a4d21f0a193b36bbb30 100644 GIT binary patch delta 6074 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 A7ytkO delta 6021 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 diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index fbf25f4ea060a5e70be819d3d1e2acb5328a62e6..07740e5e2ccfab4bbc3b6ce6333159c762d09d90 100644 GIT binary patch delta 1731 zcmV;!20Zze6_ypS`vL=W&a{*M0zZH5hhkOw6_h>%RE;!iq`7ZM^SD(44=5up{SNV57~+Hsv1xz$YgT?u z*vL5SXL{aR{9lxB1_WvVuoFhQ3jkn2kJn3B84T_3!=VIKZEVBr(wYOaX8Nm+optQo zx7c~oE{>g-Y{(5GrY0%Q&n!L+(KVMvKz+Yb+)J7>ihVJsTS?y(F}K7iWGYp04FBR% z&AZaTA3YK`(7yR{#JGTKkUW17_ALEu;Z(s3{tVvLcp|AyT-@(1HAoPT zT1R!k`PEk${qOem8)%5~Q~iH$3*u)*tnvL$ z0f}9}_irb`S3~a_dhc6IJL?w5v@4><(lDBZG{xXMTEv5F#Gyb}T^5+arj~@HqV|l! z(u~)w!0#gNO7a@HO506toD+5fFYNrEmtK{hbI+qHuRY5xEJbMsN+YEt z?sd3+Vj$)GXk2JB#8&hXay5GK!MgBxP4p1eKVInKoI27Ff7QoGp)Z(6FEk+G= zcOs(h0?^G{#~vP)E4A}CEo*+G^Mc3P`l==D={(Mu#)A?%WTw!i15D8&O>1(JF#SJ& ztYK0kcf7)BX}@04Yt7Rv{TJNS4wDQgoxyiv1L;0VXM8g0lJ1Ebbmaqh{$Kq0Z$FrD z&;M)S4Pi3u_J*_@OiqFTdY#8!vD+8S)5Bmwxk->!FG%YJ>08Fk-I7C` z37*S`@2Z%&W4`Z3m&P#i_du-;b35CAhq;|Phr4UGxeL%Xuf%T;?$vRwj&u7K=Xxb4 z%(X+m60m-HC?VcRhG=0mwMGO?7}{nK%!r*%_tdd)C+v3@V&UGWdFs?hjUs9k@s3c$ zNy$Ot*!K{_RxQ#53q19z(RvSq50-|7Vou6?U>hA;7}R9Rj}SI|9*DMpOqYH zz5%t~OEMF}GMG5v++DKUFKOVlJN`1PEZOy!bm$TBAdwow%qoaP5Z!CetfPUxaD~*E z=pQPP1f?h&-7=3%kRlI&2pYnF6Ov$a=18PC((av%hys0~h4%a-lkxV^kB$?biZeBwEOq%JOHejiWUTs7OE(y*6w$q-JOziJF_`4 zeVxOcu6POT5&v6$;_qZg{W4v)FrK%(WNEyjd7}k$T9RTznAJc>w?V7b1r)uw;MEJ^ z6;kA8WOe;23AdSDX)T?9nO6TL-16!Zr3d9IlW%JLv@$`p->IXsZppEnX#4Iu->gbe zuKQ}Q*@tZYIZ<%Q9C%*w>8ir*8pp_{PUD<9hEH5l{BP@VOLmCr^EmZ$mrAb*%bCR% zzwgv*b9(LkX^#nR^G2yHdIOZmZE64-R*o>JC5$CLLQfFJO7hV_3wa! zGE~VqU7=EzCcoxnug!g>`!80%q^p6;?FWehrl^b57z7|90I}#PokSvFEPS^2N{xMY Z$*9v7o=)$k{|f*B|NqnoW^P$}000n$fOY@? delta 1731 zcmV;!20Zze6_ypS`vL>BkF}Hj0zZGwNwF&Z3d$ZEDpE*ehGr8-8sz#bM(%d(s**z_R2b2+)eusE23~@q+*ff9rH7ma+ zY-AkvGd*uD{x8Zm0|GSw*a;)u1pu(1$Ll4m42Jgi;ZTCAHnw4QY0ZIIGyPS^&N_DP zTkPze701p?Hspp8Q?m|Nl$GL@=0hJSIX z=3Qyvj~>uZG?=^xn6acG@bAX;(yzrC~G+X^O#jw1@}Uh(m#{x-2k-O)UvYMeP}d zr5UeVf!{^kmE<*YmA0GQI4A4|Vziipxca7MsrYW>6>^yL6Ss*Q@?LcJPoSf5rDVzm zQzW};YiI2eTNB&{DQ}ZG1!gygnSJOKo7pwEKqP!+5{UWUp9Sh4`7CCk>us&n@)Fjo zwVlGAyRcfTpT^Wn!g@)#&r8D7R=1Pa1yz4)_lk{6ugcH4=TVi{p5+#nqBH}gkx~-( zI$S?7kaB)BF0>h9EBXkz8ol^nU3k1EdWh;DFLZHE9cc)1Q(|lj;ATf^anG?9qXxP= z5m9#m=;p0s50A=~+WDK7HNVk$!DDTG)e`n}9%oGBK?xl)Q|QtGrs$BSH91L`{vUtV zFsYF{Ug5N~U$5x3=4qDx3vOzMNrsco;5)H_bf2U%KACh$_e2f4@&P>mFaG?uA56IC z|26Q2Fd24xL)r}{CqV$c&SNiH18lNALsyw}ZZ@CkVKAZGB*>~4r1gUIEo0_R$sx`J z&*j5+Rm|Km-*=-+V;K2+pw@=Do$Y_a+)kat-8I|X1!$XB;Nr=&xqXXs-I5dL z+M!R7lF_PYzQaPQMRb?T!=5jBc&D~zPX;od#zGC=Cu_#REBRAtar=5o89uKCnblP zZ$Pd0lFWp#3?>dZcbDw;OB#6Xj=u~mOLqMw9ePAONTkLvvkD>+ME9CA>u8`aTp=|k z`iDv+K`F{cx6C6Gq{ssxf`)(age2IUIT9(3w0kEbqCj6LA|H@ecPDT+xX19vyq&Y& z#P_LpdRB61GTuJ=(Q(34ai)for7mA&3Cc#x>?hmKtBokfR=Fi@Y(*62gsQzDG5!9L zO}>!u-D&lQlH-Yg5czNs`94F9PEnR>GZ0rhL>YDyy~3hYvs3N8R;z!rtZiSTb7EG1 z{b%#B)wIK29W}QJ^`0|UYxld*?smz!o!Ok2 zzRqDzSG)xFi2p4=@pm$$ewi*?7|&Z?vNT@NywQR=ElIH<%xa*c+o09z0*YQ-@al!| z3Mq0kvbuhigxk!nw3dI)OsoGAZh7^I(t~o9$u~8ATA85Q@6^#*r{q{pw0(D-Z&sx! z*L}6u>_ayHoG7?t4m_{;bXDPYjbmg}r*Tdl!zV5&{ z-*@V@x@VjC#M9n>scO&m&*Trl#NKoD%oJ{N$+mS)9VB%35}1F|qDXLuM^T*tWuquc zFolTY2#^vSMCYwF(52D?Fyz7m2Ms2Op#MbQV6+fH=iC7s9XySxJw}u{REwzl@B&v+ zTi-qMiTwc1ycZR&MhJ5dL7#Q4F_Gg&R&h3S*rxtaME#Vqeo7fZ&2GtQme`gJVP;HK zyhNM3hjN$~S6Ooc@!}dr$S&b(EAQ7U8>*yyOHs7j?sh(`9Y=RmA$uj;&i^D-{X3wb z3{`SYSE!Vw$*(!tYja=e{)^Qw>1yC|`$3|BDe59M1_6i&KrDJnCy@vk3!m-1Qe)p; ZGV1sgPp5a&{{;X5|Nl>=ppaR5000ghVSxYu diff --git a/cmd/lotus-miner/info.go b/cmd/lotus-miner/info.go index 3941ce563..4c409788f 100644 --- a/cmd/lotus-miner/info.go +++ b/cmd/lotus-miner/info.go @@ -55,7 +55,7 @@ func infoCmdAct(cctx *cli.Context) error { } defer closer() - api, acloser, err := lcli.GetFullNodeAPI(cctx) + fullapi, acloser, err := lcli.GetFullNodeAPI(cctx) if err != nil { return err } @@ -63,9 +63,16 @@ func infoCmdAct(cctx *cli.Context) error { ctx := lcli.ReqContext(cctx) + subsystems, err := nodeApi.RuntimeSubsystems(ctx) + if err != nil { + return err + } + + fmt.Println("Enabled subsystems:", subsystems) + fmt.Print("Chain: ") - head, err := api.ChainHead(ctx) + head, err := fullapi.ChainHead(ctx) if err != nil { return err } @@ -95,284 +102,289 @@ func infoCmdAct(cctx *cli.Context) error { fmt.Println() - maddr, err := getActorAddress(ctx, cctx) - if err != nil { - return err - } - - mact, err := api.StateGetActor(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - tbs := blockstore.NewTieredBstore(blockstore.NewAPIBlockstore(api), blockstore.NewMemory()) - mas, err := miner.Load(adt.WrapStore(ctx, cbor.NewCborStore(tbs)), mact) - if err != nil { - return err - } - - // Sector size - mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - 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 { - return err - } - - fmt.Printf("Power: %s / %s (%0.4f%%)\n", - color.GreenString(types.DeciStr(pow.MinerPower.QualityAdjPower)), - types.DeciStr(pow.TotalPower.QualityAdjPower), - types.BigDivFloat( - types.BigMul(pow.MinerPower.QualityAdjPower, big.NewInt(100)), - pow.TotalPower.QualityAdjPower, - ), - ) - - fmt.Printf("\tRaw: %s / %s (%0.4f%%)\n", - color.BlueString(types.SizeStr(pow.MinerPower.RawBytePower)), - types.SizeStr(pow.TotalPower.RawBytePower), - types.BigDivFloat( - types.BigMul(pow.MinerPower.RawBytePower, big.NewInt(100)), - pow.TotalPower.RawBytePower, - ), - ) - secCounts, err := api.StateMinerSectorCount(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - proving := secCounts.Active + secCounts.Faulty - nfaults := secCounts.Faulty - fmt.Printf("\tCommitted: %s\n", types.SizeStr(types.BigMul(types.NewInt(secCounts.Live), types.NewInt(uint64(mi.SectorSize))))) - if nfaults == 0 { - fmt.Printf("\tProving: %s\n", types.SizeStr(types.BigMul(types.NewInt(proving), types.NewInt(uint64(mi.SectorSize))))) - } else { - var faultyPercentage float64 - if secCounts.Live != 0 { - faultyPercentage = float64(100*nfaults) / float64(secCounts.Live) - } - fmt.Printf("\tProving: %s (%s Faulty, %.2f%%)\n", - types.SizeStr(types.BigMul(types.NewInt(proving), types.NewInt(uint64(mi.SectorSize)))), - types.SizeStr(types.BigMul(types.NewInt(nfaults), types.NewInt(uint64(mi.SectorSize)))), - faultyPercentage) - } - - if !pow.HasMinPower { - fmt.Print("Below minimum power threshold, no blocks will be won") - } else { - - winRatio := new(corebig.Rat).SetFrac( - types.BigMul(pow.MinerPower.QualityAdjPower, types.NewInt(build.BlocksPerEpoch)).Int, - pow.TotalPower.QualityAdjPower.Int, - ) - - if winRatioFloat, _ := winRatio.Float64(); winRatioFloat > 0 { - - // if the corresponding poisson distribution isn't infinitely small then - // throw it into the mix as well, accounting for multi-wins - winRationWithPoissonFloat := -math.Expm1(-winRatioFloat) - winRationWithPoisson := new(corebig.Rat).SetFloat64(winRationWithPoissonFloat) - if winRationWithPoisson != nil { - winRatio = winRationWithPoisson - winRatioFloat = winRationWithPoissonFloat - } - - weekly, _ := new(corebig.Rat).Mul( - winRatio, - new(corebig.Rat).SetInt64(7*builtin.EpochsInDay), - ).Float64() - - avgDuration, _ := new(corebig.Rat).Mul( - new(corebig.Rat).SetInt64(builtin.EpochDurationSeconds), - new(corebig.Rat).Inv(winRatio), - ).Float64() - - fmt.Print("Projected average block win rate: ") - color.Blue( - "%.02f/week (every %s)", - weekly, - (time.Second * time.Duration(avgDuration)).Truncate(time.Second).String(), - ) - - // Geometric distribution of P(Y < k) calculated as described in https://en.wikipedia.org/wiki/Geometric_distribution#Probability_Outcomes_Examples - // https://www.wolframalpha.com/input/?i=t+%3E+0%3B+p+%3E+0%3B+p+%3C+1%3B+c+%3E+0%3B+c+%3C1%3B+1-%281-p%29%5E%28t%29%3Dc%3B+solve+t - // t == how many dice-rolls (epochs) before win - // p == winRate == ( minerPower / netPower ) - // c == target probability of win ( 99.9% in this case ) - fmt.Print("Projected block win with ") - color.Green( - "99.9%% probability every %s", - (time.Second * time.Duration( - builtin.EpochDurationSeconds*math.Log(1-0.999)/ - math.Log(1-winRatioFloat), - )).Truncate(time.Second).String(), - ) - fmt.Println("(projections DO NOT account for future network and miner growth)") - } - } - - fmt.Println() - - deals, err := nodeApi.MarketListIncompleteDeals(ctx) - if err != nil { - return err - } - - type dealStat struct { - count, verifCount int - bytes, verifBytes uint64 - } - dsAdd := func(ds *dealStat, deal storagemarket.MinerDeal) { - ds.count++ - ds.bytes += uint64(deal.Proposal.PieceSize) - if deal.Proposal.VerifiedDeal { - ds.verifCount++ - ds.verifBytes += uint64(deal.Proposal.PieceSize) - } - } - - showDealStates := map[storagemarket.StorageDealStatus]struct{}{ - storagemarket.StorageDealActive: {}, - storagemarket.StorageDealTransferring: {}, - storagemarket.StorageDealStaged: {}, - storagemarket.StorageDealAwaitingPreCommit: {}, - storagemarket.StorageDealSealing: {}, - storagemarket.StorageDealPublish: {}, - storagemarket.StorageDealCheckForAcceptance: {}, - storagemarket.StorageDealPublishing: {}, - } - - var total dealStat - perState := map[storagemarket.StorageDealStatus]*dealStat{} - for _, deal := range deals { - if _, ok := showDealStates[deal.State]; !ok { - continue - } - if perState[deal.State] == nil { - perState[deal.State] = new(dealStat) - } - - dsAdd(&total, deal) - dsAdd(perState[deal.State], deal) - } - - type wstr struct { - str string - status storagemarket.StorageDealStatus - } - sorted := make([]wstr, 0, len(perState)) - for status, stat := range perState { - st := strings.TrimPrefix(storagemarket.DealStates[status], "StorageDeal") - sorted = append(sorted, wstr{ - str: fmt.Sprintf(" %s:\t%d\t\t%s\t(Verified: %d\t%s)\n", st, stat.count, types.SizeStr(types.NewInt(stat.bytes)), stat.verifCount, types.SizeStr(types.NewInt(stat.verifBytes))), - status: status, - }, - ) - } - sort.Slice(sorted, func(i, j int) bool { - if sorted[i].status == storagemarket.StorageDealActive || sorted[j].status == storagemarket.StorageDealActive { - return sorted[i].status == storagemarket.StorageDealActive - } - return sorted[i].status > sorted[j].status - }) - - fmt.Printf("Storage Deals: %d, %s\n", total.count, types.SizeStr(types.NewInt(total.bytes))) - - tw := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0) - for _, e := range sorted { - _, _ = tw.Write([]byte(e.str)) - } - - _ = tw.Flush() - fmt.Println() - - retrievals, err := nodeApi.MarketListRetrievalDeals(ctx) - if err != nil { - return xerrors.Errorf("getting retrieval deal list: %w", err) - } - - var retrComplete dealStat - for _, retrieval := range retrievals { - if retrieval.Status == retrievalmarket.DealStatusCompleted { - retrComplete.count++ - retrComplete.bytes += retrieval.TotalSent - } - } - - fmt.Printf("Retrieval Deals (complete): %d, %s\n", retrComplete.count, types.SizeStr(types.NewInt(retrComplete.bytes))) - - 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() - if err != nil { - return xerrors.Errorf("getting locked funds: %w", err) - } - availBalance, err := mas.AvailableBalance(mact.Balance) - if err != nil { - return xerrors.Errorf("getting available balance: %w", err) - } - 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()) - colorTokenAmount(" Available: %s\n", availBalance) - - mb, err := api.StateMarketBalance(ctx, maddr, types.EmptyTSK) - if err != nil { - return xerrors.Errorf("getting market balance: %w", err) - } - 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()) - colorTokenAmount(" Available: %s\n", big.Sub(mb.Escrow, mb.Locked)) - - 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()) - } - colorTokenAmount("Total Spendable: %s\n", spendable) - - fmt.Println() - - if !cctx.Bool("hide-sectors-info") { - fmt.Println("Sectors:") - err = sectorsInfo(ctx, nodeApi) + if subsystems.Has(api.SectorStorageSubsystem) { + maddr, err := getActorAddress(ctx, cctx) if err != nil { return err } + + mact, err := fullapi.StateGetActor(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + tbs := blockstore.NewTieredBstore(blockstore.NewAPIBlockstore(fullapi), blockstore.NewMemory()) + mas, err := miner.Load(adt.WrapStore(ctx, cbor.NewCborStore(tbs)), mact) + if err != nil { + return err + } + + // Sector size + mi, err := fullapi.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + ssize := types.SizeStr(types.NewInt(uint64(mi.SectorSize))) + fmt.Printf("Miner: %s (%s sectors)\n", color.BlueString("%s", maddr), ssize) + + pow, err := fullapi.StateMinerPower(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + fmt.Printf("Power: %s / %s (%0.4f%%)\n", + color.GreenString(types.DeciStr(pow.MinerPower.QualityAdjPower)), + types.DeciStr(pow.TotalPower.QualityAdjPower), + types.BigDivFloat( + types.BigMul(pow.MinerPower.QualityAdjPower, big.NewInt(100)), + pow.TotalPower.QualityAdjPower, + ), + ) + + fmt.Printf("\tRaw: %s / %s (%0.4f%%)\n", + color.BlueString(types.SizeStr(pow.MinerPower.RawBytePower)), + types.SizeStr(pow.TotalPower.RawBytePower), + types.BigDivFloat( + types.BigMul(pow.MinerPower.RawBytePower, big.NewInt(100)), + pow.TotalPower.RawBytePower, + ), + ) + secCounts, err := fullapi.StateMinerSectorCount(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + proving := secCounts.Active + secCounts.Faulty + nfaults := secCounts.Faulty + fmt.Printf("\tCommitted: %s\n", types.SizeStr(types.BigMul(types.NewInt(secCounts.Live), types.NewInt(uint64(mi.SectorSize))))) + if nfaults == 0 { + fmt.Printf("\tProving: %s\n", types.SizeStr(types.BigMul(types.NewInt(proving), types.NewInt(uint64(mi.SectorSize))))) + } else { + var faultyPercentage float64 + if secCounts.Live != 0 { + faultyPercentage = float64(100*nfaults) / float64(secCounts.Live) + } + fmt.Printf("\tProving: %s (%s Faulty, %.2f%%)\n", + types.SizeStr(types.BigMul(types.NewInt(proving), types.NewInt(uint64(mi.SectorSize)))), + types.SizeStr(types.BigMul(types.NewInt(nfaults), types.NewInt(uint64(mi.SectorSize)))), + faultyPercentage) + } + + if !pow.HasMinPower { + fmt.Print("Below minimum power threshold, no blocks will be won") + } else { + + winRatio := new(corebig.Rat).SetFrac( + types.BigMul(pow.MinerPower.QualityAdjPower, types.NewInt(build.BlocksPerEpoch)).Int, + pow.TotalPower.QualityAdjPower.Int, + ) + + if winRatioFloat, _ := winRatio.Float64(); winRatioFloat > 0 { + + // if the corresponding poisson distribution isn't infinitely small then + // throw it into the mix as well, accounting for multi-wins + winRationWithPoissonFloat := -math.Expm1(-winRatioFloat) + winRationWithPoisson := new(corebig.Rat).SetFloat64(winRationWithPoissonFloat) + if winRationWithPoisson != nil { + winRatio = winRationWithPoisson + winRatioFloat = winRationWithPoissonFloat + } + + weekly, _ := new(corebig.Rat).Mul( + winRatio, + new(corebig.Rat).SetInt64(7*builtin.EpochsInDay), + ).Float64() + + avgDuration, _ := new(corebig.Rat).Mul( + new(corebig.Rat).SetInt64(builtin.EpochDurationSeconds), + new(corebig.Rat).Inv(winRatio), + ).Float64() + + fmt.Print("Projected average block win rate: ") + color.Blue( + "%.02f/week (every %s)", + weekly, + (time.Second * time.Duration(avgDuration)).Truncate(time.Second).String(), + ) + + // Geometric distribution of P(Y < k) calculated as described in https://en.wikipedia.org/wiki/Geometric_distribution#Probability_Outcomes_Examples + // https://www.wolframalpha.com/input/?i=t+%3E+0%3B+p+%3E+0%3B+p+%3C+1%3B+c+%3E+0%3B+c+%3C1%3B+1-%281-p%29%5E%28t%29%3Dc%3B+solve+t + // t == how many dice-rolls (epochs) before win + // p == winRate == ( minerPower / netPower ) + // c == target probability of win ( 99.9% in this case ) + fmt.Print("Projected block win with ") + color.Green( + "99.9%% probability every %s", + (time.Second * time.Duration( + builtin.EpochDurationSeconds*math.Log(1-0.999)/ + math.Log(1-winRatioFloat), + )).Truncate(time.Second).String(), + ) + fmt.Println("(projections DO NOT account for future network and miner growth)") + } + } + + 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() + if err != nil { + return xerrors.Errorf("getting locked funds: %w", err) + } + availBalance, err := mas.AvailableBalance(mact.Balance) + if err != nil { + return xerrors.Errorf("getting available balance: %w", err) + } + 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()) + colorTokenAmount(" Available: %s\n", availBalance) + + mb, err := fullapi.StateMarketBalance(ctx, maddr, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting market balance: %w", err) + } + 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()) + colorTokenAmount(" Available: %s\n", big.Sub(mb.Escrow, mb.Locked)) + + wb, err := fullapi.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 := fullapi.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()) + } + colorTokenAmount("Total Spendable: %s\n", spendable) + + fmt.Println() + + if !cctx.Bool("hide-sectors-info") { + fmt.Println("Sectors:") + err = sectorsInfo(ctx, nodeApi) + if err != nil { + return err + } + } + + // TODO: grab actr state / info + // * Sealed sectors (count / bytes) + // * Power + } + + if subsystems.Has(api.MarketsSubsystem) { + deals, err := nodeApi.MarketListIncompleteDeals(ctx) + if err != nil { + return err + } + + type dealStat struct { + count, verifCount int + bytes, verifBytes uint64 + } + dsAdd := func(ds *dealStat, deal storagemarket.MinerDeal) { + ds.count++ + ds.bytes += uint64(deal.Proposal.PieceSize) + if deal.Proposal.VerifiedDeal { + ds.verifCount++ + ds.verifBytes += uint64(deal.Proposal.PieceSize) + } + } + + showDealStates := map[storagemarket.StorageDealStatus]struct{}{ + storagemarket.StorageDealActive: {}, + storagemarket.StorageDealTransferring: {}, + storagemarket.StorageDealStaged: {}, + storagemarket.StorageDealAwaitingPreCommit: {}, + storagemarket.StorageDealSealing: {}, + storagemarket.StorageDealPublish: {}, + storagemarket.StorageDealCheckForAcceptance: {}, + storagemarket.StorageDealPublishing: {}, + } + + var total dealStat + perState := map[storagemarket.StorageDealStatus]*dealStat{} + for _, deal := range deals { + if _, ok := showDealStates[deal.State]; !ok { + continue + } + if perState[deal.State] == nil { + perState[deal.State] = new(dealStat) + } + + dsAdd(&total, deal) + dsAdd(perState[deal.State], deal) + } + + type wstr struct { + str string + status storagemarket.StorageDealStatus + } + sorted := make([]wstr, 0, len(perState)) + for status, stat := range perState { + st := strings.TrimPrefix(storagemarket.DealStates[status], "StorageDeal") + sorted = append(sorted, wstr{ + str: fmt.Sprintf(" %s:\t%d\t\t%s\t(Verified: %d\t%s)\n", st, stat.count, types.SizeStr(types.NewInt(stat.bytes)), stat.verifCount, types.SizeStr(types.NewInt(stat.verifBytes))), + status: status, + }, + ) + } + sort.Slice(sorted, func(i, j int) bool { + if sorted[i].status == storagemarket.StorageDealActive || sorted[j].status == storagemarket.StorageDealActive { + return sorted[i].status == storagemarket.StorageDealActive + } + return sorted[i].status > sorted[j].status + }) + + fmt.Printf("Storage Deals: %d, %s\n", total.count, types.SizeStr(types.NewInt(total.bytes))) + + tw := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0) + for _, e := range sorted { + _, _ = tw.Write([]byte(e.str)) + } + + _ = tw.Flush() + fmt.Println() + + retrievals, err := nodeApi.MarketListRetrievalDeals(ctx) + if err != nil { + return xerrors.Errorf("getting retrieval deal list: %w", err) + } + + var retrComplete dealStat + for _, retrieval := range retrievals { + if retrieval.Status == retrievalmarket.DealStatusCompleted { + retrComplete.count++ + retrComplete.bytes += retrieval.TotalSent + } + } + + fmt.Printf("Retrieval Deals (complete): %d, %s\n", retrComplete.count, types.SizeStr(types.NewInt(retrComplete.bytes))) + + fmt.Println() } - // TODO: grab actr state / info - // * Sealed sectors (count / bytes) - // * Power return nil } diff --git a/documentation/en/api-v0-methods-miner.md b/documentation/en/api-v0-methods-miner.md index e8598ff0c..dcd3abd4d 100644 --- a/documentation/en/api-v0-methods-miner.md +++ b/documentation/en/api-v0-methods-miner.md @@ -94,6 +94,8 @@ * [ReturnSealPreCommit1](#ReturnSealPreCommit1) * [ReturnSealPreCommit2](#ReturnSealPreCommit2) * [ReturnUnsealPiece](#ReturnUnsealPiece) +* [Runtime](#Runtime) + * [RuntimeSubsystems](#RuntimeSubsystems) * [Sealing](#Sealing) * [SealingAbort](#SealingAbort) * [SealingSchedDiag](#SealingSchedDiag) @@ -1522,6 +1524,18 @@ Inputs: Response: `{}` +## Runtime + + +### RuntimeSubsystems + + +Perms: read + +Inputs: `null` + +Response: `null` + ## Sealing diff --git a/node/builder_miner.go b/node/builder_miner.go index 0c0f9d15a..d62cbcad9 100644 --- a/node/builder_miner.go +++ b/node/builder_miner.go @@ -72,6 +72,7 @@ func ConfigStorageMiner(c interface{}) Option { return Options( ConfigCommon(&cfg.Common, enableLibp2pNode), + Override(new([]api.MinerSubsystem), modules.AddMinerSubsystems(cfg.Subsystems)), Override(new(stores.LocalStorage), From(new(repo.LockedRepo))), Override(new(*stores.Local), modules.LocalStorage), Override(new(*stores.Remote), modules.RemoteStorage), @@ -215,6 +216,7 @@ func StorageMiner(out *api.StorageMiner, subsystemsCfg config.MinerSubsystemConf func(s *Settings) error { resAPI := &impl.StorageMinerAPI{} + s.invokes[ExtractApiKey] = fx.Populate(resAPI) *out = resAPI return nil diff --git a/node/impl/storminer.go b/node/impl/storminer.go index 9db6a3775..86c84fee7 100644 --- a/node/impl/storminer.go +++ b/node/impl/storminer.go @@ -48,6 +48,8 @@ import ( type StorageMinerAPI struct { fx.In + Subsystems api.MinerSubsystems + api.Common api.Net @@ -703,4 +705,8 @@ func (sm *StorageMinerAPI) ComputeProof(ctx context.Context, ssi []builtin.Secto return sm.Epp.ComputeProof(ctx, ssi, rand) } +func (sm *StorageMinerAPI) RuntimeSubsystems(context.Context) (res api.MinerSubsystems, err error) { + return sm.Subsystems, nil +} + var _ api.StorageMiner = &StorageMinerAPI{} diff --git a/node/modules/storageminer.go b/node/modules/storageminer.go index 3a3914e0c..06ef78ca0 100644 --- a/node/modules/storageminer.go +++ b/node/modules/storageminer.go @@ -1007,3 +1007,20 @@ func mutateCfg(r repo.LockedRepo, mutator func(*config.StorageMiner)) error { return multierr.Combine(typeErr, setConfigErr) } + +func AddMinerSubsystems(cfg config.MinerSubsystemConfig) (res api.MinerSubsystems) { + if cfg.EnableMining { + res = append(res, api.MiningSubsystem) + } + if cfg.EnableSealing { + res = append(res, api.SealingSubsystem) + } + if cfg.EnableSectorStorage { + res = append(res, api.SectorStorageSubsystem) + } + if cfg.EnableMarkets { + res = append(res, api.MarketsSubsystem) + } + + return +} From 4cf5c8f656f58f81f69b85fe5b8180592a810e57 Mon Sep 17 00:00:00 2001 From: Anton Evangelatov Date: Wed, 28 Jul 2021 16:23:04 +0300 Subject: [PATCH 14/31] fixup --- node/builder_miner.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/node/builder_miner.go b/node/builder_miner.go index d62cbcad9..830e6d075 100644 --- a/node/builder_miner.go +++ b/node/builder_miner.go @@ -72,7 +72,7 @@ func ConfigStorageMiner(c interface{}) Option { return Options( ConfigCommon(&cfg.Common, enableLibp2pNode), - Override(new([]api.MinerSubsystem), modules.AddMinerSubsystems(cfg.Subsystems)), + Override(new(api.MinerSubsystems), modules.AddMinerSubsystems(cfg.Subsystems)), Override(new(stores.LocalStorage), From(new(repo.LockedRepo))), Override(new(*stores.Local), modules.LocalStorage), Override(new(*stores.Remote), modules.RemoteStorage), @@ -216,7 +216,6 @@ func StorageMiner(out *api.StorageMiner, subsystemsCfg config.MinerSubsystemConf func(s *Settings) error { resAPI := &impl.StorageMinerAPI{} - s.invokes[ExtractApiKey] = fx.Populate(resAPI) *out = resAPI return nil From 54c6a3a45413f1fd4bb0857f453d2820ce80400e Mon Sep 17 00:00:00 2001 From: Anton Evangelatov Date: Wed, 28 Jul 2021 17:30:59 +0300 Subject: [PATCH 15/31] extract outputs for mining node and markets node in separate functions --- cmd/lotus-miner/info.go | 561 +++++++++++++++++++++------------------- 1 file changed, 290 insertions(+), 271 deletions(-) diff --git a/cmd/lotus-miner/info.go b/cmd/lotus-miner/info.go index 4c409788f..2a3627959 100644 --- a/cmd/lotus-miner/info.go +++ b/cmd/lotus-miner/info.go @@ -21,6 +21,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" + "github.com/filecoin-project/lotus/api/v0api" sealing "github.com/filecoin-project/lotus/extern/storage-sealing" "github.com/filecoin-project/lotus/api" @@ -103,291 +104,309 @@ func infoCmdAct(cctx *cli.Context) error { fmt.Println() if subsystems.Has(api.SectorStorageSubsystem) { - maddr, err := getActorAddress(ctx, cctx) + err := handleMiningInfo(cctx, ctx, fullapi, nodeApi) if err != nil { return err } - - mact, err := fullapi.StateGetActor(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - tbs := blockstore.NewTieredBstore(blockstore.NewAPIBlockstore(fullapi), blockstore.NewMemory()) - mas, err := miner.Load(adt.WrapStore(ctx, cbor.NewCborStore(tbs)), mact) - if err != nil { - return err - } - - // Sector size - mi, err := fullapi.StateMinerInfo(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - ssize := types.SizeStr(types.NewInt(uint64(mi.SectorSize))) - fmt.Printf("Miner: %s (%s sectors)\n", color.BlueString("%s", maddr), ssize) - - pow, err := fullapi.StateMinerPower(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - fmt.Printf("Power: %s / %s (%0.4f%%)\n", - color.GreenString(types.DeciStr(pow.MinerPower.QualityAdjPower)), - types.DeciStr(pow.TotalPower.QualityAdjPower), - types.BigDivFloat( - types.BigMul(pow.MinerPower.QualityAdjPower, big.NewInt(100)), - pow.TotalPower.QualityAdjPower, - ), - ) - - fmt.Printf("\tRaw: %s / %s (%0.4f%%)\n", - color.BlueString(types.SizeStr(pow.MinerPower.RawBytePower)), - types.SizeStr(pow.TotalPower.RawBytePower), - types.BigDivFloat( - types.BigMul(pow.MinerPower.RawBytePower, big.NewInt(100)), - pow.TotalPower.RawBytePower, - ), - ) - secCounts, err := fullapi.StateMinerSectorCount(ctx, maddr, types.EmptyTSK) - if err != nil { - return err - } - - proving := secCounts.Active + secCounts.Faulty - nfaults := secCounts.Faulty - fmt.Printf("\tCommitted: %s\n", types.SizeStr(types.BigMul(types.NewInt(secCounts.Live), types.NewInt(uint64(mi.SectorSize))))) - if nfaults == 0 { - fmt.Printf("\tProving: %s\n", types.SizeStr(types.BigMul(types.NewInt(proving), types.NewInt(uint64(mi.SectorSize))))) - } else { - var faultyPercentage float64 - if secCounts.Live != 0 { - faultyPercentage = float64(100*nfaults) / float64(secCounts.Live) - } - fmt.Printf("\tProving: %s (%s Faulty, %.2f%%)\n", - types.SizeStr(types.BigMul(types.NewInt(proving), types.NewInt(uint64(mi.SectorSize)))), - types.SizeStr(types.BigMul(types.NewInt(nfaults), types.NewInt(uint64(mi.SectorSize)))), - faultyPercentage) - } - - if !pow.HasMinPower { - fmt.Print("Below minimum power threshold, no blocks will be won") - } else { - - winRatio := new(corebig.Rat).SetFrac( - types.BigMul(pow.MinerPower.QualityAdjPower, types.NewInt(build.BlocksPerEpoch)).Int, - pow.TotalPower.QualityAdjPower.Int, - ) - - if winRatioFloat, _ := winRatio.Float64(); winRatioFloat > 0 { - - // if the corresponding poisson distribution isn't infinitely small then - // throw it into the mix as well, accounting for multi-wins - winRationWithPoissonFloat := -math.Expm1(-winRatioFloat) - winRationWithPoisson := new(corebig.Rat).SetFloat64(winRationWithPoissonFloat) - if winRationWithPoisson != nil { - winRatio = winRationWithPoisson - winRatioFloat = winRationWithPoissonFloat - } - - weekly, _ := new(corebig.Rat).Mul( - winRatio, - new(corebig.Rat).SetInt64(7*builtin.EpochsInDay), - ).Float64() - - avgDuration, _ := new(corebig.Rat).Mul( - new(corebig.Rat).SetInt64(builtin.EpochDurationSeconds), - new(corebig.Rat).Inv(winRatio), - ).Float64() - - fmt.Print("Projected average block win rate: ") - color.Blue( - "%.02f/week (every %s)", - weekly, - (time.Second * time.Duration(avgDuration)).Truncate(time.Second).String(), - ) - - // Geometric distribution of P(Y < k) calculated as described in https://en.wikipedia.org/wiki/Geometric_distribution#Probability_Outcomes_Examples - // https://www.wolframalpha.com/input/?i=t+%3E+0%3B+p+%3E+0%3B+p+%3C+1%3B+c+%3E+0%3B+c+%3C1%3B+1-%281-p%29%5E%28t%29%3Dc%3B+solve+t - // t == how many dice-rolls (epochs) before win - // p == winRate == ( minerPower / netPower ) - // c == target probability of win ( 99.9% in this case ) - fmt.Print("Projected block win with ") - color.Green( - "99.9%% probability every %s", - (time.Second * time.Duration( - builtin.EpochDurationSeconds*math.Log(1-0.999)/ - math.Log(1-winRatioFloat), - )).Truncate(time.Second).String(), - ) - fmt.Println("(projections DO NOT account for future network and miner growth)") - } - } - - 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() - if err != nil { - return xerrors.Errorf("getting locked funds: %w", err) - } - availBalance, err := mas.AvailableBalance(mact.Balance) - if err != nil { - return xerrors.Errorf("getting available balance: %w", err) - } - 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()) - colorTokenAmount(" Available: %s\n", availBalance) - - mb, err := fullapi.StateMarketBalance(ctx, maddr, types.EmptyTSK) - if err != nil { - return xerrors.Errorf("getting market balance: %w", err) - } - 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()) - colorTokenAmount(" Available: %s\n", big.Sub(mb.Escrow, mb.Locked)) - - wb, err := fullapi.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 := fullapi.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()) - } - colorTokenAmount("Total Spendable: %s\n", spendable) - - fmt.Println() - - if !cctx.Bool("hide-sectors-info") { - fmt.Println("Sectors:") - err = sectorsInfo(ctx, nodeApi) - if err != nil { - return err - } - } - - // TODO: grab actr state / info - // * Sealed sectors (count / bytes) - // * Power } if subsystems.Has(api.MarketsSubsystem) { - deals, err := nodeApi.MarketListIncompleteDeals(ctx) + err := handleMarketsInfo(ctx, nodeApi) if err != nil { return err } - - type dealStat struct { - count, verifCount int - bytes, verifBytes uint64 - } - dsAdd := func(ds *dealStat, deal storagemarket.MinerDeal) { - ds.count++ - ds.bytes += uint64(deal.Proposal.PieceSize) - if deal.Proposal.VerifiedDeal { - ds.verifCount++ - ds.verifBytes += uint64(deal.Proposal.PieceSize) - } - } - - showDealStates := map[storagemarket.StorageDealStatus]struct{}{ - storagemarket.StorageDealActive: {}, - storagemarket.StorageDealTransferring: {}, - storagemarket.StorageDealStaged: {}, - storagemarket.StorageDealAwaitingPreCommit: {}, - storagemarket.StorageDealSealing: {}, - storagemarket.StorageDealPublish: {}, - storagemarket.StorageDealCheckForAcceptance: {}, - storagemarket.StorageDealPublishing: {}, - } - - var total dealStat - perState := map[storagemarket.StorageDealStatus]*dealStat{} - for _, deal := range deals { - if _, ok := showDealStates[deal.State]; !ok { - continue - } - if perState[deal.State] == nil { - perState[deal.State] = new(dealStat) - } - - dsAdd(&total, deal) - dsAdd(perState[deal.State], deal) - } - - type wstr struct { - str string - status storagemarket.StorageDealStatus - } - sorted := make([]wstr, 0, len(perState)) - for status, stat := range perState { - st := strings.TrimPrefix(storagemarket.DealStates[status], "StorageDeal") - sorted = append(sorted, wstr{ - str: fmt.Sprintf(" %s:\t%d\t\t%s\t(Verified: %d\t%s)\n", st, stat.count, types.SizeStr(types.NewInt(stat.bytes)), stat.verifCount, types.SizeStr(types.NewInt(stat.verifBytes))), - status: status, - }, - ) - } - sort.Slice(sorted, func(i, j int) bool { - if sorted[i].status == storagemarket.StorageDealActive || sorted[j].status == storagemarket.StorageDealActive { - return sorted[i].status == storagemarket.StorageDealActive - } - return sorted[i].status > sorted[j].status - }) - - fmt.Printf("Storage Deals: %d, %s\n", total.count, types.SizeStr(types.NewInt(total.bytes))) - - tw := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0) - for _, e := range sorted { - _, _ = tw.Write([]byte(e.str)) - } - - _ = tw.Flush() - fmt.Println() - - retrievals, err := nodeApi.MarketListRetrievalDeals(ctx) - if err != nil { - return xerrors.Errorf("getting retrieval deal list: %w", err) - } - - var retrComplete dealStat - for _, retrieval := range retrievals { - if retrieval.Status == retrievalmarket.DealStatusCompleted { - retrComplete.count++ - retrComplete.bytes += retrieval.TotalSent - } - } - - fmt.Printf("Retrieval Deals (complete): %d, %s\n", retrComplete.count, types.SizeStr(types.NewInt(retrComplete.bytes))) - - fmt.Println() } return nil } +func handleMiningInfo(cctx *cli.Context, ctx context.Context, fullapi v0api.FullNode, nodeApi api.StorageMiner) error { + maddr, err := getActorAddress(ctx, cctx) + if err != nil { + return err + } + + mact, err := fullapi.StateGetActor(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + tbs := blockstore.NewTieredBstore(blockstore.NewAPIBlockstore(fullapi), blockstore.NewMemory()) + mas, err := miner.Load(adt.WrapStore(ctx, cbor.NewCborStore(tbs)), mact) + if err != nil { + return err + } + + // Sector size + mi, err := fullapi.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + ssize := types.SizeStr(types.NewInt(uint64(mi.SectorSize))) + fmt.Printf("Miner: %s (%s sectors)\n", color.BlueString("%s", maddr), ssize) + + pow, err := fullapi.StateMinerPower(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + fmt.Printf("Power: %s / %s (%0.4f%%)\n", + color.GreenString(types.DeciStr(pow.MinerPower.QualityAdjPower)), + types.DeciStr(pow.TotalPower.QualityAdjPower), + types.BigDivFloat( + types.BigMul(pow.MinerPower.QualityAdjPower, big.NewInt(100)), + pow.TotalPower.QualityAdjPower, + ), + ) + + fmt.Printf("\tRaw: %s / %s (%0.4f%%)\n", + color.BlueString(types.SizeStr(pow.MinerPower.RawBytePower)), + types.SizeStr(pow.TotalPower.RawBytePower), + types.BigDivFloat( + types.BigMul(pow.MinerPower.RawBytePower, big.NewInt(100)), + pow.TotalPower.RawBytePower, + ), + ) + secCounts, err := fullapi.StateMinerSectorCount(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + proving := secCounts.Active + secCounts.Faulty + nfaults := secCounts.Faulty + fmt.Printf("\tCommitted: %s\n", types.SizeStr(types.BigMul(types.NewInt(secCounts.Live), types.NewInt(uint64(mi.SectorSize))))) + if nfaults == 0 { + fmt.Printf("\tProving: %s\n", types.SizeStr(types.BigMul(types.NewInt(proving), types.NewInt(uint64(mi.SectorSize))))) + } else { + var faultyPercentage float64 + if secCounts.Live != 0 { + faultyPercentage = float64(100*nfaults) / float64(secCounts.Live) + } + fmt.Printf("\tProving: %s (%s Faulty, %.2f%%)\n", + types.SizeStr(types.BigMul(types.NewInt(proving), types.NewInt(uint64(mi.SectorSize)))), + types.SizeStr(types.BigMul(types.NewInt(nfaults), types.NewInt(uint64(mi.SectorSize)))), + faultyPercentage) + } + + if !pow.HasMinPower { + fmt.Print("Below minimum power threshold, no blocks will be won") + } else { + + winRatio := new(corebig.Rat).SetFrac( + types.BigMul(pow.MinerPower.QualityAdjPower, types.NewInt(build.BlocksPerEpoch)).Int, + pow.TotalPower.QualityAdjPower.Int, + ) + + if winRatioFloat, _ := winRatio.Float64(); winRatioFloat > 0 { + + // if the corresponding poisson distribution isn't infinitely small then + // throw it into the mix as well, accounting for multi-wins + winRationWithPoissonFloat := -math.Expm1(-winRatioFloat) + winRationWithPoisson := new(corebig.Rat).SetFloat64(winRationWithPoissonFloat) + if winRationWithPoisson != nil { + winRatio = winRationWithPoisson + winRatioFloat = winRationWithPoissonFloat + } + + weekly, _ := new(corebig.Rat).Mul( + winRatio, + new(corebig.Rat).SetInt64(7*builtin.EpochsInDay), + ).Float64() + + avgDuration, _ := new(corebig.Rat).Mul( + new(corebig.Rat).SetInt64(builtin.EpochDurationSeconds), + new(corebig.Rat).Inv(winRatio), + ).Float64() + + fmt.Print("Projected average block win rate: ") + color.Blue( + "%.02f/week (every %s)", + weekly, + (time.Second * time.Duration(avgDuration)).Truncate(time.Second).String(), + ) + + // Geometric distribution of P(Y < k) calculated as described in https://en.wikipedia.org/wiki/Geometric_distribution#Probability_Outcomes_Examples + // https://www.wolframalpha.com/input/?i=t+%3E+0%3B+p+%3E+0%3B+p+%3C+1%3B+c+%3E+0%3B+c+%3C1%3B+1-%281-p%29%5E%28t%29%3Dc%3B+solve+t + // t == how many dice-rolls (epochs) before win + // p == winRate == ( minerPower / netPower ) + // c == target probability of win ( 99.9% in this case ) + fmt.Print("Projected block win with ") + color.Green( + "99.9%% probability every %s", + (time.Second * time.Duration( + builtin.EpochDurationSeconds*math.Log(1-0.999)/ + math.Log(1-winRatioFloat), + )).Truncate(time.Second).String(), + ) + fmt.Println("(projections DO NOT account for future network and miner growth)") + } + } + + 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() + if err != nil { + return xerrors.Errorf("getting locked funds: %w", err) + } + availBalance, err := mas.AvailableBalance(mact.Balance) + if err != nil { + return xerrors.Errorf("getting available balance: %w", err) + } + 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()) + colorTokenAmount(" Available: %s\n", availBalance) + + mb, err := fullapi.StateMarketBalance(ctx, maddr, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting market balance: %w", err) + } + 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()) + colorTokenAmount(" Available: %s\n", big.Sub(mb.Escrow, mb.Locked)) + + wb, err := fullapi.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 := fullapi.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()) + } + colorTokenAmount("Total Spendable: %s\n", spendable) + + fmt.Println() + + if !cctx.Bool("hide-sectors-info") { + fmt.Println("Sectors:") + err = sectorsInfo(ctx, nodeApi) + if err != nil { + return err + } + } + + // TODO: grab actr state / info + // * Sealed sectors (count / bytes) + // * Power + + return nil +} + +func handleMarketsInfo(ctx context.Context, nodeApi api.StorageMiner) error { + deals, err := nodeApi.MarketListIncompleteDeals(ctx) + if err != nil { + return err + } + + type dealStat struct { + count, verifCount int + bytes, verifBytes uint64 + } + dsAdd := func(ds *dealStat, deal storagemarket.MinerDeal) { + ds.count++ + ds.bytes += uint64(deal.Proposal.PieceSize) + if deal.Proposal.VerifiedDeal { + ds.verifCount++ + ds.verifBytes += uint64(deal.Proposal.PieceSize) + } + } + + showDealStates := map[storagemarket.StorageDealStatus]struct{}{ + storagemarket.StorageDealActive: {}, + storagemarket.StorageDealTransferring: {}, + storagemarket.StorageDealStaged: {}, + storagemarket.StorageDealAwaitingPreCommit: {}, + storagemarket.StorageDealSealing: {}, + storagemarket.StorageDealPublish: {}, + storagemarket.StorageDealCheckForAcceptance: {}, + storagemarket.StorageDealPublishing: {}, + } + + var total dealStat + perState := map[storagemarket.StorageDealStatus]*dealStat{} + for _, deal := range deals { + if _, ok := showDealStates[deal.State]; !ok { + continue + } + if perState[deal.State] == nil { + perState[deal.State] = new(dealStat) + } + + dsAdd(&total, deal) + dsAdd(perState[deal.State], deal) + } + + type wstr struct { + str string + status storagemarket.StorageDealStatus + } + sorted := make([]wstr, 0, len(perState)) + for status, stat := range perState { + st := strings.TrimPrefix(storagemarket.DealStates[status], "StorageDeal") + sorted = append(sorted, wstr{ + str: fmt.Sprintf(" %s:\t%d\t\t%s\t(Verified: %d\t%s)\n", st, stat.count, types.SizeStr(types.NewInt(stat.bytes)), stat.verifCount, types.SizeStr(types.NewInt(stat.verifBytes))), + status: status, + }, + ) + } + sort.Slice(sorted, func(i, j int) bool { + if sorted[i].status == storagemarket.StorageDealActive || sorted[j].status == storagemarket.StorageDealActive { + return sorted[i].status == storagemarket.StorageDealActive + } + return sorted[i].status > sorted[j].status + }) + + fmt.Printf("Storage Deals: %d, %s\n", total.count, types.SizeStr(types.NewInt(total.bytes))) + + tw := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0) + for _, e := range sorted { + _, _ = tw.Write([]byte(e.str)) + } + + _ = tw.Flush() + fmt.Println() + + retrievals, err := nodeApi.MarketListRetrievalDeals(ctx) + if err != nil { + return xerrors.Errorf("getting retrieval deal list: %w", err) + } + + var retrComplete dealStat + for _, retrieval := range retrievals { + if retrieval.Status == retrievalmarket.DealStatusCompleted { + retrComplete.count++ + retrComplete.bytes += retrieval.TotalSent + } + } + + fmt.Printf("Retrieval Deals (complete): %d, %s\n", retrComplete.count, types.SizeStr(types.NewInt(retrComplete.bytes))) + + fmt.Println() + + return nil +} + type stateMeta struct { i int col color.Attribute From 6969202a03466e7f46d280c05ae5bf0ca2ed0468 Mon Sep 17 00:00:00 2001 From: Anton Evangelatov Date: Wed, 28 Jul 2021 17:47:01 +0300 Subject: [PATCH 16/31] make linter happy --- cmd/lotus-miner/info.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/lotus-miner/info.go b/cmd/lotus-miner/info.go index 2a3627959..92f25667c 100644 --- a/cmd/lotus-miner/info.go +++ b/cmd/lotus-miner/info.go @@ -104,7 +104,7 @@ func infoCmdAct(cctx *cli.Context) error { fmt.Println() if subsystems.Has(api.SectorStorageSubsystem) { - err := handleMiningInfo(cctx, ctx, fullapi, nodeApi) + err := handleMiningInfo(ctx, cctx, fullapi, nodeApi) if err != nil { return err } @@ -120,7 +120,7 @@ func infoCmdAct(cctx *cli.Context) error { return nil } -func handleMiningInfo(cctx *cli.Context, ctx context.Context, fullapi v0api.FullNode, nodeApi api.StorageMiner) error { +func handleMiningInfo(ctx context.Context, cctx *cli.Context, fullapi v0api.FullNode, nodeApi api.StorageMiner) error { maddr, err := getActorAddress(ctx, cctx) if err != nil { return err From f8afb699cddb455075c4f90c0512d92723bfcd32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 28 Jul 2021 17:29:17 +0200 Subject: [PATCH 17/31] make: Allow setting Go compiler with GOCC --- Makefile | 62 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/Makefile b/Makefile index 77fd38b9e..77040d1b6 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,8 @@ MODULES:= CLEAN:= BINS:= +GOCC?=go + ldflags=-X=github.com/filecoin-project/lotus/build.CurrentCommit=+git.$(subst -,.,$(shell git describe --always --match=NeVeRmAtCh --dirty 2>/dev/null || git rev-parse --short HEAD 2>/dev/null)) ifneq ($(strip $(LDFLAGS)),) ldflags+=-extldflags=$(LDFLAGS) @@ -85,32 +87,32 @@ interopnet: build-devnets lotus: $(BUILD_DEPS) rm -f lotus - go build $(GOFLAGS) -o lotus ./cmd/lotus + $(GOCC) build $(GOFLAGS) -o lotus ./cmd/lotus .PHONY: lotus BINS+=lotus lotus-miner: $(BUILD_DEPS) rm -f lotus-miner - go build $(GOFLAGS) -o lotus-miner ./cmd/lotus-miner + $(GOCC) build $(GOFLAGS) -o lotus-miner ./cmd/lotus-miner .PHONY: lotus-miner BINS+=lotus-miner lotus-worker: $(BUILD_DEPS) rm -f lotus-worker - go build $(GOFLAGS) -o lotus-worker ./cmd/lotus-seal-worker + $(GOCC) build $(GOFLAGS) -o lotus-worker ./cmd/lotus-seal-worker .PHONY: lotus-worker BINS+=lotus-worker lotus-shed: $(BUILD_DEPS) rm -f lotus-shed - go build $(GOFLAGS) -o lotus-shed ./cmd/lotus-shed + $(GOCC) build $(GOFLAGS) -o lotus-shed ./cmd/lotus-shed .PHONY: lotus-shed BINS+=lotus-shed lotus-gateway: $(BUILD_DEPS) rm -f lotus-gateway - go build $(GOFLAGS) -o lotus-gateway ./cmd/lotus-gateway + $(GOCC) build $(GOFLAGS) -o lotus-gateway ./cmd/lotus-gateway .PHONY: lotus-gateway BINS+=lotus-gateway @@ -138,19 +140,19 @@ install-app: lotus-seed: $(BUILD_DEPS) rm -f lotus-seed - go build $(GOFLAGS) -o lotus-seed ./cmd/lotus-seed + $(GOCC) build $(GOFLAGS) -o lotus-seed ./cmd/lotus-seed .PHONY: lotus-seed BINS+=lotus-seed benchmarks: - go run github.com/whyrusleeping/bencher ./... > bench.json + $(GOCC) run github.com/whyrusleeping/bencher ./... > bench.json @echo Submitting results @curl -X POST 'http://benchmark.kittyhawk.wtf/benchmark' -d '@bench.json' -u "${benchmark_http_cred}" .PHONY: benchmarks lotus-pond: 2k - go build -o lotus-pond ./lotuspond + $(GOCC) build -o lotus-pond ./lotuspond .PHONY: lotus-pond BINS+=lotus-pond @@ -163,7 +165,7 @@ lotus-pond-app: lotus-pond-front lotus-pond lotus-townhall: rm -f lotus-townhall - go build -o lotus-townhall ./cmd/lotus-townhall + $(GOCC) build -o lotus-townhall ./cmd/lotus-townhall .PHONY: lotus-townhall BINS+=lotus-townhall @@ -176,61 +178,61 @@ lotus-townhall-app: lotus-touch lotus-townhall-front lotus-fountain: rm -f lotus-fountain - go build -o lotus-fountain ./cmd/lotus-fountain + $(GOCC) build -o lotus-fountain ./cmd/lotus-fountain .PHONY: lotus-fountain BINS+=lotus-fountain lotus-chainwatch: rm -f lotus-chainwatch - go build $(GOFLAGS) -o lotus-chainwatch ./cmd/lotus-chainwatch + $(GOCC) build $(GOFLAGS) -o lotus-chainwatch ./cmd/lotus-chainwatch .PHONY: lotus-chainwatch BINS+=lotus-chainwatch lotus-bench: rm -f lotus-bench - go build -o lotus-bench ./cmd/lotus-bench + $(GOCC) build -o lotus-bench ./cmd/lotus-bench .PHONY: lotus-bench BINS+=lotus-bench lotus-stats: rm -f lotus-stats - go build $(GOFLAGS) -o lotus-stats ./cmd/lotus-stats + $(GOCC) build $(GOFLAGS) -o lotus-stats ./cmd/lotus-stats .PHONY: lotus-stats BINS+=lotus-stats lotus-pcr: rm -f lotus-pcr - go build $(GOFLAGS) -o lotus-pcr ./cmd/lotus-pcr + $(GOCC) build $(GOFLAGS) -o lotus-pcr ./cmd/lotus-pcr .PHONY: lotus-pcr BINS+=lotus-pcr lotus-health: rm -f lotus-health - go build -o lotus-health ./cmd/lotus-health + $(GOCC) build -o lotus-health ./cmd/lotus-health .PHONY: lotus-health BINS+=lotus-health lotus-wallet: rm -f lotus-wallet - go build -o lotus-wallet ./cmd/lotus-wallet + $(GOCC) build -o lotus-wallet ./cmd/lotus-wallet .PHONY: lotus-wallet BINS+=lotus-wallet lotus-keygen: rm -f lotus-keygen - go build -o lotus-keygen ./cmd/lotus-keygen + $(GOCC) build -o lotus-keygen ./cmd/lotus-keygen .PHONY: lotus-keygen BINS+=lotus-keygen testground: - go build -tags testground -o /dev/null ./cmd/lotus + $(GOCC) build -tags testground -o /dev/null ./cmd/lotus .PHONY: testground BINS+=testground tvx: rm -f tvx - go build -o tvx ./cmd/tvx + $(GOCC) build -o tvx ./cmd/tvx .PHONY: tvx BINS+=tvx @@ -239,7 +241,7 @@ install-chainwatch: lotus-chainwatch lotus-sim: $(BUILD_DEPS) rm -f lotus-sim - go build $(GOFLAGS) -o lotus-sim ./cmd/lotus-sim + $(GOCC) build $(GOFLAGS) -o lotus-sim ./cmd/lotus-sim .PHONY: lotus-sim BINS+=lotus-sim @@ -319,25 +321,25 @@ dist-clean: .PHONY: dist-clean type-gen: api-gen - go run ./gen/main.go - go generate -x ./... + $(GOCC) run ./gen/main.go + $(GOCC) generate -x ./... goimports -w api/ method-gen: api-gen - (cd ./lotuspond/front/src/chain && go run ./methodgen.go) + (cd ./lotuspond/front/src/chain && $(GOCC) run ./methodgen.go) actors-gen: - go run ./chain/actors/agen - go fmt ./... + $(GOCC) run ./chain/actors/agen + $(GOCC) fmt ./... api-gen: - go run ./gen/api + $(GOCC) run ./gen/api goimports -w api goimports -w api .PHONY: api-gen cfgdoc-gen: - go run ./node/config/cfgdocgen > ./node/config/doc_gen.go + $(GOCC) run ./node/config/cfgdocgen > ./node/config/doc_gen.go appimage: lotus rm -rf appimage-builder-cache || true @@ -351,9 +353,9 @@ appimage: lotus docsgen: docsgen-md docsgen-openrpc docsgen-md-bin: api-gen actors-gen - go build $(GOFLAGS) -o docgen-md ./api/docgen/cmd + $(GOCC) build $(GOFLAGS) -o docgen-md ./api/docgen/cmd docsgen-openrpc-bin: api-gen actors-gen - go build $(GOFLAGS) -o docgen-openrpc ./api/docgen-openrpc/cmd + $(GOCC) build $(GOFLAGS) -o docgen-openrpc ./api/docgen-openrpc/cmd docsgen-md: docsgen-md-full docsgen-md-storage docsgen-md-worker @@ -393,4 +395,4 @@ print-%: @echo $*=$($*) circleci: - go generate -x ./.circleci \ No newline at end of file + $(GOCC) generate -x ./.circleci \ No newline at end of file From 389f71251c770030a59e22400eb97202eaa9c835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 28 Jul 2021 17:50:29 +0200 Subject: [PATCH 18/31] Remove townhall --- Makefile | 13 -- chain/metrics/consensus.go | 129 ----------------- cmd/lotus-townhall/main.go | 134 ------------------ cmd/lotus-townhall/townhall/.gitignore | 23 --- cmd/lotus-townhall/townhall/package.json | 31 ---- cmd/lotus-townhall/townhall/public/index.html | 13 -- cmd/lotus-townhall/townhall/public/robots.txt | 2 - cmd/lotus-townhall/townhall/src/App.css | 1 - cmd/lotus-townhall/townhall/src/App.js | 87 ------------ cmd/lotus-townhall/townhall/src/App.test.js | 9 -- cmd/lotus-townhall/townhall/src/index.css | 6 - cmd/lotus-townhall/townhall/src/index.js | 6 - node/builder_chain.go | 5 - node/config/doc_gen.go | 20 --- node/config/types.go | 7 - 15 files changed, 486 deletions(-) delete mode 100644 chain/metrics/consensus.go delete mode 100644 cmd/lotus-townhall/main.go delete mode 100644 cmd/lotus-townhall/townhall/.gitignore delete mode 100644 cmd/lotus-townhall/townhall/package.json delete mode 100644 cmd/lotus-townhall/townhall/public/index.html delete mode 100644 cmd/lotus-townhall/townhall/public/robots.txt delete mode 100644 cmd/lotus-townhall/townhall/src/App.css delete mode 100644 cmd/lotus-townhall/townhall/src/App.js delete mode 100644 cmd/lotus-townhall/townhall/src/App.test.js delete mode 100644 cmd/lotus-townhall/townhall/src/index.css delete mode 100644 cmd/lotus-townhall/townhall/src/index.js diff --git a/Makefile b/Makefile index 77040d1b6..4aac5f21a 100644 --- a/Makefile +++ b/Makefile @@ -163,19 +163,6 @@ lotus-pond-front: lotus-pond-app: lotus-pond-front lotus-pond .PHONY: lotus-pond-app -lotus-townhall: - rm -f lotus-townhall - $(GOCC) build -o lotus-townhall ./cmd/lotus-townhall -.PHONY: lotus-townhall -BINS+=lotus-townhall - -lotus-townhall-front: - (cd ./cmd/lotus-townhall/townhall && npm i && npm run build) -.PHONY: lotus-townhall-front - -lotus-townhall-app: lotus-touch lotus-townhall-front -.PHONY: lotus-townhall-app - lotus-fountain: rm -f lotus-fountain $(GOCC) build -o lotus-fountain ./cmd/lotus-fountain diff --git a/chain/metrics/consensus.go b/chain/metrics/consensus.go deleted file mode 100644 index c3c4a10d1..000000000 --- a/chain/metrics/consensus.go +++ /dev/null @@ -1,129 +0,0 @@ -package metrics - -import ( - "context" - "encoding/json" - - "github.com/filecoin-project/go-state-types/abi" - "github.com/ipfs/go-cid" - logging "github.com/ipfs/go-log/v2" - pubsub "github.com/libp2p/go-libp2p-pubsub" - "go.uber.org/fx" - - "github.com/filecoin-project/lotus/build" - "github.com/filecoin-project/lotus/chain/types" - "github.com/filecoin-project/lotus/node/impl/full" - "github.com/filecoin-project/lotus/node/modules/helpers" -) - -var log = logging.Logger("metrics") - -const baseTopic = "/fil/headnotifs/" - -type Update struct { - Type string -} - -func SendHeadNotifs(nickname string) func(mctx helpers.MetricsCtx, lc fx.Lifecycle, ps *pubsub.PubSub, chain full.ChainAPI) error { - return func(mctx helpers.MetricsCtx, lc fx.Lifecycle, ps *pubsub.PubSub, chain full.ChainAPI) error { - ctx := helpers.LifecycleCtx(mctx, lc) - - lc.Append(fx.Hook{ - OnStart: func(_ context.Context) error { - gen, err := chain.Chain.GetGenesis() - if err != nil { - return err - } - - topic := baseTopic + gen.Cid().String() - - go func() { - if err := sendHeadNotifs(ctx, ps, topic, chain, nickname); err != nil { - log.Error("consensus metrics error", err) - return - } - }() - go func() { - sub, err := ps.Subscribe(topic) //nolint - if err != nil { - return - } - defer sub.Cancel() - - for { - if _, err := sub.Next(ctx); err != nil { - return - } - } - - }() - return nil - }, - }) - - return nil - } -} - -type message struct { - // TipSet - Cids []cid.Cid - Blocks []*types.BlockHeader - Height abi.ChainEpoch - Weight types.BigInt - Time uint64 - Nonce uint64 - - // Meta - - NodeName string -} - -func sendHeadNotifs(ctx context.Context, ps *pubsub.PubSub, topic string, chain full.ChainAPI, nickname string) error { - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - notifs, err := chain.ChainNotify(ctx) - if err != nil { - return err - } - - // using unix nano time makes very sure we pick a nonce higher than previous restart - nonce := uint64(build.Clock.Now().UnixNano()) - - for { - select { - case notif := <-notifs: - n := notif[len(notif)-1] - - w, err := chain.ChainTipSetWeight(ctx, n.Val.Key()) - if err != nil { - return err - } - - m := message{ - Cids: n.Val.Cids(), - Blocks: n.Val.Blocks(), - Height: n.Val.Height(), - Weight: w, - NodeName: nickname, - Time: uint64(build.Clock.Now().UnixNano() / 1000_000), - Nonce: nonce, - } - - b, err := json.Marshal(m) - if err != nil { - return err - } - - //nolint - if err := ps.Publish(topic, b); err != nil { - return err - } - case <-ctx.Done(): - return nil - } - - nonce++ - } -} diff --git a/cmd/lotus-townhall/main.go b/cmd/lotus-townhall/main.go deleted file mode 100644 index 1e0460dee..000000000 --- a/cmd/lotus-townhall/main.go +++ /dev/null @@ -1,134 +0,0 @@ -package main - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "net/http" - "time" - - rice "github.com/GeertJohan/go.rice" - "github.com/gorilla/websocket" - "github.com/ipld/go-car" - "github.com/libp2p/go-libp2p" - "github.com/libp2p/go-libp2p-core/peer" - pubsub "github.com/libp2p/go-libp2p-pubsub" - - "github.com/filecoin-project/lotus/blockstore" - "github.com/filecoin-project/lotus/build" -) - -var topic = "/fil/headnotifs/" - -func init() { - genBytes := build.MaybeGenesis() - if len(genBytes) == 0 { - topic = "" - return - } - - bs := blockstore.NewMemory() - - c, err := car.LoadCar(bs, bytes.NewReader(genBytes)) - if err != nil { - panic(err) - } - if len(c.Roots) != 1 { - panic("expected genesis file to have one root") - } - - fmt.Printf("Genesis CID: %s\n", c.Roots[0]) - topic = topic + c.Roots[0].String() -} - -var upgrader = websocket.Upgrader{ - WriteBufferSize: 1024, - CheckOrigin: func(r *http.Request) bool { - return true - }, -} - -func main() { - if topic == "" { - fmt.Println("FATAL: No genesis found") - return - } - - ctx := context.Background() - - host, err := libp2p.New( - ctx, - libp2p.Defaults, - ) - if err != nil { - panic(err) - } - ps, err := pubsub.NewGossipSub(ctx, host) - if err != nil { - panic(err) - } - - pi, err := build.BuiltinBootstrap() - if err != nil { - panic(err) - } - - if err := host.Connect(ctx, pi[0]); err != nil { - panic(err) - } - - http.HandleFunc("/sub", handler(ps)) - http.Handle("/", http.FileServer(rice.MustFindBox("townhall/build").HTTPBox())) - - fmt.Println("listening on http://localhost:2975") - - if err := http.ListenAndServe("0.0.0.0:2975", nil); err != nil { - panic(err) - } -} - -type update struct { - From peer.ID - Update json.RawMessage - Time uint64 -} - -func handler(ps *pubsub.PubSub) func(w http.ResponseWriter, r *http.Request) { - return func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") - if r.Header.Get("Sec-WebSocket-Protocol") != "" { - w.Header().Set("Sec-WebSocket-Protocol", r.Header.Get("Sec-WebSocket-Protocol")) - } - - conn, err := upgrader.Upgrade(w, r, nil) - if err != nil { - return - } - - sub, err := ps.Subscribe(topic) //nolint - if err != nil { - return - } - defer sub.Cancel() //nolint:errcheck - - fmt.Println("new conn") - - for { - msg, err := sub.Next(r.Context()) - if err != nil { - return - } - - //fmt.Println(msg) - - if err := conn.WriteJSON(update{ - From: peer.ID(msg.From), - Update: msg.Data, - Time: uint64(time.Now().UnixNano() / 1000_000), - }); err != nil { - return - } - } - } -} diff --git a/cmd/lotus-townhall/townhall/.gitignore b/cmd/lotus-townhall/townhall/.gitignore deleted file mode 100644 index 4d29575de..000000000 --- a/cmd/lotus-townhall/townhall/.gitignore +++ /dev/null @@ -1,23 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# production -/build - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -npm-debug.log* -yarn-debug.log* -yarn-error.log* diff --git a/cmd/lotus-townhall/townhall/package.json b/cmd/lotus-townhall/townhall/package.json deleted file mode 100644 index 5a8167622..000000000 --- a/cmd/lotus-townhall/townhall/package.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "townhall", - "version": "0.1.0", - "private": true, - "dependencies": { - "react": "^16.10.2", - "react-dom": "^16.10.2", - "react-scripts": "3.2.0" - }, - "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject" - }, - "eslintConfig": { - "extends": "react-app" - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - } -} diff --git a/cmd/lotus-townhall/townhall/public/index.html b/cmd/lotus-townhall/townhall/public/index.html deleted file mode 100644 index 38af10597..000000000 --- a/cmd/lotus-townhall/townhall/public/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - Lotus TownHall - - - -
- - diff --git a/cmd/lotus-townhall/townhall/public/robots.txt b/cmd/lotus-townhall/townhall/public/robots.txt deleted file mode 100644 index 01b0f9a10..000000000 --- a/cmd/lotus-townhall/townhall/public/robots.txt +++ /dev/null @@ -1,2 +0,0 @@ -# https://www.robotstxt.org/robotstxt.html -User-agent: * diff --git a/cmd/lotus-townhall/townhall/src/App.css b/cmd/lotus-townhall/townhall/src/App.css deleted file mode 100644 index 8b1378917..000000000 --- a/cmd/lotus-townhall/townhall/src/App.css +++ /dev/null @@ -1 +0,0 @@ - diff --git a/cmd/lotus-townhall/townhall/src/App.js b/cmd/lotus-townhall/townhall/src/App.js deleted file mode 100644 index 2f216f5da..000000000 --- a/cmd/lotus-townhall/townhall/src/App.js +++ /dev/null @@ -1,87 +0,0 @@ -import React from 'react'; -import './App.css'; - -function colForH(besth, height) { - const diff = besth - height - if(diff === 0) return '#6f6' - if(diff === 1) return '#df4' - if(diff < 4) return '#ff0' - if(diff < 10) return '#f60' - return '#f00' -} - -function colLag(lag) { - if(lag < 100) return '#6f6' - if(lag < 400) return '#df4' - if(lag < 1000) return '#ff0' - if(lag < 4000) return '#f60' - return '#f00' -} - -function lagCol(lag, good) { - return - {lag} - ms - -} - -class App extends React.Component { - constructor(props) { - super(props); - - let ws = new WebSocket("ws://" + window.location.host + "/sub") - //let ws = new WebSocket("ws://127.0.0.1:2975/sub") - - ws.onmessage = (ev) => { - console.log(ev) - let update = JSON.parse(ev.data) - - update.Update.Weight = Number(update.Update.Weight) - - let wdiff = update.Update.Weight - (this.state[update.From] || {Weight: update.Update.Weight}).Weight - wdiff = {wdiff} - - let utDiff = update.Time - (this.state[update.From] || {utime: update.Time}).utime - utDiff = {utDiff}ms - - this.setState( prev => ({ - ...prev, [update.From]: {...update.Update, utime: update.Time, wdiff: wdiff, utDiff: utDiff}, - })) - } - - ws.onclose = () => { - this.setState({disconnected: true}) - } - - this.state = {} - } - - render() { - if(this.state.disconnected) { - return Error: disconnected - } - - let besth = Object.keys(this.state).map(k => this.state[k]).reduce((p, n) => p > n.Height ? p : n.Height, -1) - let bestw = Object.keys(this.state).map(k => this.state[k]).reduce((p, n) => p > n.Weight ? p : n.Weight, -1) - - return - - {Object.keys(this.state).map(k => [k, this.state[k]]).map(([k, v]) => { - let mnrs = v.Blocks.map(b => ) - let l = [ - , - , - , - , - , - ...mnrs, - ] - - l = {l} - return l - }) - } -
PeerIDNicknameLagWeight(best, prev)HeightBlocks
 m:{b.Miner}({lagCol(v.Time ? v.Time - (b.Timestamp*1000) : v.utime - (b.Timestamp*1000), v.Time)}){k}{v.NodeName}{v.Time ? lagCol(v.utime - v.Time, true) : ""}(Δ{v.utDiff}){v.Weight}({bestw - v.Weight}, {v.wdiff}){v.Height}({besth - v.Height})
- } -} -export default App; diff --git a/cmd/lotus-townhall/townhall/src/App.test.js b/cmd/lotus-townhall/townhall/src/App.test.js deleted file mode 100644 index a754b201b..000000000 --- a/cmd/lotus-townhall/townhall/src/App.test.js +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import App from './App'; - -it('renders without crashing', () => { - const div = document.createElement('div'); - ReactDOM.render(, div); - ReactDOM.unmountComponentAtNode(div); -}); diff --git a/cmd/lotus-townhall/townhall/src/index.css b/cmd/lotus-townhall/townhall/src/index.css deleted file mode 100644 index fb0d9d10e..000000000 --- a/cmd/lotus-townhall/townhall/src/index.css +++ /dev/null @@ -1,6 +0,0 @@ -body { - margin: 0; - font-family: monospace; - background: #1f1f1f; - color: #f0f0f0; -} diff --git a/cmd/lotus-townhall/townhall/src/index.js b/cmd/lotus-townhall/townhall/src/index.js deleted file mode 100644 index 395b74997..000000000 --- a/cmd/lotus-townhall/townhall/src/index.js +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import './index.css'; -import App from './App'; - -ReactDOM.render(, document.getElementById('root')); diff --git a/node/builder_chain.go b/node/builder_chain.go index 1447a4df7..4d9294972 100644 --- a/node/builder_chain.go +++ b/node/builder_chain.go @@ -17,7 +17,6 @@ import ( "github.com/filecoin-project/lotus/chain/market" "github.com/filecoin-project/lotus/chain/messagepool" "github.com/filecoin-project/lotus/chain/messagesigner" - "github.com/filecoin-project/lotus/chain/metrics" "github.com/filecoin-project/lotus/chain/stmgr" rpcstmgr "github.com/filecoin-project/lotus/chain/stmgr/rpc" "github.com/filecoin-project/lotus/chain/store" @@ -174,10 +173,6 @@ func ConfigFullNode(c interface{}) Option { ), Override(new(dtypes.Graphsync), modules.Graphsync(cfg.Client.SimultaneousTransfers)), - If(cfg.Metrics.HeadNotifs, - Override(HeadMetricsKey, metrics.SendHeadNotifs(cfg.Metrics.Nickname)), - ), - If(cfg.Wallet.RemoteBackend != "", Override(new(*remotewallet.RemoteWallet), remotewallet.SetupRemoteWallet(cfg.Wallet.RemoteBackend)), ), diff --git a/node/config/doc_gen.go b/node/config/doc_gen.go index 5d4a91d5f..430abebbb 100644 --- a/node/config/doc_gen.go +++ b/node/config/doc_gen.go @@ -245,12 +245,6 @@ see https://docs.filecoin.io/mine/lotus/miner-configuration/#using-filters-for-f Comment: ``, }, - { - Name: "Metrics", - Type: "Metrics", - - Comment: ``, - }, { Name: "Wallet", Type: "Wallet", @@ -324,20 +318,6 @@ Format: multiaddress`, Comment: ``, }, }, - "Metrics": []DocField{ - { - Name: "Nickname", - Type: "string", - - Comment: ``, - }, - { - Name: "HeadNotifs", - Type: "bool", - - Comment: ``, - }, - }, "MinerAddressConfig": []DocField{ { Name: "PreCommitControl", diff --git a/node/config/types.go b/node/config/types.go index fe42aa27e..12f7653fb 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -23,7 +23,6 @@ type Common struct { type FullNode struct { Common Client Client - Metrics Metrics Wallet Wallet Fees FeeConfig Chainstore Chainstore @@ -298,12 +297,6 @@ type Splitstore struct { } // // Full Node - -type Metrics struct { - Nickname string - HeadNotifs bool -} - type Client struct { UseIpfs bool IpfsOnlineMode bool From 25052fe48f6e155e2cf05b57572e5102f0be8360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 28 Jul 2021 17:53:33 +0200 Subject: [PATCH 19/31] Remove chainwatch (now filecoin-project/sentinel) --- Makefile | 27 +- cmd/lotus-chainwatch/dot.go | 131 --- cmd/lotus-chainwatch/main.go | 54 - .../processor/common_actors.go | 299 ----- cmd/lotus-chainwatch/processor/market.go | 316 ----- cmd/lotus-chainwatch/processor/messages.go | 318 ----- cmd/lotus-chainwatch/processor/miner.go | 1035 ----------------- cmd/lotus-chainwatch/processor/mpool.go | 100 -- cmd/lotus-chainwatch/processor/power.go | 190 --- cmd/lotus-chainwatch/processor/processor.go | 420 ------- cmd/lotus-chainwatch/processor/reward.go | 234 ---- cmd/lotus-chainwatch/run.go | 107 -- .../refresh_top_miners_by_base_reward.go | 78 -- cmd/lotus-chainwatch/scheduler/scheduler.go | 60 - cmd/lotus-chainwatch/syncer/blockssub.go | 27 - cmd/lotus-chainwatch/syncer/sync.go | 527 --------- cmd/lotus-chainwatch/util/api.go | 34 - cmd/lotus-chainwatch/util/contextStore.go | 51 - scripts/lotus-chainwatch.service | 15 - 19 files changed, 2 insertions(+), 4021 deletions(-) delete mode 100644 cmd/lotus-chainwatch/dot.go delete mode 100644 cmd/lotus-chainwatch/main.go delete mode 100644 cmd/lotus-chainwatch/processor/common_actors.go delete mode 100644 cmd/lotus-chainwatch/processor/market.go delete mode 100644 cmd/lotus-chainwatch/processor/messages.go delete mode 100644 cmd/lotus-chainwatch/processor/miner.go delete mode 100644 cmd/lotus-chainwatch/processor/mpool.go delete mode 100644 cmd/lotus-chainwatch/processor/power.go delete mode 100644 cmd/lotus-chainwatch/processor/processor.go delete mode 100644 cmd/lotus-chainwatch/processor/reward.go delete mode 100644 cmd/lotus-chainwatch/run.go delete mode 100644 cmd/lotus-chainwatch/scheduler/refresh_top_miners_by_base_reward.go delete mode 100644 cmd/lotus-chainwatch/scheduler/scheduler.go delete mode 100644 cmd/lotus-chainwatch/syncer/blockssub.go delete mode 100644 cmd/lotus-chainwatch/syncer/sync.go delete mode 100644 cmd/lotus-chainwatch/util/api.go delete mode 100644 cmd/lotus-chainwatch/util/contextStore.go delete mode 100644 scripts/lotus-chainwatch.service diff --git a/Makefile b/Makefile index 4aac5f21a..dfe4a65c7 100644 --- a/Makefile +++ b/Makefile @@ -169,12 +169,6 @@ lotus-fountain: .PHONY: lotus-fountain BINS+=lotus-fountain -lotus-chainwatch: - rm -f lotus-chainwatch - $(GOCC) build $(GOFLAGS) -o lotus-chainwatch ./cmd/lotus-chainwatch -.PHONY: lotus-chainwatch -BINS+=lotus-chainwatch - lotus-bench: rm -f lotus-bench $(GOCC) build -o lotus-bench ./cmd/lotus-bench @@ -223,9 +217,6 @@ tvx: .PHONY: tvx BINS+=tvx -install-chainwatch: lotus-chainwatch - install -C ./lotus-chainwatch /usr/local/bin/lotus-chainwatch - lotus-sim: $(BUILD_DEPS) rm -f lotus-sim $(GOCC) build $(GOFLAGS) -o lotus-sim ./cmd/lotus-sim @@ -250,21 +241,13 @@ install-miner-service: install-miner install-daemon-service @echo @echo "lotus-miner service installed. Don't forget to run 'sudo systemctl start lotus-miner' to start it and 'sudo systemctl enable lotus-miner' for it to be enabled on startup." -install-chainwatch-service: install-chainwatch install-daemon-service - mkdir -p /etc/systemd/system - mkdir -p /var/log/lotus - install -C -m 0644 ./scripts/lotus-chainwatch.service /etc/systemd/system/lotus-chainwatch.service - systemctl daemon-reload - @echo - @echo "chainwatch service installed. Don't forget to run 'sudo systemctl start lotus-chainwatch' to start it and 'sudo systemctl enable lotus-chainwatch' for it to be enabled on startup." - install-main-services: install-miner-service -install-all-services: install-main-services install-chainwatch-service +install-all-services: install-main-services install-services: install-main-services -clean-daemon-service: clean-miner-service clean-chainwatch-service +clean-daemon-service: clean-miner-service -systemctl stop lotus-daemon -systemctl disable lotus-daemon rm -f /etc/systemd/system/lotus-daemon.service @@ -276,12 +259,6 @@ clean-miner-service: rm -f /etc/systemd/system/lotus-miner.service systemctl daemon-reload -clean-chainwatch-service: - -systemctl stop lotus-chainwatch - -systemctl disable lotus-chainwatch - rm -f /etc/systemd/system/lotus-chainwatch.service - systemctl daemon-reload - clean-main-services: clean-daemon-service clean-all-services: clean-main-services diff --git a/cmd/lotus-chainwatch/dot.go b/cmd/lotus-chainwatch/dot.go deleted file mode 100644 index 3149d65f5..000000000 --- a/cmd/lotus-chainwatch/dot.go +++ /dev/null @@ -1,131 +0,0 @@ -package main - -import ( - "database/sql" - "fmt" - "hash/crc32" - "strconv" - - "github.com/ipfs/go-cid" - logging "github.com/ipfs/go-log/v2" - "github.com/urfave/cli/v2" - "golang.org/x/xerrors" -) - -var dotCmd = &cli.Command{ - Name: "dot", - Usage: "generate dot graphs", - ArgsUsage: " ", - Action: func(cctx *cli.Context) error { - ll := cctx.String("log-level") - if err := logging.SetLogLevel("*", ll); err != nil { - return err - } - - db, err := sql.Open("postgres", cctx.String("db")) - if err != nil { - return err - } - defer func() { - if err := db.Close(); err != nil { - log.Errorw("Failed to close database", "error", err) - } - }() - - if err := db.Ping(); err != nil { - return xerrors.Errorf("Database failed to respond to ping (is it online?): %w", err) - } - - minH, err := strconv.ParseInt(cctx.Args().Get(0), 10, 32) - if err != nil { - return err - } - tosee, err := strconv.ParseInt(cctx.Args().Get(1), 10, 32) - if err != nil { - return err - } - maxH := minH + tosee - - res, err := db.Query(`select block, parent, b.miner, b.height, p.height from block_parents - inner join blocks b on block_parents.block = b.cid - inner join blocks p on block_parents.parent = p.cid -where b.height > $1 and b.height < $2`, minH, maxH) - - if err != nil { - return err - } - - fmt.Println("digraph D {") - - hl, err := syncedBlocks(db) - if err != nil { - log.Fatal(err) - } - - for res.Next() { - var block, parent, miner string - var height, ph uint64 - if err := res.Scan(&block, &parent, &miner, &height, &ph); err != nil { - return err - } - - bc, err := cid.Parse(block) - if err != nil { - return err - } - - _, has := hl[bc] - - col := crc32.Checksum([]byte(miner), crc32.MakeTable(crc32.Castagnoli))&0xc0c0c0c0 + 0x30303030 - - hasstr := "" - if !has { - //col = 0xffffffff - hasstr = " UNSYNCED" - } - - nulls := height - ph - 1 - for i := uint64(0); i < nulls; i++ { - name := block + "NP" + fmt.Sprint(i) - - fmt.Printf("%s [label = \"NULL:%d\", fillcolor = \"#ffddff\", style=filled, forcelabels=true]\n%s -> %s\n", - name, height-nulls+i, name, parent) - - parent = name - } - - fmt.Printf("%s [label = \"%s:%d%s\", fillcolor = \"#%06x\", style=filled, forcelabels=true]\n%s -> %s\n", block, miner, height, hasstr, col, block, parent) - } - if res.Err() != nil { - return res.Err() - } - - fmt.Println("}") - - return nil - }, -} - -func syncedBlocks(db *sql.DB) (map[cid.Cid]struct{}, error) { - // timestamp is used to return a configurable amount of rows based on when they were last added. - rws, err := db.Query(`select cid FROM blocks_synced`) - if err != nil { - return nil, xerrors.Errorf("Failed to query blocks_synced: %w", err) - } - out := map[cid.Cid]struct{}{} - - for rws.Next() { - var c string - if err := rws.Scan(&c); err != nil { - return nil, xerrors.Errorf("Failed to scan blocks_synced: %w", err) - } - - ci, err := cid.Parse(c) - if err != nil { - return nil, xerrors.Errorf("Failed to parse blocks_synced: %w", err) - } - - out[ci] = struct{}{} - } - return out, nil -} diff --git a/cmd/lotus-chainwatch/main.go b/cmd/lotus-chainwatch/main.go deleted file mode 100644 index 5cb0f3507..000000000 --- a/cmd/lotus-chainwatch/main.go +++ /dev/null @@ -1,54 +0,0 @@ -package main - -import ( - "os" - - "github.com/filecoin-project/lotus/build" - logging "github.com/ipfs/go-log/v2" - "github.com/urfave/cli/v2" -) - -var log = logging.Logger("chainwatch") - -func main() { - if err := logging.SetLogLevel("*", "info"); err != nil { - log.Fatal(err) - } - log.Info("Starting chainwatch", " v", build.UserVersion()) - - app := &cli.App{ - Name: "lotus-chainwatch", - Usage: "Devnet token distribution utility", - Version: build.UserVersion(), - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "repo", - EnvVars: []string{"LOTUS_PATH"}, - Value: "~/.lotus", // TODO: Consider XDG_DATA_HOME - }, - &cli.StringFlag{ - Name: "api", - EnvVars: []string{"FULLNODE_API_INFO"}, - Value: "", - }, - &cli.StringFlag{ - Name: "db", - EnvVars: []string{"LOTUS_DB"}, - Value: "", - }, - &cli.StringFlag{ - Name: "log-level", - EnvVars: []string{"GOLOG_LOG_LEVEL"}, - Value: "info", - }, - }, - Commands: []*cli.Command{ - dotCmd, - runCmd, - }, - } - - if err := app.Run(os.Args); err != nil { - log.Fatal(err) - } -} diff --git a/cmd/lotus-chainwatch/processor/common_actors.go b/cmd/lotus-chainwatch/processor/common_actors.go deleted file mode 100644 index 0f2c0d2ea..000000000 --- a/cmd/lotus-chainwatch/processor/common_actors.go +++ /dev/null @@ -1,299 +0,0 @@ -package processor - -import ( - "context" - "time" - - "golang.org/x/sync/errgroup" - "golang.org/x/xerrors" - - "github.com/filecoin-project/go-address" - "github.com/filecoin-project/go-state-types/abi" - "github.com/ipfs/go-cid" - - builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" - - "github.com/filecoin-project/lotus/chain/actors/builtin" - _init "github.com/filecoin-project/lotus/chain/actors/builtin/init" - "github.com/filecoin-project/lotus/chain/events/state" - "github.com/filecoin-project/lotus/chain/types" - cw_util "github.com/filecoin-project/lotus/cmd/lotus-chainwatch/util" -) - -func (p *Processor) setupCommonActors() error { - tx, err := p.db.Begin() - if err != nil { - return err - } - - if _, err := tx.Exec(` -create table if not exists id_address_map -( - id text not null, - address text not null, - constraint id_address_map_pk - primary key (id, address) -); - -create unique index if not exists id_address_map_id_uindex - on id_address_map (id); - -create unique index if not exists id_address_map_address_uindex - on id_address_map (address); - -create table if not exists actors - ( - id text not null - constraint id_address_map_actors_id_fk - references id_address_map (id), - code text not null, - head text not null, - nonce int not null, - balance text not null, - stateroot text - ); - -create index if not exists actors_id_index - on actors (id); - -create index if not exists id_address_map_address_index - on id_address_map (address); - -create index if not exists id_address_map_id_index - on id_address_map (id); - -create or replace function actor_tips(epoch bigint) - returns table (id text, - code text, - head text, - nonce int, - balance text, - stateroot text, - height bigint, - parentstateroot text) as -$body$ - select distinct on (id) * from actors - inner join state_heights sh on sh.parentstateroot = stateroot - where height < $1 - order by id, height desc; -$body$ language sql; - -create table if not exists actor_states -( - head text not null, - code text not null, - state json not null -); - -create unique index if not exists actor_states_head_code_uindex - on actor_states (head, code); - -create index if not exists actor_states_head_index - on actor_states (head); - -create index if not exists actor_states_code_head_index - on actor_states (head, code); - -`); err != nil { - return err - } - - return tx.Commit() -} - -func (p *Processor) HandleCommonActorsChanges(ctx context.Context, actors map[cid.Cid]ActorTips) error { - if err := p.storeActorAddresses(ctx, actors); err != nil { - return err - } - - grp, _ := errgroup.WithContext(ctx) - - grp.Go(func() error { - if err := p.storeActorHeads(actors); err != nil { - return err - } - return nil - }) - - grp.Go(func() error { - if err := p.storeActorStates(actors); err != nil { - return err - } - return nil - }) - - return grp.Wait() -} - -type UpdateAddresses struct { - Old state.AddressPair - New state.AddressPair -} - -func (p Processor) storeActorAddresses(ctx context.Context, actors map[cid.Cid]ActorTips) error { - start := time.Now() - defer func() { - log.Debugw("Stored Actor Addresses", "duration", time.Since(start).String()) - }() - - addressToID := map[address.Address]address.Address{} - // HACK until genesis storage is figured out: - addressToID[builtin2.SystemActorAddr] = builtin2.SystemActorAddr - addressToID[builtin2.InitActorAddr] = builtin2.InitActorAddr - addressToID[builtin2.RewardActorAddr] = builtin2.RewardActorAddr - addressToID[builtin2.CronActorAddr] = builtin2.CronActorAddr - addressToID[builtin2.StoragePowerActorAddr] = builtin2.StoragePowerActorAddr - addressToID[builtin2.StorageMarketActorAddr] = builtin2.StorageMarketActorAddr - addressToID[builtin2.VerifiedRegistryActorAddr] = builtin2.VerifiedRegistryActorAddr - addressToID[builtin2.BurntFundsActorAddr] = builtin2.BurntFundsActorAddr - initActor, err := p.node.StateGetActor(ctx, builtin2.InitActorAddr, types.EmptyTSK) - if err != nil { - return err - } - - initActorState, err := _init.Load(cw_util.NewAPIIpldStore(ctx, p.node), initActor) - if err != nil { - return err - } - // gross.. - if err := initActorState.ForEachActor(func(id abi.ActorID, addr address.Address) error { - idAddr, err := address.NewIDAddress(uint64(id)) - if err != nil { - return err - } - addressToID[addr] = idAddr - return nil - }); err != nil { - return err - } - tx, err := p.db.Begin() - if err != nil { - return err - } - - if _, err := tx.Exec(` -create temp table iam (like id_address_map excluding constraints) on commit drop; -`); err != nil { - return xerrors.Errorf("prep temp: %w", err) - } - - stmt, err := tx.Prepare(`copy iam (id, address) from STDIN `) - if err != nil { - return err - } - - for a, i := range addressToID { - if i == address.Undef { - continue - } - if _, err := stmt.Exec( - i.String(), - a.String(), - ); err != nil { - return err - } - } - if err := stmt.Close(); err != nil { - return err - } - - // HACK until chain watch can handle reorgs we need to update this table when ID -> PubKey mappings change - if _, err := tx.Exec(`insert into id_address_map select * from iam on conflict (id) do update set address = EXCLUDED.address`); err != nil { - log.Warnw("Failed to update id_address_map table, this is a known issue") - return nil - } - - return tx.Commit() -} - -func (p *Processor) storeActorHeads(actors map[cid.Cid]ActorTips) error { - start := time.Now() - defer func() { - log.Debugw("Stored Actor Heads", "duration", time.Since(start).String()) - }() - // Basic - tx, err := p.db.Begin() - if err != nil { - return err - } - if _, err := tx.Exec(` - create temp table a_tmp (like actors excluding constraints) on commit drop; - `); err != nil { - return xerrors.Errorf("prep temp: %w", err) - } - - stmt, err := tx.Prepare(`copy a_tmp (id, code, head, nonce, balance, stateroot) from stdin `) - if err != nil { - return err - } - - for code, actTips := range actors { - actorName := code.String() - if builtin.IsBuiltinActor(code) { - actorName = builtin.ActorNameByCode(code) - } - for _, actorInfo := range actTips { - for _, a := range actorInfo { - if _, err := stmt.Exec(a.addr.String(), actorName, a.act.Head.String(), a.act.Nonce, a.act.Balance.String(), a.stateroot.String()); err != nil { - return err - } - } - } - } - - if err := stmt.Close(); err != nil { - return err - } - - if _, err := tx.Exec(`insert into actors select * from a_tmp on conflict do nothing `); err != nil { - return xerrors.Errorf("actor put: %w", err) - } - - return tx.Commit() -} - -func (p *Processor) storeActorStates(actors map[cid.Cid]ActorTips) error { - start := time.Now() - defer func() { - log.Debugw("Stored Actor States", "duration", time.Since(start).String()) - }() - // States - tx, err := p.db.Begin() - if err != nil { - return err - } - if _, err := tx.Exec(` - create temp table as_tmp (like actor_states excluding constraints) on commit drop; - `); err != nil { - return xerrors.Errorf("prep temp: %w", err) - } - - stmt, err := tx.Prepare(`copy as_tmp (head, code, state) from stdin `) - if err != nil { - return err - } - - for code, actTips := range actors { - actorName := code.String() - if builtin.IsBuiltinActor(code) { - actorName = builtin.ActorNameByCode(code) - } - for _, actorInfo := range actTips { - for _, a := range actorInfo { - if _, err := stmt.Exec(a.act.Head.String(), actorName, a.state); err != nil { - return err - } - } - } - } - - if err := stmt.Close(); err != nil { - return err - } - - if _, err := tx.Exec(`insert into actor_states select * from as_tmp on conflict do nothing `); err != nil { - return xerrors.Errorf("actor put: %w", err) - } - - return tx.Commit() -} diff --git a/cmd/lotus-chainwatch/processor/market.go b/cmd/lotus-chainwatch/processor/market.go deleted file mode 100644 index 17aa1c37b..000000000 --- a/cmd/lotus-chainwatch/processor/market.go +++ /dev/null @@ -1,316 +0,0 @@ -package processor - -import ( - "context" - "strconv" - "time" - - "golang.org/x/sync/errgroup" - "golang.org/x/xerrors" - - "github.com/filecoin-project/lotus/chain/actors/builtin/market" - "github.com/filecoin-project/lotus/chain/events/state" -) - -func (p *Processor) setupMarket() error { - tx, err := p.db.Begin() - if err != nil { - return err - } - - if _, err := tx.Exec(` -create table if not exists market_deal_proposals -( - deal_id bigint not null, - - state_root text not null, - - piece_cid text not null, - padded_piece_size bigint not null, - unpadded_piece_size bigint not null, - is_verified bool not null, - - client_id text not null, - provider_id text not null, - - start_epoch bigint not null, - end_epoch bigint not null, - slashed_epoch bigint, - storage_price_per_epoch text not null, - - provider_collateral text not null, - client_collateral text not null, - - constraint market_deal_proposal_pk - primary key (deal_id) -); - -create table if not exists market_deal_states -( - deal_id bigint not null, - - sector_start_epoch bigint not null, - last_update_epoch bigint not null, - slash_epoch bigint not null, - - state_root text not null, - - unique (deal_id, sector_start_epoch, last_update_epoch, slash_epoch), - - constraint market_deal_states_pk - primary key (deal_id, state_root) - -); - -create table if not exists minerid_dealid_sectorid -( - deal_id bigint not null - constraint sectors_sector_ids_id_fk - references market_deal_proposals(deal_id), - - sector_id bigint not null, - miner_id text not null, - foreign key (sector_id, miner_id) references sector_precommit_info(sector_id, miner_id), - - constraint miner_sector_deal_ids_pk - primary key (miner_id, sector_id, deal_id) -); - -`); err != nil { - return err - } - - return tx.Commit() -} - -type marketActorInfo struct { - common actorInfo -} - -func (p *Processor) HandleMarketChanges(ctx context.Context, marketTips ActorTips) error { - marketChanges, err := p.processMarket(ctx, marketTips) - if err != nil { - log.Fatalw("Failed to process market actors", "error", err) - } - - if err := p.persistMarket(ctx, marketChanges); err != nil { - log.Fatalw("Failed to persist market actors", "error", err) - } - - if err := p.updateMarket(ctx, marketChanges); err != nil { - log.Fatalw("Failed to update market actors", "error", err) - } - return nil -} - -func (p *Processor) processMarket(ctx context.Context, marketTips ActorTips) ([]marketActorInfo, error) { - start := time.Now() - defer func() { - log.Debugw("Processed Market", "duration", time.Since(start).String()) - }() - - var out []marketActorInfo - for _, markets := range marketTips { - for _, mt := range markets { - // NB: here is where we can extract the market state when we need it. - out = append(out, marketActorInfo{common: mt}) - } - } - return out, nil -} - -func (p *Processor) persistMarket(ctx context.Context, info []marketActorInfo) error { - start := time.Now() - defer func() { - log.Debugw("Persisted Market", "duration", time.Since(start).String()) - }() - - grp, ctx := errgroup.WithContext(ctx) - - grp.Go(func() error { - if err := p.storeMarketActorDealProposals(ctx, info); err != nil { - return xerrors.Errorf("Failed to store marker deal proposals: %w", err) - } - return nil - }) - - grp.Go(func() error { - if err := p.storeMarketActorDealStates(info); err != nil { - return xerrors.Errorf("Failed to store marker deal states: %w", err) - } - return nil - }) - - return grp.Wait() - -} - -func (p *Processor) updateMarket(ctx context.Context, info []marketActorInfo) error { - if err := p.updateMarketActorDealProposals(ctx, info); err != nil { - return xerrors.Errorf("Failed to update market info: %w", err) - } - return nil -} - -func (p *Processor) storeMarketActorDealStates(marketTips []marketActorInfo) error { - start := time.Now() - defer func() { - log.Debugw("Stored Market Deal States", "duration", time.Since(start).String()) - }() - tx, err := p.db.Begin() - if err != nil { - return err - } - if _, err := tx.Exec(`create temp table mds (like market_deal_states excluding constraints) on commit drop;`); err != nil { - return err - } - stmt, err := tx.Prepare(`copy mds (deal_id, sector_start_epoch, last_update_epoch, slash_epoch, state_root) from STDIN`) - if err != nil { - return err - } - for _, mt := range marketTips { - dealStates, err := p.node.StateMarketDeals(context.TODO(), mt.common.tsKey) - if err != nil { - return err - } - - for dealID, ds := range dealStates { - id, err := strconv.ParseUint(dealID, 10, 64) - if err != nil { - return err - } - - if _, err := stmt.Exec( - id, - ds.State.SectorStartEpoch, - ds.State.LastUpdatedEpoch, - ds.State.SlashEpoch, - mt.common.stateroot.String(), - ); err != nil { - return err - } - - } - } - if err := stmt.Close(); err != nil { - return err - } - - if _, err := tx.Exec(`insert into market_deal_states select * from mds on conflict do nothing`); err != nil { - return err - } - - return tx.Commit() -} - -func (p *Processor) storeMarketActorDealProposals(ctx context.Context, marketTips []marketActorInfo) error { - start := time.Now() - defer func() { - log.Debugw("Stored Market Deal Proposals", "duration", time.Since(start).String()) - }() - tx, err := p.db.Begin() - if err != nil { - return err - } - - if _, err := tx.Exec(`create temp table mdp (like market_deal_proposals excluding constraints) on commit drop;`); err != nil { - return xerrors.Errorf("prep temp: %w", err) - } - - stmt, err := tx.Prepare(`copy mdp (deal_id, state_root, piece_cid, padded_piece_size, unpadded_piece_size, is_verified, client_id, provider_id, start_epoch, end_epoch, slashed_epoch, storage_price_per_epoch, provider_collateral, client_collateral) from STDIN`) - if err != nil { - return err - } - - // insert in sorted order (lowest height -> highest height) since dealid is pk of table. - for _, mt := range marketTips { - dealStates, err := p.node.StateMarketDeals(ctx, mt.common.tsKey) - if err != nil { - return err - } - - for dealID, ds := range dealStates { - id, err := strconv.ParseUint(dealID, 10, 64) - if err != nil { - return err - } - - if _, err := stmt.Exec( - id, - mt.common.stateroot.String(), - ds.Proposal.PieceCID.String(), - ds.Proposal.PieceSize, - ds.Proposal.PieceSize.Unpadded(), - ds.Proposal.VerifiedDeal, - ds.Proposal.Client.String(), - ds.Proposal.Provider.String(), - ds.Proposal.StartEpoch, - ds.Proposal.EndEpoch, - nil, // slashed_epoch - ds.Proposal.StoragePricePerEpoch.String(), - ds.Proposal.ProviderCollateral.String(), - ds.Proposal.ClientCollateral.String(), - ); err != nil { - return err - } - - } - } - if err := stmt.Close(); err != nil { - return err - } - if _, err := tx.Exec(`insert into market_deal_proposals select * from mdp on conflict do nothing`); err != nil { - return err - } - - return tx.Commit() - -} - -func (p *Processor) updateMarketActorDealProposals(ctx context.Context, marketTip []marketActorInfo) error { - start := time.Now() - defer func() { - log.Debugw("Updated Market Deal Proposals", "duration", time.Since(start).String()) - }() - pred := state.NewStatePredicates(p.node) - - tx, err := p.db.Begin() - if err != nil { - return err - } - - stmt, err := tx.Prepare(`update market_deal_proposals set slashed_epoch=$1 where deal_id=$2`) - if err != nil { - return err - } - - for _, mt := range marketTip { - stateDiff := pred.OnStorageMarketActorChanged(pred.OnDealStateChanged(pred.OnDealStateAmtChanged())) - - changed, val, err := stateDiff(ctx, mt.common.parentTsKey, mt.common.tsKey) - if err != nil { - log.Warnw("error getting market deal state diff", "error", err) - } - if !changed { - continue - } - changes, ok := val.(*market.DealStateChanges) - if !ok { - return xerrors.Errorf("Unknown type returned by Deal State AMT predicate: %T", val) - } - - for _, modified := range changes.Modified { - if modified.From.SlashEpoch != modified.To.SlashEpoch { - if _, err := stmt.Exec(modified.To.SlashEpoch, modified.ID); err != nil { - return err - } - } - } - } - - if err := stmt.Close(); err != nil { - return err - } - - return tx.Commit() -} diff --git a/cmd/lotus-chainwatch/processor/messages.go b/cmd/lotus-chainwatch/processor/messages.go deleted file mode 100644 index 333477c6a..000000000 --- a/cmd/lotus-chainwatch/processor/messages.go +++ /dev/null @@ -1,318 +0,0 @@ -package processor - -import ( - "context" - "sync" - - "golang.org/x/sync/errgroup" - "golang.org/x/xerrors" - - "github.com/ipfs/go-cid" - - "github.com/filecoin-project/lotus/chain/types" - "github.com/filecoin-project/lotus/lib/parmap" -) - -func (p *Processor) setupMessages() error { - tx, err := p.db.Begin() - if err != nil { - return err - } - - if _, err := tx.Exec(` -create table if not exists messages -( - cid text not null - constraint messages_pk - primary key, - "from" text not null, - "to" text not null, - size_bytes bigint not null, - nonce bigint not null, - value text not null, - gas_fee_cap text not null, - gas_premium text not null, - gas_limit bigint not null, - method bigint, - params bytea -); - -create unique index if not exists messages_cid_uindex - on messages (cid); - -create index if not exists messages_from_index - on messages ("from"); - -create index if not exists messages_to_index - on messages ("to"); - -create table if not exists block_messages -( - block text not null - constraint blocks_block_cids_cid_fk - references block_cids (cid), - message text not null, - constraint block_messages_pk - primary key (block, message) -); - -create table if not exists mpool_messages -( - msg text not null - constraint mpool_messages_pk - primary key - constraint mpool_messages_messages_cid_fk - references messages, - add_ts int not null -); - -create unique index if not exists mpool_messages_msg_uindex - on mpool_messages (msg); - -create table if not exists receipts -( - msg text not null, - state text not null, - idx int not null, - exit int not null, - gas_used bigint not null, - return bytea, - constraint receipts_pk - primary key (msg, state) -); - -create index if not exists receipts_msg_state_index - on receipts (msg, state); -`); err != nil { - return err - } - - return tx.Commit() -} - -func (p *Processor) HandleMessageChanges(ctx context.Context, blocks map[cid.Cid]*types.BlockHeader) error { - if err := p.persistMessagesAndReceipts(ctx, blocks); err != nil { - return err - } - return nil -} - -func (p *Processor) persistMessagesAndReceipts(ctx context.Context, blocks map[cid.Cid]*types.BlockHeader) error { - messages, inclusions := p.fetchMessages(ctx, blocks) - receipts := p.fetchParentReceipts(ctx, blocks) - - grp, _ := errgroup.WithContext(ctx) - - grp.Go(func() error { - return p.storeMessages(messages) - }) - - grp.Go(func() error { - return p.storeMsgInclusions(inclusions) - }) - - grp.Go(func() error { - return p.storeReceipts(receipts) - }) - - return grp.Wait() -} - -func (p *Processor) storeReceipts(recs map[mrec]*types.MessageReceipt) error { - tx, err := p.db.Begin() - if err != nil { - return err - } - - if _, err := tx.Exec(` -create temp table recs (like receipts excluding constraints) on commit drop; -`); err != nil { - return xerrors.Errorf("prep temp: %w", err) - } - - stmt, err := tx.Prepare(`copy recs (msg, state, idx, exit, gas_used, return) from stdin `) - if err != nil { - return err - } - - for c, m := range recs { - if _, err := stmt.Exec( - c.msg.String(), - c.state.String(), - c.idx, - m.ExitCode, - m.GasUsed, - m.Return, - ); err != nil { - return err - } - } - if err := stmt.Close(); err != nil { - return err - } - - if _, err := tx.Exec(`insert into receipts select * from recs on conflict do nothing `); err != nil { - return xerrors.Errorf("actor put: %w", err) - } - - return tx.Commit() -} - -func (p *Processor) storeMsgInclusions(incls map[cid.Cid][]cid.Cid) error { - tx, err := p.db.Begin() - if err != nil { - return err - } - - if _, err := tx.Exec(` -create temp table mi (like block_messages excluding constraints) on commit drop; -`); err != nil { - return xerrors.Errorf("prep temp: %w", err) - } - - stmt, err := tx.Prepare(`copy mi (block, message) from STDIN `) - if err != nil { - return err - } - - for b, msgs := range incls { - for _, msg := range msgs { - if _, err := stmt.Exec( - b.String(), - msg.String(), - ); err != nil { - return err - } - } - } - if err := stmt.Close(); err != nil { - return err - } - - if _, err := tx.Exec(`insert into block_messages select * from mi on conflict do nothing `); err != nil { - return xerrors.Errorf("actor put: %w", err) - } - - return tx.Commit() -} - -func (p *Processor) storeMessages(msgs map[cid.Cid]*types.Message) error { - tx, err := p.db.Begin() - if err != nil { - return err - } - - if _, err := tx.Exec(` -create temp table msgs (like messages excluding constraints) on commit drop; -`); err != nil { - return xerrors.Errorf("prep temp: %w", err) - } - - stmt, err := tx.Prepare(`copy msgs (cid, "from", "to", size_bytes, nonce, "value", gas_premium, gas_fee_cap, gas_limit, method, params) from stdin `) - if err != nil { - return err - } - - for c, m := range msgs { - var msgBytes int - if b, err := m.Serialize(); err == nil { - msgBytes = len(b) - } - - if _, err := stmt.Exec( - c.String(), - m.From.String(), - m.To.String(), - msgBytes, - m.Nonce, - m.Value.String(), - m.GasPremium.String(), - m.GasFeeCap.String(), - m.GasLimit, - m.Method, - m.Params, - ); err != nil { - return err - } - } - if err := stmt.Close(); err != nil { - return err - } - - if _, err := tx.Exec(`insert into messages select * from msgs on conflict do nothing `); err != nil { - return xerrors.Errorf("actor put: %w", err) - } - - return tx.Commit() -} - -func (p *Processor) fetchMessages(ctx context.Context, blocks map[cid.Cid]*types.BlockHeader) (map[cid.Cid]*types.Message, map[cid.Cid][]cid.Cid) { - var lk sync.Mutex - messages := map[cid.Cid]*types.Message{} - inclusions := map[cid.Cid][]cid.Cid{} // block -> msgs - - parmap.Par(50, parmap.MapArr(blocks), func(header *types.BlockHeader) { - msgs, err := p.node.ChainGetBlockMessages(ctx, header.Cid()) - if err != nil { - log.Error(err) - log.Debugw("ChainGetBlockMessages", "header_cid", header.Cid()) - return - } - - vmm := make([]*types.Message, 0, len(msgs.Cids)) - for _, m := range msgs.BlsMessages { - vmm = append(vmm, m) - } - - for _, m := range msgs.SecpkMessages { - vmm = append(vmm, &m.Message) - } - - lk.Lock() - for _, message := range vmm { - messages[message.Cid()] = message - inclusions[header.Cid()] = append(inclusions[header.Cid()], message.Cid()) - } - lk.Unlock() - }) - - return messages, inclusions -} - -type mrec struct { - msg cid.Cid - state cid.Cid - idx int -} - -func (p *Processor) fetchParentReceipts(ctx context.Context, toSync map[cid.Cid]*types.BlockHeader) map[mrec]*types.MessageReceipt { - var lk sync.Mutex - out := map[mrec]*types.MessageReceipt{} - - parmap.Par(50, parmap.MapArr(toSync), func(header *types.BlockHeader) { - recs, err := p.node.ChainGetParentReceipts(ctx, header.Cid()) - if err != nil { - log.Error(err) - log.Debugw("ChainGetParentReceipts", "header_cid", header.Cid()) - return - } - msgs, err := p.node.ChainGetParentMessages(ctx, header.Cid()) - if err != nil { - log.Error(err) - log.Debugw("ChainGetParentMessages", "header_cid", header.Cid()) - return - } - - lk.Lock() - for i, r := range recs { - out[mrec{ - msg: msgs[i].Cid, - state: header.ParentStateRoot, - idx: i, - }] = r - } - lk.Unlock() - }) - - return out -} diff --git a/cmd/lotus-chainwatch/processor/miner.go b/cmd/lotus-chainwatch/processor/miner.go deleted file mode 100644 index f3514df88..000000000 --- a/cmd/lotus-chainwatch/processor/miner.go +++ /dev/null @@ -1,1035 +0,0 @@ -package processor - -import ( - "context" - "strings" - "time" - - "github.com/filecoin-project/go-address" - "github.com/filecoin-project/go-bitfield" - "github.com/ipfs/go-cid" - "golang.org/x/sync/errgroup" - "golang.org/x/xerrors" - - "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/go-state-types/big" - - "github.com/filecoin-project/lotus/api/v0api" - "github.com/filecoin-project/lotus/blockstore" - "github.com/filecoin-project/lotus/chain/actors/builtin/miner" - "github.com/filecoin-project/lotus/chain/actors/builtin/power" - "github.com/filecoin-project/lotus/chain/events/state" - "github.com/filecoin-project/lotus/chain/store" - "github.com/filecoin-project/lotus/chain/types" - cw_util "github.com/filecoin-project/lotus/cmd/lotus-chainwatch/util" -) - -func (p *Processor) setupMiners() error { - tx, err := p.db.Begin() - if err != nil { - return err - } - - if _, err := tx.Exec(` - -create table if not exists miner_info -( - miner_id text not null, - owner_addr text not null, - worker_addr text not null, - peer_id text, - sector_size text not null, - - constraint miner_info_pk - primary key (miner_id) -); - -create table if not exists sector_precommit_info -( - miner_id text not null, - sector_id bigint not null, - sealed_cid text not null, - state_root text not null, - - seal_rand_epoch bigint not null, - expiration_epoch bigint not null, - - precommit_deposit text not null, - precommit_epoch bigint not null, - deal_weight text not null, - verified_deal_weight text not null, - - - is_replace_capacity bool not null, - replace_sector_deadline bigint, - replace_sector_partition bigint, - replace_sector_number bigint, - - unique (miner_id, sector_id), - - constraint sector_precommit_info_pk - primary key (miner_id, sector_id, sealed_cid) - -); - -create table if not exists sector_info -( - miner_id text not null, - sector_id bigint not null, - sealed_cid text not null, - state_root text not null, - - activation_epoch bigint not null, - expiration_epoch bigint not null, - - deal_weight text not null, - verified_deal_weight text not null, - - initial_pledge text not null, - expected_day_reward text not null, - expected_storage_pledge text not null, - - constraint sector_info_pk - primary key (miner_id, sector_id, sealed_cid) -); - -/* -* captures miner-specific power state for any given stateroot -*/ -create table if not exists miner_power -( - miner_id text not null, - state_root text not null, - raw_bytes_power text not null, - quality_adjusted_power text not null, - constraint miner_power_pk - primary key (miner_id, state_root) -); - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'miner_sector_event_type') THEN - CREATE TYPE miner_sector_event_type AS ENUM - ( - 'PRECOMMIT_ADDED', 'PRECOMMIT_EXPIRED', 'COMMIT_CAPACITY_ADDED', 'SECTOR_ADDED', - 'SECTOR_EXTENDED', 'SECTOR_EXPIRED', 'SECTOR_FAULTED', 'SECTOR_RECOVERING', 'SECTOR_RECOVERED', 'SECTOR_TERMINATED' - ); - END IF; -END$$; - -create table if not exists miner_sector_events -( - miner_id text not null, - sector_id bigint not null, - state_root text not null, - event miner_sector_event_type not null, - - constraint miner_sector_events_pk - primary key (sector_id, event, miner_id, state_root) -); - -`); err != nil { - return err - } - - return tx.Commit() -} - -type SectorLifecycleEvent string - -const ( - PreCommitAdded = "PRECOMMIT_ADDED" - PreCommitExpired = "PRECOMMIT_EXPIRED" - - CommitCapacityAdded = "COMMIT_CAPACITY_ADDED" - - SectorAdded = "SECTOR_ADDED" - SectorExpired = "SECTOR_EXPIRED" - SectorExtended = "SECTOR_EXTENDED" - SectorFaulted = "SECTOR_FAULTED" - SectorRecovering = "SECTOR_RECOVERING" - SectorRecovered = "SECTOR_RECOVERED" - SectorTerminated = "SECTOR_TERMINATED" -) - -type MinerSectorsEvent struct { - MinerID address.Address - SectorIDs []uint64 - StateRoot cid.Cid - Event SectorLifecycleEvent -} - -type SectorDealEvent struct { - MinerID address.Address - SectorID uint64 - DealIDs []abi.DealID -} - -type PartitionStatus struct { - Terminated bitfield.BitField - Expired bitfield.BitField - Faulted bitfield.BitField - InRecovery bitfield.BitField - Recovered bitfield.BitField -} - -type minerActorInfo struct { - common actorInfo - - state miner.State - - // tracked by power actor - rawPower big.Int - qalPower big.Int -} - -func (p *Processor) HandleMinerChanges(ctx context.Context, minerTips ActorTips) error { - minerChanges, err := p.processMiners(ctx, minerTips) - if err != nil { - log.Fatalw("Failed to process miner actors", "error", err) - } - - if err := p.persistMiners(ctx, minerChanges); err != nil { - log.Fatalw("Failed to persist miner actors", "error", err) - } - - return nil -} - -func (p *Processor) processMiners(ctx context.Context, minerTips map[types.TipSetKey][]actorInfo) ([]minerActorInfo, error) { - start := time.Now() - defer func() { - log.Debugw("Processed Miners", "duration", time.Since(start).String()) - }() - - stor := store.ActorStore(ctx, blockstore.NewAPIBlockstore(p.node)) - - var out []minerActorInfo - // TODO add parallel calls if this becomes slow - for tipset, miners := range minerTips { - // get the power actors claims map - powerState, err := getPowerActorState(ctx, p.node, tipset) - if err != nil { - return nil, err - } - - // Get miner raw and quality power - for _, act := range miners { - var mi minerActorInfo - mi.common = act - - // get miner claim from power actors claim map and store if found, else the miner had no claim at - // this tipset - claim, found, err := powerState.MinerPower(act.addr) - if err != nil { - return nil, err - } - if found { - mi.qalPower = claim.QualityAdjPower - mi.rawPower = claim.RawBytePower - } - - // Get the miner state - mas, err := miner.Load(stor, &act.act) - if err != nil { - log.Warnw("failed to find miner actor state", "address", act.addr, "error", err) - continue - } - mi.state = mas - out = append(out, mi) - } - } - return out, nil -} - -func (p *Processor) persistMiners(ctx context.Context, miners []minerActorInfo) error { - start := time.Now() - defer func() { - log.Debugw("Persisted Miners", "duration", time.Since(start).String()) - }() - - grp, _ := errgroup.WithContext(ctx) - - grp.Go(func() error { - if err := p.storeMinersPower(miners); err != nil { - return err - } - return nil - }) - - grp.Go(func() error { - if err := p.storeMinersActorInfoState(ctx, miners); err != nil { - return err - } - return nil - }) - - // 8 is arbitrary, idk what a good value here is. - preCommitEvents := make(chan *MinerSectorsEvent, 8) - sectorEvents := make(chan *MinerSectorsEvent, 8) - partitionEvents := make(chan *MinerSectorsEvent, 8) - dealEvents := make(chan *SectorDealEvent, 8) - - grp.Go(func() error { - return p.storePreCommitDealInfo(dealEvents) - }) - - grp.Go(func() error { - return p.storeMinerSectorEvents(ctx, sectorEvents, preCommitEvents, partitionEvents) - }) - - grp.Go(func() error { - defer func() { - close(preCommitEvents) - close(dealEvents) - }() - return p.storeMinerPreCommitInfo(ctx, miners, preCommitEvents, dealEvents) - }) - - grp.Go(func() error { - defer close(sectorEvents) - return p.storeMinerSectorInfo(ctx, miners, sectorEvents) - }) - - grp.Go(func() error { - defer close(partitionEvents) - return p.getMinerPartitionsDifferences(ctx, miners, partitionEvents) - }) - - return grp.Wait() -} - -func (p *Processor) storeMinerPreCommitInfo(ctx context.Context, miners []minerActorInfo, sectorEvents chan<- *MinerSectorsEvent, sectorDeals chan<- *SectorDealEvent) error { - tx, err := p.db.Begin() - if err != nil { - return err - } - - if _, err := tx.Exec(`create temp table spi (like sector_precommit_info excluding constraints) on commit drop;`); err != nil { - return xerrors.Errorf("Failed to create temp table for sector_precommit_info: %w", err) - } - - stmt, err := tx.Prepare(`copy spi (miner_id, sector_id, sealed_cid, state_root, seal_rand_epoch, expiration_epoch, precommit_deposit, precommit_epoch, deal_weight, verified_deal_weight, is_replace_capacity, replace_sector_deadline, replace_sector_partition, replace_sector_number) from STDIN`) - - if err != nil { - return xerrors.Errorf("Failed to prepare miner precommit info statement: %w", err) - } - - grp, _ := errgroup.WithContext(ctx) - for _, m := range miners { - m := m - grp.Go(func() error { - changes, err := p.getMinerPreCommitChanges(ctx, m) - if err != nil { - if strings.Contains(err.Error(), types.ErrActorNotFound.Error()) { - return nil - } - return err - } - if changes == nil { - return nil - } - - preCommitAdded := make([]uint64, len(changes.Added)) - for i, added := range changes.Added { - if len(added.Info.DealIDs) > 0 { - sectorDeals <- &SectorDealEvent{ - MinerID: m.common.addr, - SectorID: uint64(added.Info.SectorNumber), - DealIDs: added.Info.DealIDs, - } - } - if added.Info.ReplaceCapacity { - if _, err := stmt.Exec( - m.common.addr.String(), - added.Info.SectorNumber, - added.Info.SealedCID.String(), - m.common.stateroot.String(), - added.Info.SealRandEpoch, - added.Info.Expiration, - added.PreCommitDeposit.String(), - added.PreCommitEpoch, - added.DealWeight.String(), - added.VerifiedDealWeight.String(), - added.Info.ReplaceCapacity, - added.Info.ReplaceSectorDeadline, - added.Info.ReplaceSectorPartition, - added.Info.ReplaceSectorNumber, - ); err != nil { - return err - } - } else { - if _, err := stmt.Exec( - m.common.addr.String(), - added.Info.SectorNumber, - added.Info.SealedCID.String(), - m.common.stateroot.String(), - added.Info.SealRandEpoch, - added.Info.Expiration, - added.PreCommitDeposit.String(), - added.PreCommitEpoch, - added.DealWeight.String(), - added.VerifiedDealWeight.String(), - added.Info.ReplaceCapacity, - nil, // replace deadline - nil, // replace partition - nil, // replace sector - ); err != nil { - return err - } - - } - preCommitAdded[i] = uint64(added.Info.SectorNumber) - } - if len(preCommitAdded) > 0 { - sectorEvents <- &MinerSectorsEvent{ - MinerID: m.common.addr, - StateRoot: m.common.stateroot, - SectorIDs: preCommitAdded, - Event: PreCommitAdded, - } - } - var preCommitExpired []uint64 - for _, removed := range changes.Removed { - // TODO: we can optimize this to not load the AMT every time, if necessary. - si, err := m.state.GetSector(removed.Info.SectorNumber) - if err != nil { - return err - } - if si == nil { - preCommitExpired = append(preCommitExpired, uint64(removed.Info.SectorNumber)) - } - } - if len(preCommitExpired) > 0 { - sectorEvents <- &MinerSectorsEvent{ - MinerID: m.common.addr, - StateRoot: m.common.stateroot, - SectorIDs: preCommitExpired, - Event: PreCommitExpired, - } - } - return nil - }) - } - if err := grp.Wait(); err != nil { - return err - } - - if err := stmt.Close(); err != nil { - return xerrors.Errorf("Failed to close sector precommit info statement: %w", err) - } - - if _, err := tx.Exec(`insert into sector_precommit_info select * from spi on conflict do nothing`); err != nil { - return xerrors.Errorf("Failed to insert into sector precommit info table: %w", err) - } - - if err := tx.Commit(); err != nil { - return xerrors.Errorf("Failed to commit sector precommit info: %w", err) - } - return nil -} - -func (p *Processor) storeMinerSectorInfo(ctx context.Context, miners []minerActorInfo, events chan<- *MinerSectorsEvent) error { - tx, err := p.db.Begin() - if err != nil { - return err - } - - if _, err := tx.Exec(`create temp table si (like sector_info excluding constraints) on commit drop;`); err != nil { - return xerrors.Errorf("Failed to create temp table for sector_: %w", err) - } - - stmt, err := tx.Prepare(`copy si (miner_id, sector_id, sealed_cid, state_root, activation_epoch, expiration_epoch, deal_weight, verified_deal_weight, initial_pledge, expected_day_reward, expected_storage_pledge) from STDIN`) - if err != nil { - return xerrors.Errorf("Failed to prepare miner sector info statement: %w", err) - } - - grp, _ := errgroup.WithContext(ctx) - for _, m := range miners { - m := m - grp.Go(func() error { - changes, err := p.getMinerSectorChanges(ctx, m) - if err != nil { - if strings.Contains(err.Error(), types.ErrActorNotFound.Error()) { - return nil - } - return err - } - if changes == nil { - return nil - } - var sectorsAdded []uint64 - var ccAdded []uint64 - var extended []uint64 - for _, added := range changes.Added { - // add the sector to the table - if _, err := stmt.Exec( - m.common.addr.String(), - added.SectorNumber, - added.SealedCID.String(), - m.common.stateroot.String(), - added.Activation.String(), - added.Expiration.String(), - added.DealWeight.String(), - added.VerifiedDealWeight.String(), - added.InitialPledge.String(), - added.ExpectedDayReward.String(), - added.ExpectedStoragePledge.String(), - ); err != nil { - log.Errorw("writing miner sector changes statement", "error", err.Error()) - } - if len(added.DealIDs) == 0 { - ccAdded = append(ccAdded, uint64(added.SectorNumber)) - } else { - sectorsAdded = append(sectorsAdded, uint64(added.SectorNumber)) - } - } - - for _, mod := range changes.Extended { - extended = append(extended, uint64(mod.To.SectorNumber)) - } - - events <- &MinerSectorsEvent{ - MinerID: m.common.addr, - StateRoot: m.common.stateroot, - SectorIDs: ccAdded, - Event: CommitCapacityAdded, - } - events <- &MinerSectorsEvent{ - MinerID: m.common.addr, - StateRoot: m.common.stateroot, - SectorIDs: sectorsAdded, - Event: SectorAdded, - } - events <- &MinerSectorsEvent{ - MinerID: m.common.addr, - StateRoot: m.common.stateroot, - SectorIDs: extended, - Event: SectorExtended, - } - return nil - }) - } - - if err := grp.Wait(); err != nil { - return err - } - - if err := stmt.Close(); err != nil { - return xerrors.Errorf("Failed to close sector info statement: %w", err) - } - - if _, err := tx.Exec(`insert into sector_info select * from si on conflict do nothing`); err != nil { - return xerrors.Errorf("Failed to insert into sector info table: %w", err) - } - - if err := tx.Commit(); err != nil { - return xerrors.Errorf("Failed to commit sector info: %w", err) - } - return nil - -} - -func (p *Processor) getMinerPartitionsDifferences(ctx context.Context, miners []minerActorInfo, events chan<- *MinerSectorsEvent) error { - grp, ctx := errgroup.WithContext(ctx) - for _, m := range miners { - m := m - grp.Go(func() error { - if err := p.diffMinerPartitions(ctx, m, events); err != nil { - if strings.Contains(err.Error(), types.ErrActorNotFound.Error()) { - return nil - } - return err - } - return nil - }) - } - return grp.Wait() -} - -func (p *Processor) storeMinerSectorEvents(ctx context.Context, sectorEvents, preCommitEvents, partitionEvents <-chan *MinerSectorsEvent) error { - tx, err := p.db.Begin() - if err != nil { - return err - } - - if _, err := tx.Exec(`create temp table mse (like miner_sector_events excluding constraints) on commit drop;`); err != nil { - return xerrors.Errorf("Failed to create temp table for sector_: %w", err) - } - - stmt, err := tx.Prepare(`copy mse (miner_id, sector_id, event, state_root) from STDIN`) - if err != nil { - return xerrors.Errorf("Failed to prepare miner sector info statement: %w", err) - } - - grp, ctx := errgroup.WithContext(ctx) - grp.Go(func() error { - innerGrp, _ := errgroup.WithContext(ctx) - for mse := range sectorEvents { - mse := mse - innerGrp.Go(func() error { - for _, sid := range mse.SectorIDs { - if _, err := stmt.Exec( - mse.MinerID.String(), - sid, - mse.Event, - mse.StateRoot.String(), - ); err != nil { - return err - } - } - return nil - }) - } - return innerGrp.Wait() - }) - - grp.Go(func() error { - innerGrp, _ := errgroup.WithContext(ctx) - for mse := range preCommitEvents { - mse := mse - innerGrp.Go(func() error { - for _, sid := range mse.SectorIDs { - if _, err := stmt.Exec( - mse.MinerID.String(), - sid, - mse.Event, - mse.StateRoot.String(), - ); err != nil { - return err - } - } - return nil - }) - } - return innerGrp.Wait() - }) - - grp.Go(func() error { - innerGrp, _ := errgroup.WithContext(ctx) - for mse := range partitionEvents { - mse := mse - grp.Go(func() error { - for _, sid := range mse.SectorIDs { - if _, err := stmt.Exec( - mse.MinerID.String(), - sid, - mse.Event, - mse.StateRoot.String(), - ); err != nil { - return err - } - } - return nil - }) - } - return innerGrp.Wait() - }) - - if err := grp.Wait(); err != nil { - return err - } - - if err := stmt.Close(); err != nil { - return xerrors.Errorf("Failed to close sector event statement: %w", err) - } - - if _, err := tx.Exec(`insert into miner_sector_events select * from mse on conflict do nothing`); err != nil { - return xerrors.Errorf("Failed to insert into sector event table: %w", err) - } - - if err := tx.Commit(); err != nil { - return xerrors.Errorf("Failed to commit sector events: %w", err) - } - return nil -} - -func (p *Processor) getMinerStateAt(ctx context.Context, maddr address.Address, tskey types.TipSetKey) (miner.State, error) { - prevActor, err := p.node.StateGetActor(ctx, maddr, tskey) - if err != nil { - return nil, err - } - return miner.Load(store.ActorStore(ctx, blockstore.NewAPIBlockstore(p.node)), prevActor) -} - -func (p *Processor) getMinerPreCommitChanges(ctx context.Context, m minerActorInfo) (*miner.PreCommitChanges, error) { - pred := state.NewStatePredicates(p.node) - changed, val, err := pred.OnMinerActorChange(m.common.addr, pred.OnMinerPreCommitChange())(ctx, m.common.parentTsKey, m.common.tsKey) - if err != nil { - return nil, xerrors.Errorf("Failed to diff miner precommit amt: %w", err) - } - if !changed { - return nil, nil - } - out := val.(*miner.PreCommitChanges) - return out, nil -} - -func (p *Processor) getMinerSectorChanges(ctx context.Context, m minerActorInfo) (*miner.SectorChanges, error) { - pred := state.NewStatePredicates(p.node) - changed, val, err := pred.OnMinerActorChange(m.common.addr, pred.OnMinerSectorChange())(ctx, m.common.parentTsKey, m.common.tsKey) - if err != nil { - return nil, xerrors.Errorf("Failed to diff miner sectors amt: %w", err) - } - if !changed { - return nil, nil - } - out := val.(*miner.SectorChanges) - return out, nil -} - -func (p *Processor) diffMinerPartitions(ctx context.Context, m minerActorInfo, events chan<- *MinerSectorsEvent) error { - prevMiner, err := p.getMinerStateAt(ctx, m.common.addr, m.common.parentTsKey) - if err != nil { - return err - } - curMiner := m.state - dc, err := prevMiner.DeadlinesChanged(curMiner) - if err != nil { - return err - } - if !dc { - return nil - } - panic("TODO") - - // FIXME: This code doesn't work. - // 1. We need to diff all deadlines, not just the "current" deadline. - // 2. We need to handle the case where we _add_ a partition. (i.e., - // where len(newPartitions) != len(oldPartitions). - /* - - // NOTE: If we change the number of deadlines in an upgrade, this will - // break. - - // load the old deadline - prevDls, err := prevMiner.LoadDeadlines(p.ctxStore) - if err != nil { - return err - } - var prevDl miner.Deadline - if err := p.ctxStore.Get(ctx, prevDls.Due[dlIdx], &prevDl); err != nil { - return err - } - - prevPartitions, err := prevDl.PartitionsArray(p.ctxStore) - if err != nil { - return err - } - - // load the new deadline - curDls, err := curMiner.LoadDeadlines(p.ctxStore) - if err != nil { - return err - } - - var curDl miner.Deadline - if err := p.ctxStore.Get(ctx, curDls.Due[dlIdx], &curDl); err != nil { - return err - } - - curPartitions, err := curDl.PartitionsArray(p.ctxStore) - if err != nil { - return err - } - - // TODO this can be optimized by inspecting the miner state for partitions that have changed and only inspecting those. - var prevPart miner.Partition - if err := prevPartitions.ForEach(&prevPart, func(i int64) error { - var curPart miner.Partition - if found, err := curPartitions.Get(uint64(i), &curPart); err != nil { - return err - } else if !found { - log.Fatal("I don't know what this means, are partitions ever removed?") - } - partitionDiff, err := p.diffPartition(prevPart, curPart) - if err != nil { - return err - } - - recovered, err := partitionDiff.Recovered.All(miner.SectorsMax) - if err != nil { - return err - } - events <- &MinerSectorsEvent{ - MinerID: m.common.addr, - StateRoot: m.common.stateroot, - SectorIDs: recovered, - Event: SectorRecovered, - } - inRecovery, err := partitionDiff.InRecovery.All(miner.SectorsMax) - if err != nil { - return err - } - events <- &MinerSectorsEvent{ - MinerID: m.common.addr, - StateRoot: m.common.stateroot, - SectorIDs: inRecovery, - Event: SectorRecovering, - } - faulted, err := partitionDiff.Faulted.All(miner.SectorsMax) - if err != nil { - return err - } - events <- &MinerSectorsEvent{ - MinerID: m.common.addr, - StateRoot: m.common.stateroot, - SectorIDs: faulted, - Event: SectorFaulted, - } - terminated, err := partitionDiff.Terminated.All(miner.SectorsMax) - if err != nil { - return err - } - events <- &MinerSectorsEvent{ - MinerID: m.common.addr, - StateRoot: m.common.stateroot, - SectorIDs: terminated, - Event: SectorTerminated, - } - expired, err := partitionDiff.Expired.All(miner.SectorsMax) - if err != nil { - return err - } - events <- &MinerSectorsEvent{ - MinerID: m.common.addr, - StateRoot: m.common.stateroot, - SectorIDs: expired, - Event: SectorExpired, - } - - return nil - }); err != nil { - return err - } - - return nil - */ -} - -func (p *Processor) diffPartition(prevPart, curPart miner.Partition) (*PartitionStatus, error) { - prevLiveSectors, err := prevPart.LiveSectors() - if err != nil { - return nil, err - } - curLiveSectors, err := curPart.LiveSectors() - if err != nil { - return nil, err - } - - removedSectors, err := bitfield.SubtractBitField(prevLiveSectors, curLiveSectors) - if err != nil { - return nil, err - } - - prevRecoveries, err := prevPart.RecoveringSectors() - if err != nil { - return nil, err - } - - curRecoveries, err := curPart.RecoveringSectors() - if err != nil { - return nil, err - } - - newRecoveries, err := bitfield.SubtractBitField(curRecoveries, prevRecoveries) - if err != nil { - return nil, err - } - - prevFaults, err := prevPart.FaultySectors() - if err != nil { - return nil, err - } - - curFaults, err := curPart.FaultySectors() - if err != nil { - return nil, err - } - - newFaults, err := bitfield.SubtractBitField(curFaults, prevFaults) - if err != nil { - return nil, err - } - - // all current good sectors - curActiveSectors, err := curPart.ActiveSectors() - if err != nil { - return nil, err - } - - // sectors that were previously fault and are now currently active are considered recovered. - recovered, err := bitfield.IntersectBitField(prevFaults, curActiveSectors) - if err != nil { - return nil, err - } - - // TODO: distinguish between "terminated" and "expired" sectors. The - // previous code here never had a chance of working in the first place, - // so I'm not going to try to replicate it right now. - // - // How? If the sector expires before it should (according to sector - // info) and it wasn't replaced by a pre-commit deleted in this change - // set, it was "early terminated". - - return &PartitionStatus{ - Terminated: bitfield.New(), - Expired: removedSectors, - Faulted: newFaults, - InRecovery: newRecoveries, - Recovered: recovered, - }, nil -} - -func (p *Processor) storeMinersActorInfoState(ctx context.Context, miners []minerActorInfo) error { - start := time.Now() - defer func() { - log.Debugw("Stored Miners Actor State", "duration", time.Since(start).String()) - }() - - tx, err := p.db.Begin() - if err != nil { - return err - } - - if _, err := tx.Exec(`create temp table mi (like miner_info excluding constraints) on commit drop;`); err != nil { - return xerrors.Errorf("prep temp: %w", err) - } - - stmt, err := tx.Prepare(`copy mi (miner_id, owner_addr, worker_addr, peer_id, sector_size) from STDIN`) - if err != nil { - return err - } - for _, m := range miners { - mi, err := p.node.StateMinerInfo(ctx, m.common.addr, m.common.tsKey) - if err != nil { - if strings.Contains(err.Error(), types.ErrActorNotFound.Error()) { - continue - } else { - return err - } - } - var pid string - if mi.PeerId != nil { - pid = mi.PeerId.String() - } - if _, err := stmt.Exec( - m.common.addr.String(), - mi.Owner.String(), - mi.Worker.String(), - pid, - mi.SectorSize.ShortString(), - ); err != nil { - log.Errorw("failed to store miner state", "state", m.state, "info", m.state.Info, "error", err) - return xerrors.Errorf("failed to store miner state: %w", err) - } - - } - if err := stmt.Close(); err != nil { - return err - } - - if _, err := tx.Exec(`insert into miner_info select * from mi on conflict do nothing `); err != nil { - return xerrors.Errorf("actor put: %w", err) - } - - return tx.Commit() -} - -func (p *Processor) storePreCommitDealInfo(dealEvents <-chan *SectorDealEvent) error { - tx, err := p.db.Begin() - if err != nil { - return err - } - - if _, err := tx.Exec(`create temp table mds (like minerid_dealid_sectorid excluding constraints) on commit drop;`); err != nil { - return xerrors.Errorf("Failed to create temp table for minerid_dealid_sectorid: %w", err) - } - - stmt, err := tx.Prepare(`copy mds (deal_id, miner_id, sector_id) from STDIN`) - if err != nil { - return xerrors.Errorf("Failed to prepare minerid_dealid_sectorid statement: %w", err) - } - - for sde := range dealEvents { - for _, did := range sde.DealIDs { - if _, err := stmt.Exec( - uint64(did), - sde.MinerID.String(), - sde.SectorID, - ); err != nil { - return err - } - } - } - - if err := stmt.Close(); err != nil { - return xerrors.Errorf("Failed to close miner sector deals statement: %w", err) - } - - if _, err := tx.Exec(`insert into minerid_dealid_sectorid select * from mds on conflict do nothing`); err != nil { - return xerrors.Errorf("Failed to insert into miner deal sector table: %w", err) - } - - if err := tx.Commit(); err != nil { - return xerrors.Errorf("Failed to commit miner deal sector table: %w", err) - } - return nil - -} - -func (p *Processor) storeMinersPower(miners []minerActorInfo) error { - start := time.Now() - defer func() { - log.Debugw("Stored Miners Power", "duration", time.Since(start).String()) - }() - - tx, err := p.db.Begin() - if err != nil { - return xerrors.Errorf("begin miner_power tx: %w", err) - } - - if _, err := tx.Exec(`create temp table mp (like miner_power excluding constraints) on commit drop`); err != nil { - return xerrors.Errorf("prep miner_power temp: %w", err) - } - - stmt, err := tx.Prepare(`copy mp (miner_id, state_root, raw_bytes_power, quality_adjusted_power) from STDIN`) - if err != nil { - return xerrors.Errorf("prepare tmp miner_power: %w", err) - } - - for _, m := range miners { - if _, err := stmt.Exec( - m.common.addr.String(), - m.common.stateroot.String(), - m.rawPower.String(), - m.qalPower.String(), - ); err != nil { - log.Errorw("failed to store miner power", "miner", m.common.addr, "stateroot", m.common.stateroot, "error", err) - } - } - - if err := stmt.Close(); err != nil { - return xerrors.Errorf("close prepared miner_power: %w", err) - } - - if _, err := tx.Exec(`insert into miner_power select * from mp on conflict do nothing`); err != nil { - return xerrors.Errorf("insert miner_power from tmp: %w", err) - } - - if err := tx.Commit(); err != nil { - return xerrors.Errorf("commit miner_power tx: %w", err) - } - - return nil - -} - -// load the power actor state clam as an adt.Map at the tipset `ts`. -func getPowerActorState(ctx context.Context, api v0api.FullNode, ts types.TipSetKey) (power.State, error) { - powerActor, err := api.StateGetActor(ctx, power.Address, ts) - if err != nil { - return nil, err - } - return power.Load(cw_util.NewAPIIpldStore(ctx, api), powerActor) -} diff --git a/cmd/lotus-chainwatch/processor/mpool.go b/cmd/lotus-chainwatch/processor/mpool.go deleted file mode 100644 index 0a6445d78..000000000 --- a/cmd/lotus-chainwatch/processor/mpool.go +++ /dev/null @@ -1,100 +0,0 @@ -package processor - -import ( - "context" - "time" - - "golang.org/x/xerrors" - - "github.com/ipfs/go-cid" - - "github.com/filecoin-project/lotus/api" - "github.com/filecoin-project/lotus/chain/types" -) - -func (p *Processor) subMpool(ctx context.Context) { - sub, err := p.node.MpoolSub(ctx) - if err != nil { - return - } - - for { - var updates []api.MpoolUpdate - - select { - case update := <-sub: - updates = append(updates, update) - case <-ctx.Done(): - return - } - - loop: - for { - select { - case update := <-sub: - updates = append(updates, update) - case <-time.After(10 * time.Millisecond): - break loop - } - } - - msgs := map[cid.Cid]*types.Message{} - for _, v := range updates { - if v.Type != api.MpoolAdd { - continue - } - - msgs[v.Message.Message.Cid()] = &v.Message.Message - } - - err := p.storeMessages(msgs) - if err != nil { - log.Error(err) - } - - if err := p.storeMpoolInclusions(updates); err != nil { - log.Error(err) - } - } -} - -func (p *Processor) storeMpoolInclusions(msgs []api.MpoolUpdate) error { - tx, err := p.db.Begin() - if err != nil { - return err - } - - if _, err := tx.Exec(` - create temp table mi (like mpool_messages excluding constraints) on commit drop; - `); err != nil { - return xerrors.Errorf("prep temp: %w", err) - } - - stmt, err := tx.Prepare(`copy mi (msg, add_ts) from stdin `) - if err != nil { - return err - } - - for _, msg := range msgs { - if msg.Type != api.MpoolAdd { - continue - } - - if _, err := stmt.Exec( - msg.Message.Message.Cid().String(), - time.Now().Unix(), - ); err != nil { - return err - } - } - - if err := stmt.Close(); err != nil { - return err - } - - if _, err := tx.Exec(`insert into mpool_messages select * from mi on conflict do nothing `); err != nil { - return xerrors.Errorf("actor put: %w", err) - } - - return tx.Commit() -} diff --git a/cmd/lotus-chainwatch/processor/power.go b/cmd/lotus-chainwatch/processor/power.go deleted file mode 100644 index 726a46706..000000000 --- a/cmd/lotus-chainwatch/processor/power.go +++ /dev/null @@ -1,190 +0,0 @@ -package processor - -import ( - "context" - "time" - - "golang.org/x/xerrors" - - "github.com/filecoin-project/go-state-types/big" - - "github.com/filecoin-project/lotus/chain/actors/builtin" -) - -type powerActorInfo struct { - common actorInfo - - totalRawBytes big.Int - totalRawBytesCommitted big.Int - totalQualityAdjustedBytes big.Int - totalQualityAdjustedBytesCommitted big.Int - totalPledgeCollateral big.Int - - qaPowerSmoothed builtin.FilterEstimate - - minerCount int64 - minerCountAboveMinimumPower int64 -} - -func (p *Processor) setupPower() error { - tx, err := p.db.Begin() - if err != nil { - return err - } - - if _, err := tx.Exec(` -create table if not exists chain_power -( - state_root text not null - constraint power_smoothing_estimates_pk - primary key, - - total_raw_bytes_power text not null, - total_raw_bytes_committed text not null, - total_qa_bytes_power text not null, - total_qa_bytes_committed text not null, - total_pledge_collateral text not null, - - qa_smoothed_position_estimate text not null, - qa_smoothed_velocity_estimate text not null, - - miner_count int not null, - minimum_consensus_miner_count int not null -); -`); err != nil { - return err - } - - return tx.Commit() -} - -func (p *Processor) HandlePowerChanges(ctx context.Context, powerTips ActorTips) error { - powerChanges, err := p.processPowerActors(ctx, powerTips) - if err != nil { - return xerrors.Errorf("Failed to process power actors: %w", err) - } - - if err := p.persistPowerActors(ctx, powerChanges); err != nil { - return err - } - - return nil -} - -func (p *Processor) processPowerActors(ctx context.Context, powerTips ActorTips) ([]powerActorInfo, error) { - start := time.Now() - defer func() { - log.Debugw("Processed Power Actors", "duration", time.Since(start).String()) - }() - - var out []powerActorInfo - for tipset, powerStates := range powerTips { - for _, act := range powerStates { - var pw powerActorInfo - pw.common = act - - powerActorState, err := getPowerActorState(ctx, p.node, tipset) - if err != nil { - return nil, xerrors.Errorf("get power state (@ %s): %w", pw.common.stateroot.String(), err) - } - - totalPower, err := powerActorState.TotalPower() - if err != nil { - return nil, xerrors.Errorf("failed to compute total power: %w", err) - } - - totalCommitted, err := powerActorState.TotalCommitted() - if err != nil { - return nil, xerrors.Errorf("failed to compute total committed: %w", err) - } - - totalLocked, err := powerActorState.TotalLocked() - if err != nil { - return nil, xerrors.Errorf("failed to compute total locked: %w", err) - } - - powerSmoothed, err := powerActorState.TotalPowerSmoothed() - if err != nil { - return nil, xerrors.Errorf("failed to determine smoothed power: %w", err) - } - - // NOTE: this doesn't set new* fields. Previously, we - // filled these using ThisEpoch* fields from the actor - // state, but these fields are effectively internal - // state and don't represent "new" power, as was - // assumed. - - participatingMiners, totalMiners, err := powerActorState.MinerCounts() - if err != nil { - return nil, xerrors.Errorf("failed to count miners: %w", err) - } - - pw.totalRawBytes = totalPower.RawBytePower - pw.totalQualityAdjustedBytes = totalPower.QualityAdjPower - pw.totalRawBytesCommitted = totalCommitted.RawBytePower - pw.totalQualityAdjustedBytesCommitted = totalCommitted.QualityAdjPower - pw.totalPledgeCollateral = totalLocked - pw.qaPowerSmoothed = powerSmoothed - pw.minerCountAboveMinimumPower = int64(participatingMiners) - pw.minerCount = int64(totalMiners) - } - } - - return out, nil -} - -func (p *Processor) persistPowerActors(ctx context.Context, powerStates []powerActorInfo) error { - // NB: use errgroup when there is more than a single store operation - return p.storePowerSmoothingEstimates(powerStates) -} - -func (p *Processor) storePowerSmoothingEstimates(powerStates []powerActorInfo) error { - tx, err := p.db.Begin() - if err != nil { - return xerrors.Errorf("begin chain_power tx: %w", err) - } - - if _, err := tx.Exec(`create temp table cp (like chain_power) on commit drop`); err != nil { - return xerrors.Errorf("prep chain_power: %w", err) - } - - stmt, err := tx.Prepare(`copy cp (state_root, total_raw_bytes_power, total_raw_bytes_committed, total_qa_bytes_power, total_qa_bytes_committed, total_pledge_collateral, qa_smoothed_position_estimate, qa_smoothed_velocity_estimate, miner_count, minimum_consensus_miner_count) from stdin;`) - if err != nil { - return xerrors.Errorf("prepare tmp chain_power: %w", err) - } - - for _, ps := range powerStates { - if _, err := stmt.Exec( - ps.common.stateroot.String(), - - ps.totalRawBytes.String(), - ps.totalRawBytesCommitted.String(), - ps.totalQualityAdjustedBytes.String(), - ps.totalQualityAdjustedBytesCommitted.String(), - ps.totalPledgeCollateral.String(), - - ps.qaPowerSmoothed.PositionEstimate.String(), - ps.qaPowerSmoothed.VelocityEstimate.String(), - - ps.minerCount, - ps.minerCountAboveMinimumPower, - ); err != nil { - return xerrors.Errorf("failed to store smoothing estimate: %w", err) - } - } - - if err := stmt.Close(); err != nil { - return xerrors.Errorf("close prepared chain_power: %w", err) - } - - if _, err := tx.Exec(`insert into chain_power select * from cp on conflict do nothing`); err != nil { - return xerrors.Errorf("insert chain_power from tmp: %w", err) - } - - if err := tx.Commit(); err != nil { - return xerrors.Errorf("commit chain_power tx: %w", err) - } - - return nil - -} diff --git a/cmd/lotus-chainwatch/processor/processor.go b/cmd/lotus-chainwatch/processor/processor.go deleted file mode 100644 index af5935d47..000000000 --- a/cmd/lotus-chainwatch/processor/processor.go +++ /dev/null @@ -1,420 +0,0 @@ -package processor - -import ( - "context" - "database/sql" - "encoding/json" - "math" - "sync" - "time" - - "golang.org/x/xerrors" - - "github.com/filecoin-project/go-address" - "github.com/ipfs/go-cid" - logging "github.com/ipfs/go-log/v2" - - "github.com/filecoin-project/go-state-types/abi" - builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" - - "github.com/filecoin-project/lotus/api/v0api" - "github.com/filecoin-project/lotus/chain/types" - cw_util "github.com/filecoin-project/lotus/cmd/lotus-chainwatch/util" - "github.com/filecoin-project/lotus/lib/parmap" -) - -var log = logging.Logger("processor") - -type Processor struct { - db *sql.DB - - node v0api.FullNode - ctxStore *cw_util.APIIpldStore - - genesisTs *types.TipSet - - // number of blocks processed at a time - batch int -} - -type ActorTips map[types.TipSetKey][]actorInfo - -type actorInfo struct { - act types.Actor - - stateroot cid.Cid - height abi.ChainEpoch // so that we can walk the actor changes in chronological order. - - tsKey types.TipSetKey - parentTsKey types.TipSetKey - - addr address.Address - state string -} - -func NewProcessor(ctx context.Context, db *sql.DB, node v0api.FullNode, batch int) *Processor { - ctxStore := cw_util.NewAPIIpldStore(ctx, node) - return &Processor{ - db: db, - ctxStore: ctxStore, - node: node, - batch: batch, - } -} - -func (p *Processor) setupSchemas() error { - // maintain order, subsequent calls create tables with foreign keys. - if err := p.setupMiners(); err != nil { - return err - } - - if err := p.setupMarket(); err != nil { - return err - } - - if err := p.setupRewards(); err != nil { - return err - } - - if err := p.setupMessages(); err != nil { - return err - } - - if err := p.setupCommonActors(); err != nil { - return err - } - - if err := p.setupPower(); err != nil { - return err - } - - return nil -} - -func (p *Processor) Start(ctx context.Context) { - log.Debug("Starting Processor") - - if err := p.setupSchemas(); err != nil { - log.Fatalw("Failed to setup processor", "error", err) - } - - var err error - p.genesisTs, err = p.node.ChainGetGenesis(ctx) - if err != nil { - log.Fatalw("Failed to get genesis state from lotus", "error", err.Error()) - } - - go p.subMpool(ctx) - - // main processor loop - go func() { - for { - select { - case <-ctx.Done(): - log.Info("Stopping Processor...") - return - default: - loopStart := time.Now() - toProcess, err := p.unprocessedBlocks(ctx, p.batch) - if err != nil { - log.Fatalw("Failed to get unprocessed blocks", "error", err) - } - - if len(toProcess) == 0 { - log.Info("No unprocessed blocks. Wait then try again...") - time.Sleep(time.Second * 30) - continue - } - - // TODO special case genesis state handling here to avoid all the special cases that will be needed for it else where - // before doing "normal" processing. - - actorChanges, nullRounds, err := p.collectActorChanges(ctx, toProcess) - if err != nil { - log.Fatalw("Failed to collect actor changes", "error", err) - } - log.Infow("Collected Actor Changes", - "MarketChanges", len(actorChanges[builtin2.StorageMarketActorCodeID]), - "MinerChanges", len(actorChanges[builtin2.StorageMinerActorCodeID]), - "RewardChanges", len(actorChanges[builtin2.RewardActorCodeID]), - "AccountChanges", len(actorChanges[builtin2.AccountActorCodeID]), - "nullRounds", len(nullRounds)) - - grp := sync.WaitGroup{} - - grp.Add(1) - go func() { - defer grp.Done() - if err := p.HandleMarketChanges(ctx, actorChanges[builtin2.StorageMarketActorCodeID]); err != nil { - log.Errorf("Failed to handle market changes: %v", err) - return - } - }() - - grp.Add(1) - go func() { - defer grp.Done() - if err := p.HandleMinerChanges(ctx, actorChanges[builtin2.StorageMinerActorCodeID]); err != nil { - log.Errorf("Failed to handle miner changes: %v", err) - return - } - }() - - grp.Add(1) - go func() { - defer grp.Done() - if err := p.HandleRewardChanges(ctx, actorChanges[builtin2.RewardActorCodeID], nullRounds); err != nil { - log.Errorf("Failed to handle reward changes: %v", err) - return - } - }() - - grp.Add(1) - go func() { - defer grp.Done() - if err := p.HandlePowerChanges(ctx, actorChanges[builtin2.StoragePowerActorCodeID]); err != nil { - log.Errorf("Failed to handle power actor changes: %v", err) - return - } - }() - - grp.Add(1) - go func() { - defer grp.Done() - if err := p.HandleMessageChanges(ctx, toProcess); err != nil { - log.Errorf("Failed to handle message changes: %v", err) - return - } - }() - - grp.Add(1) - go func() { - defer grp.Done() - if err := p.HandleCommonActorsChanges(ctx, actorChanges); err != nil { - log.Errorf("Failed to handle common actor changes: %v", err) - return - } - }() - - grp.Wait() - - if err := p.markBlocksProcessed(ctx, toProcess); err != nil { - log.Fatalw("Failed to mark blocks as processed", "error", err) - } - - if err := p.refreshViews(); err != nil { - log.Errorw("Failed to refresh views", "error", err) - } - log.Infow("Processed Batch Complete", "duration", time.Since(loopStart).String()) - } - } - }() - -} - -func (p *Processor) refreshViews() error { - if _, err := p.db.Exec(`refresh materialized view state_heights`); err != nil { - return err - } - - return nil -} - -func (p *Processor) collectActorChanges(ctx context.Context, toProcess map[cid.Cid]*types.BlockHeader) (map[cid.Cid]ActorTips, []types.TipSetKey, error) { - start := time.Now() - defer func() { - log.Debugw("Collected Actor Changes", "duration", time.Since(start).String()) - }() - // ActorCode - > tipset->[]actorInfo - out := map[cid.Cid]ActorTips{} - var outMu sync.Mutex - - // map of addresses to changed actors - var changes map[string]types.Actor - actorsSeen := map[cid.Cid]struct{}{} - - var nullRounds []types.TipSetKey - var nullBlkMu sync.Mutex - - // collect all actor state that has changes between block headers - paDone := 0 - parmap.Par(50, parmap.MapArr(toProcess), func(bh *types.BlockHeader) { - paDone++ - if paDone%100 == 0 { - log.Debugw("Collecting actor changes", "done", paDone, "percent", (paDone*100)/len(toProcess)) - } - - pts, err := p.node.ChainGetTipSet(ctx, types.NewTipSetKey(bh.Parents...)) - if err != nil { - log.Error(err) - return - } - - if pts.ParentState().Equals(bh.ParentStateRoot) { - nullBlkMu.Lock() - nullRounds = append(nullRounds, pts.Key()) - nullBlkMu.Unlock() - } - - // collect all actors that had state changes between the blockheader parent-state and its grandparent-state. - // TODO: changes will contain deleted actors, this causes needless processing further down the pipeline, consider - // a separate strategy for deleted actors - changes, err = p.node.StateChangedActors(ctx, pts.ParentState(), bh.ParentStateRoot) - if err != nil { - log.Error(err) - log.Debugw("StateChangedActors", "grandparent_state", pts.ParentState(), "parent_state", bh.ParentStateRoot) - return - } - - // record the state of all actors that have changed - for a, act := range changes { - act := act - a := a - - // ignore actors that were deleted. - has, err := p.node.ChainHasObj(ctx, act.Head) - if err != nil { - log.Error(err) - log.Debugw("ChanHasObj", "actor_head", act.Head) - return - } - if !has { - continue - } - - addr, err := address.NewFromString(a) - if err != nil { - log.Error(err) - log.Debugw("NewFromString", "address_string", a) - return - } - - ast, err := p.node.StateReadState(ctx, addr, pts.Key()) - if err != nil { - log.Error(err) - log.Debugw("StateReadState", "address_string", a, "parent_tipset_key", pts.Key()) - return - } - - // TODO look here for an empty state, maybe thats a sign the actor was deleted? - - state, err := json.Marshal(ast.State) - if err != nil { - log.Error(err) - return - } - - outMu.Lock() - if _, ok := actorsSeen[act.Head]; !ok { - _, ok := out[act.Code] - if !ok { - out[act.Code] = map[types.TipSetKey][]actorInfo{} - } - out[act.Code][pts.Key()] = append(out[act.Code][pts.Key()], actorInfo{ - act: act, - stateroot: bh.ParentStateRoot, - height: bh.Height, - tsKey: pts.Key(), - parentTsKey: pts.Parents(), - addr: addr, - state: string(state), - }) - } - actorsSeen[act.Head] = struct{}{} - outMu.Unlock() - } - }) - return out, nullRounds, nil -} - -func (p *Processor) unprocessedBlocks(ctx context.Context, batch int) (map[cid.Cid]*types.BlockHeader, error) { - start := time.Now() - defer func() { - log.Debugw("Gathered Blocks to process", "duration", time.Since(start).String()) - }() - rows, err := p.db.Query(` -with toProcess as ( - select b.cid, b.height, rank() over (order by height) as rnk - from blocks_synced bs - left join blocks b on bs.cid = b.cid - where bs.processed_at is null and b.height > 0 -) -select cid -from toProcess -where rnk <= $1 -`, batch) - if err != nil { - return nil, xerrors.Errorf("Failed to query for unprocessed blocks: %w", err) - } - out := map[cid.Cid]*types.BlockHeader{} - - minBlock := abi.ChainEpoch(math.MaxInt64) - maxBlock := abi.ChainEpoch(0) - // TODO consider parallel execution here for getting the blocks from the api as is done in fetchMessages() - for rows.Next() { - if rows.Err() != nil { - return nil, err - } - var c string - if err := rows.Scan(&c); err != nil { - log.Errorf("Failed to scan unprocessed blocks: %s", err.Error()) - continue - } - ci, err := cid.Parse(c) - if err != nil { - log.Errorf("Failed to parse unprocessed blocks: %s", err.Error()) - continue - } - bh, err := p.node.ChainGetBlock(ctx, ci) - if err != nil { - // this is a pretty serious issue. - log.Errorf("Failed to get block header %s: %s", ci.String(), err.Error()) - continue - } - out[ci] = bh - if bh.Height < minBlock { - minBlock = bh.Height - } - if bh.Height > maxBlock { - maxBlock = bh.Height - } - } - if minBlock <= maxBlock { - log.Infow("Gathered Blocks to Process", "start", minBlock, "end", maxBlock) - } - return out, rows.Close() -} - -func (p *Processor) markBlocksProcessed(ctx context.Context, processed map[cid.Cid]*types.BlockHeader) error { - start := time.Now() - processedHeight := abi.ChainEpoch(0) - defer func() { - log.Debugw("Marked blocks as Processed", "duration", time.Since(start).String()) - log.Infow("Processed Blocks", "height", processedHeight) - }() - tx, err := p.db.Begin() - if err != nil { - return err - } - - processedAt := time.Now().Unix() - stmt, err := tx.Prepare(`update blocks_synced set processed_at=$1 where cid=$2`) - if err != nil { - return err - } - - for c, bh := range processed { - if bh.Height > processedHeight { - processedHeight = bh.Height - } - if _, err := stmt.Exec(processedAt, c.String()); err != nil { - return err - } - } - - if err := stmt.Close(); err != nil { - return err - } - - return tx.Commit() -} diff --git a/cmd/lotus-chainwatch/processor/reward.go b/cmd/lotus-chainwatch/processor/reward.go deleted file mode 100644 index 72a329c87..000000000 --- a/cmd/lotus-chainwatch/processor/reward.go +++ /dev/null @@ -1,234 +0,0 @@ -package processor - -import ( - "context" - "time" - - "golang.org/x/xerrors" - - "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/go-state-types/big" - - "github.com/filecoin-project/lotus/chain/actors/builtin" - "github.com/filecoin-project/lotus/chain/actors/builtin/reward" - "github.com/filecoin-project/lotus/chain/types" - - cw_util "github.com/filecoin-project/lotus/cmd/lotus-chainwatch/util" -) - -type rewardActorInfo struct { - common actorInfo - - cumSumBaselinePower big.Int - cumSumRealizedPower big.Int - - effectiveNetworkTime abi.ChainEpoch - effectiveBaselinePower big.Int - - // NOTE: These variables are wrong. Talk to @ZX about fixing. These _do - // not_ represent "new" anything. - newBaselinePower big.Int - newBaseReward big.Int - newSmoothingEstimate builtin.FilterEstimate - - totalMinedReward big.Int -} - -func (rw *rewardActorInfo) set(s reward.State) (err error) { - rw.cumSumBaselinePower, err = s.CumsumBaseline() - if err != nil { - return xerrors.Errorf("getting cumsum baseline power (@ %s): %w", rw.common.stateroot.String(), err) - } - - rw.cumSumRealizedPower, err = s.CumsumRealized() - if err != nil { - return xerrors.Errorf("getting cumsum realized power (@ %s): %w", rw.common.stateroot.String(), err) - } - - rw.effectiveNetworkTime, err = s.EffectiveNetworkTime() - if err != nil { - return xerrors.Errorf("getting effective network time (@ %s): %w", rw.common.stateroot.String(), err) - } - - rw.effectiveBaselinePower, err = s.EffectiveBaselinePower() - if err != nil { - return xerrors.Errorf("getting effective baseline power (@ %s): %w", rw.common.stateroot.String(), err) - } - - rw.totalMinedReward, err = s.TotalStoragePowerReward() - if err != nil { - return xerrors.Errorf("getting total mined (@ %s): %w", rw.common.stateroot.String(), err) - } - - rw.newBaselinePower, err = s.ThisEpochBaselinePower() - if err != nil { - return xerrors.Errorf("getting this epoch baseline power (@ %s): %w", rw.common.stateroot.String(), err) - } - - rw.newBaseReward, err = s.ThisEpochReward() - if err != nil { - return xerrors.Errorf("getting this epoch baseline power (@ %s): %w", rw.common.stateroot.String(), err) - } - - rw.newSmoothingEstimate, err = s.ThisEpochRewardSmoothed() - if err != nil { - return xerrors.Errorf("getting this epoch baseline power (@ %s): %w", rw.common.stateroot.String(), err) - } - return nil -} - -func (p *Processor) setupRewards() error { - tx, err := p.db.Begin() - if err != nil { - return err - } - - if _, err := tx.Exec(` -/* captures chain-specific power state for any given stateroot */ -create table if not exists chain_reward -( - state_root text not null - constraint chain_reward_pk - primary key, - cum_sum_baseline text not null, - cum_sum_realized text not null, - effective_network_time int not null, - effective_baseline_power text not null, - - new_baseline_power text not null, - new_reward numeric not null, - new_reward_smoothed_position_estimate text not null, - new_reward_smoothed_velocity_estimate text not null, - - total_mined_reward text not null -); -`); err != nil { - return err - } - - return tx.Commit() -} - -func (p *Processor) HandleRewardChanges(ctx context.Context, rewardTips ActorTips, nullRounds []types.TipSetKey) error { - rewardChanges, err := p.processRewardActors(ctx, rewardTips, nullRounds) - if err != nil { - return xerrors.Errorf("Failed to process reward actors: %w", err) - } - - if err := p.persistRewardActors(ctx, rewardChanges); err != nil { - return err - } - - return nil -} - -func (p *Processor) processRewardActors(ctx context.Context, rewardTips ActorTips, nullRounds []types.TipSetKey) ([]rewardActorInfo, error) { - start := time.Now() - defer func() { - log.Debugw("Processed Reward Actors", "duration", time.Since(start).String()) - }() - - var out []rewardActorInfo - for tipset, rewards := range rewardTips { - for _, act := range rewards { - var rw rewardActorInfo - rw.common = act - - // get reward actor states at each tipset once for all updates - rewardActor, err := p.node.StateGetActor(ctx, reward.Address, tipset) - if err != nil { - return nil, xerrors.Errorf("get reward state (@ %s): %w", rw.common.stateroot.String(), err) - } - - rewardActorState, err := reward.Load(cw_util.NewAPIIpldStore(ctx, p.node), rewardActor) - if err != nil { - return nil, xerrors.Errorf("read state obj (@ %s): %w", rw.common.stateroot.String(), err) - } - if err := rw.set(rewardActorState); err != nil { - return nil, err - } - - out = append(out, rw) - } - } - for _, tsKey := range nullRounds { - var rw rewardActorInfo - tipset, err := p.node.ChainGetTipSet(ctx, tsKey) - if err != nil { - return nil, err - } - rw.common.tsKey = tipset.Key() - rw.common.height = tipset.Height() - rw.common.stateroot = tipset.ParentState() - rw.common.parentTsKey = tipset.Parents() - // get reward actor states at each tipset once for all updates - rewardActor, err := p.node.StateGetActor(ctx, reward.Address, tsKey) - if err != nil { - return nil, err - } - - rewardActorState, err := reward.Load(cw_util.NewAPIIpldStore(ctx, p.node), rewardActor) - if err != nil { - return nil, xerrors.Errorf("read state obj (@ %s): %w", rw.common.stateroot.String(), err) - } - - if err := rw.set(rewardActorState); err != nil { - return nil, err - } - out = append(out, rw) - } - - return out, nil -} - -func (p *Processor) persistRewardActors(ctx context.Context, rewards []rewardActorInfo) error { - start := time.Now() - defer func() { - log.Debugw("Persisted Reward Actors", "duration", time.Since(start).String()) - }() - - tx, err := p.db.Begin() - if err != nil { - return xerrors.Errorf("begin chain_reward tx: %w", err) - } - - if _, err := tx.Exec(`create temp table cr (like chain_reward excluding constraints) on commit drop`); err != nil { - return xerrors.Errorf("prep chain_reward temp: %w", err) - } - - stmt, err := tx.Prepare(`copy cr ( state_root, cum_sum_baseline, cum_sum_realized, effective_network_time, effective_baseline_power, new_baseline_power, new_reward, new_reward_smoothed_position_estimate, new_reward_smoothed_velocity_estimate, total_mined_reward) from STDIN`) - if err != nil { - return xerrors.Errorf("prepare tmp chain_reward: %w", err) - } - - for _, rewardState := range rewards { - if _, err := stmt.Exec( - rewardState.common.stateroot.String(), - rewardState.cumSumBaselinePower.String(), - rewardState.cumSumRealizedPower.String(), - uint64(rewardState.effectiveNetworkTime), - rewardState.effectiveBaselinePower.String(), - rewardState.newBaselinePower.String(), - rewardState.newBaseReward.String(), - rewardState.newSmoothingEstimate.PositionEstimate.String(), - rewardState.newSmoothingEstimate.VelocityEstimate.String(), - rewardState.totalMinedReward.String(), - ); err != nil { - log.Errorw("failed to store chain power", "state_root", rewardState.common.stateroot, "error", err) - } - } - - if err := stmt.Close(); err != nil { - return xerrors.Errorf("close prepared chain_reward: %w", err) - } - - if _, err := tx.Exec(`insert into chain_reward select * from cr on conflict do nothing`); err != nil { - return xerrors.Errorf("insert chain_reward from tmp: %w", err) - } - - if err := tx.Commit(); err != nil { - return xerrors.Errorf("commit chain_reward tx: %w", err) - } - - return nil -} diff --git a/cmd/lotus-chainwatch/run.go b/cmd/lotus-chainwatch/run.go deleted file mode 100644 index 6e47a100d..000000000 --- a/cmd/lotus-chainwatch/run.go +++ /dev/null @@ -1,107 +0,0 @@ -package main - -import ( - "database/sql" - "fmt" - "net/http" - _ "net/http/pprof" - "os" - "strings" - - "github.com/filecoin-project/lotus/api/v0api" - - _ "github.com/lib/pq" - - "github.com/filecoin-project/go-jsonrpc" - logging "github.com/ipfs/go-log/v2" - "github.com/urfave/cli/v2" - "golang.org/x/xerrors" - - lcli "github.com/filecoin-project/lotus/cli" - "github.com/filecoin-project/lotus/cmd/lotus-chainwatch/processor" - "github.com/filecoin-project/lotus/cmd/lotus-chainwatch/scheduler" - "github.com/filecoin-project/lotus/cmd/lotus-chainwatch/syncer" - "github.com/filecoin-project/lotus/cmd/lotus-chainwatch/util" -) - -var runCmd = &cli.Command{ - Name: "run", - Usage: "Start lotus chainwatch", - Flags: []cli.Flag{ - &cli.IntFlag{ - Name: "max-batch", - Value: 50, - }, - }, - Action: func(cctx *cli.Context) error { - go func() { - http.ListenAndServe(":6060", nil) //nolint:errcheck - }() - ll := cctx.String("log-level") - if err := logging.SetLogLevel("*", ll); err != nil { - return err - } - if err := logging.SetLogLevel("rpc", "error"); err != nil { - return err - } - - var api v0api.FullNode - var closer jsonrpc.ClientCloser - var err error - if tokenMaddr := cctx.String("api"); tokenMaddr != "" { - toks := strings.Split(tokenMaddr, ":") - if len(toks) != 2 { - return fmt.Errorf("invalid api tokens, expected :, got: %s", tokenMaddr) - } - - api, closer, err = util.GetFullNodeAPIUsingCredentials(cctx.Context, toks[1], toks[0]) - if err != nil { - return err - } - } else { - api, closer, err = lcli.GetFullNodeAPI(cctx) - if err != nil { - return err - } - } - defer closer() - ctx := lcli.ReqContext(cctx) - - v, err := api.Version(ctx) - if err != nil { - return err - } - - log.Infof("Remote version: %s", v.Version) - - maxBatch := cctx.Int("max-batch") - - db, err := sql.Open("postgres", cctx.String("db")) - if err != nil { - return err - } - defer func() { - if err := db.Close(); err != nil { - log.Errorw("Failed to close database", "error", err) - } - }() - - if err := db.Ping(); err != nil { - return xerrors.Errorf("Database failed to respond to ping (is it online?): %w", err) - } - db.SetMaxOpenConns(1350) - - sync := syncer.NewSyncer(db, api, 1400) - sync.Start(ctx) - - proc := processor.NewProcessor(ctx, db, api, maxBatch) - proc.Start(ctx) - - sched := scheduler.PrepareScheduler(db) - sched.Start(ctx) - - <-ctx.Done() - os.Exit(0) - return nil - }, -} diff --git a/cmd/lotus-chainwatch/scheduler/refresh_top_miners_by_base_reward.go b/cmd/lotus-chainwatch/scheduler/refresh_top_miners_by_base_reward.go deleted file mode 100644 index 145e84229..000000000 --- a/cmd/lotus-chainwatch/scheduler/refresh_top_miners_by_base_reward.go +++ /dev/null @@ -1,78 +0,0 @@ -package scheduler - -import ( - "context" - "database/sql" - - "golang.org/x/xerrors" -) - -func setupTopMinerByBaseRewardSchema(ctx context.Context, db *sql.DB) error { - select { - case <-ctx.Done(): - return nil - default: - } - - tx, err := db.Begin() - if err != nil { - return err - } - if _, err := tx.Exec(` - create materialized view if not exists top_miners_by_base_reward as - with total_rewards_by_miner as ( - select - b.miner, - sum(cr.new_reward * b.win_count) as total_reward - from blocks b - inner join chain_reward cr on b.parentstateroot = cr.state_root - group by 1 - ) select - rank() over (order by total_reward desc), - miner, - total_reward - from total_rewards_by_miner - group by 2, 3; - - create index if not exists top_miners_by_base_reward_miner_index - on top_miners_by_base_reward (miner); - - create materialized view if not exists top_miners_by_base_reward_max_height as - select - b."timestamp"as current_timestamp, - max(b.height) as current_height - from blocks b - join chain_reward cr on b.parentstateroot = cr.state_root - where cr.new_reward is not null - group by 1 - order by 1 desc - limit 1; - `); err != nil { - return xerrors.Errorf("create top_miners_by_base_reward views: %w", err) - } - - if err := tx.Commit(); err != nil { - return xerrors.Errorf("committing top_miners_by_base_reward views; %w", err) - } - return nil -} - -func refreshTopMinerByBaseReward(ctx context.Context, db *sql.DB) error { - select { - case <-ctx.Done(): - return nil - default: - } - - _, err := db.Exec("refresh materialized view top_miners_by_base_reward;") - if err != nil { - return xerrors.Errorf("refresh top_miners_by_base_reward: %w", err) - } - - _, err = db.Exec("refresh materialized view top_miners_by_base_reward_max_height;") - if err != nil { - return xerrors.Errorf("refresh top_miners_by_base_reward_max_height: %w", err) - } - - return nil -} diff --git a/cmd/lotus-chainwatch/scheduler/scheduler.go b/cmd/lotus-chainwatch/scheduler/scheduler.go deleted file mode 100644 index 6782bc16d..000000000 --- a/cmd/lotus-chainwatch/scheduler/scheduler.go +++ /dev/null @@ -1,60 +0,0 @@ -package scheduler - -import ( - "context" - "database/sql" - "time" - - logging "github.com/ipfs/go-log/v2" - - "golang.org/x/xerrors" -) - -var log = logging.Logger("scheduler") - -// Scheduler manages the execution of jobs triggered -// by tickers. Not externally configurable at runtime. -type Scheduler struct { - db *sql.DB -} - -// PrepareScheduler returns a ready-to-run Scheduler -func PrepareScheduler(db *sql.DB) *Scheduler { - return &Scheduler{db} -} - -func (s *Scheduler) setupSchema(ctx context.Context) error { - if err := setupTopMinerByBaseRewardSchema(ctx, s.db); err != nil { - return xerrors.Errorf("setup top miners by reward schema: %w", err) - } - return nil -} - -// Start the scheduler jobs at the defined intervals -func (s *Scheduler) Start(ctx context.Context) { - log.Debug("Starting Scheduler") - - if err := s.setupSchema(ctx); err != nil { - log.Fatalw("applying scheduling schema", "error", err) - } - - go func() { - // run once on start after schema has initialized - time.Sleep(1 * time.Minute) - if err := refreshTopMinerByBaseReward(ctx, s.db); err != nil { - log.Errorw("failed to refresh top miner", "error", err) - } - refreshTopMinerCh := time.NewTicker(30 * time.Second) - defer refreshTopMinerCh.Stop() - for { - select { - case <-refreshTopMinerCh.C: - if err := refreshTopMinerByBaseReward(ctx, s.db); err != nil { - log.Errorw("failed to refresh top miner", "error", err) - } - case <-ctx.Done(): - return - } - } - }() -} diff --git a/cmd/lotus-chainwatch/syncer/blockssub.go b/cmd/lotus-chainwatch/syncer/blockssub.go deleted file mode 100644 index ea9c079e8..000000000 --- a/cmd/lotus-chainwatch/syncer/blockssub.go +++ /dev/null @@ -1,27 +0,0 @@ -package syncer - -import ( - "context" - "time" - - "github.com/filecoin-project/lotus/chain/types" - "github.com/ipfs/go-cid" -) - -func (s *Syncer) subBlocks(ctx context.Context) { - sub, err := s.node.SyncIncomingBlocks(ctx) - if err != nil { - log.Errorf("opening incoming block channel: %+v", err) - return - } - - log.Infow("Capturing incoming blocks") - for bh := range sub { - err := s.storeHeaders(map[cid.Cid]*types.BlockHeader{ - bh.Cid(): bh, - }, false, time.Now()) - if err != nil { - log.Errorf("storing incoming block header: %+v", err) - } - } -} diff --git a/cmd/lotus-chainwatch/syncer/sync.go b/cmd/lotus-chainwatch/syncer/sync.go deleted file mode 100644 index b5e9c73d6..000000000 --- a/cmd/lotus-chainwatch/syncer/sync.go +++ /dev/null @@ -1,527 +0,0 @@ -package syncer - -import ( - "container/list" - "context" - "database/sql" - "fmt" - "sync" - "time" - - "golang.org/x/xerrors" - - "github.com/ipfs/go-cid" - logging "github.com/ipfs/go-log/v2" - - "github.com/filecoin-project/lotus/api/v0api" - "github.com/filecoin-project/lotus/chain/store" - "github.com/filecoin-project/lotus/chain/types" -) - -var log = logging.Logger("syncer") - -type Syncer struct { - db *sql.DB - - lookbackLimit uint64 - - headerLk sync.Mutex - node v0api.FullNode -} - -func NewSyncer(db *sql.DB, node v0api.FullNode, lookbackLimit uint64) *Syncer { - return &Syncer{ - db: db, - node: node, - lookbackLimit: lookbackLimit, - } -} - -func (s *Syncer) setupSchemas() error { - tx, err := s.db.Begin() - if err != nil { - return err - } - - if _, err := tx.Exec(` -/* tracks circulating fil available on the network at each tipset */ -create table if not exists chain_economics -( - parent_state_root text not null - constraint chain_economics_pk primary key, - circulating_fil text not null, - vested_fil text not null, - mined_fil text not null, - burnt_fil text not null, - locked_fil text not null -); - -create table if not exists block_cids -( - cid text not null - constraint block_cids_pk - primary key -); - -create unique index if not exists block_cids_cid_uindex - on block_cids (cid); - -create table if not exists blocks_synced -( - cid text not null - constraint blocks_synced_pk - primary key - constraint blocks_block_cids_cid_fk - references block_cids (cid), - synced_at int not null, - processed_at int -); - -create unique index if not exists blocks_synced_cid_uindex - on blocks_synced (cid,processed_at); - -create table if not exists block_parents -( - block text not null - constraint blocks_block_cids_cid_fk - references block_cids (cid), - parent text not null -); - -create unique index if not exists block_parents_block_parent_uindex - on block_parents (block, parent); - -create table if not exists drand_entries -( - round bigint not null - constraint drand_entries_pk - primary key, - data bytea not null -); -create unique index if not exists drand_entries_round_uindex - on drand_entries (round); - -create table if not exists block_drand_entries -( - round bigint not null - constraint block_drand_entries_drand_entries_round_fk - references drand_entries (round), - block text not null - constraint blocks_block_cids_cid_fk - references block_cids (cid) -); -create unique index if not exists block_drand_entries_round_uindex - on block_drand_entries (round, block); - -create table if not exists blocks -( - cid text not null - constraint blocks_pk - primary key - constraint blocks_block_cids_cid_fk - references block_cids (cid), - parentWeight numeric not null, - parentStateRoot text not null, - height bigint not null, - miner text not null, - timestamp bigint not null, - ticket bytea not null, - election_proof bytea, - win_count bigint, - parent_base_fee text not null, - forksig bigint not null -); - -create unique index if not exists block_cid_uindex - on blocks (cid,height); - -create materialized view if not exists state_heights - as select min(b.height) height, b.parentstateroot - from blocks b group by b.parentstateroot; - -create index if not exists state_heights_height_index - on state_heights (height); - -create index if not exists state_heights_parentstateroot_index - on state_heights (parentstateroot); -`); err != nil { - return err - } - - return tx.Commit() -} - -func (s *Syncer) Start(ctx context.Context) { - if err := logging.SetLogLevel("syncer", "info"); err != nil { - log.Fatal(err) - } - log.Debug("Starting Syncer") - - if err := s.setupSchemas(); err != nil { - log.Fatal(err) - } - - // capture all reported blocks - go s.subBlocks(ctx) - - // we need to ensure that on a restart we don't reprocess the whole flarping chain - var sinceEpoch uint64 - blkCID, height, err := s.mostRecentlySyncedBlockHeight() - if err != nil { - log.Fatalw("failed to find most recently synced block", "error", err) - } else { - if height > 0 { - log.Infow("Found starting point for syncing", "blockCID", blkCID.String(), "height", height) - sinceEpoch = uint64(height) - } - } - - // continue to keep the block headers table up to date. - notifs, err := s.node.ChainNotify(ctx) - if err != nil { - log.Fatal(err) - } - - go func() { - for notif := range notifs { - for _, change := range notif { - switch change.Type { - case store.HCCurrent: - // This case is important for capturing the initial state of a node - // which might be on a dead network with no new blocks being produced. - // It also allows a fresh Chainwatch instance to start walking the - // chain without waiting for a new block to come along. - fallthrough - case store.HCApply: - unsynced, err := s.unsyncedBlocks(ctx, change.Val, sinceEpoch) - if err != nil { - log.Errorw("failed to gather unsynced blocks", "error", err) - } - - if err := s.storeCirculatingSupply(ctx, change.Val); err != nil { - log.Errorw("failed to store circulating supply", "error", err) - } - - if len(unsynced) == 0 { - continue - } - - if err := s.storeHeaders(unsynced, true, time.Now()); err != nil { - // so this is pretty bad, need some kind of retry.. - // for now just log an error and the blocks will be attempted again on next notifi - log.Errorw("failed to store unsynced blocks", "error", err) - } - - sinceEpoch = uint64(change.Val.Height()) - case store.HCRevert: - log.Debug("revert todo") - } - } - } - }() -} - -func (s *Syncer) unsyncedBlocks(ctx context.Context, head *types.TipSet, since uint64) (map[cid.Cid]*types.BlockHeader, error) { - hasList, err := s.syncedBlocks(since, s.lookbackLimit) - if err != nil { - return nil, err - } - - // build a list of blocks that we have not synced. - toVisit := list.New() - for _, header := range head.Blocks() { - toVisit.PushBack(header) - } - - toSync := map[cid.Cid]*types.BlockHeader{} - - for toVisit.Len() > 0 { - bh := toVisit.Remove(toVisit.Back()).(*types.BlockHeader) - _, has := hasList[bh.Cid()] - if _, seen := toSync[bh.Cid()]; seen || has { - continue - } - - toSync[bh.Cid()] = bh - if len(toSync)%500 == 10 { - log.Debugw("To visit", "toVisit", toVisit.Len(), "toSync", len(toSync), "current_height", bh.Height) - } - - if bh.Height == 0 { - continue - } - - pts, err := s.node.ChainGetTipSet(ctx, types.NewTipSetKey(bh.Parents...)) - if err != nil { - log.Error(err) - continue - } - - for _, header := range pts.Blocks() { - toVisit.PushBack(header) - } - } - log.Debugw("Gathered unsynced blocks", "count", len(toSync)) - return toSync, nil -} - -func (s *Syncer) syncedBlocks(since, limit uint64) (map[cid.Cid]struct{}, error) { - rws, err := s.db.Query(`select bs.cid FROM blocks_synced bs left join blocks b on b.cid = bs.cid where b.height <= $1 and bs.processed_at is not null limit $2`, since, limit) - if err != nil { - return nil, xerrors.Errorf("Failed to query blocks_synced: %w", err) - } - out := map[cid.Cid]struct{}{} - - for rws.Next() { - var c string - if err := rws.Scan(&c); err != nil { - return nil, xerrors.Errorf("Failed to scan blocks_synced: %w", err) - } - - ci, err := cid.Parse(c) - if err != nil { - return nil, xerrors.Errorf("Failed to parse blocks_synced: %w", err) - } - - out[ci] = struct{}{} - } - return out, nil -} - -func (s *Syncer) mostRecentlySyncedBlockHeight() (cid.Cid, int64, error) { - rw := s.db.QueryRow(` -select blocks_synced.cid, b.height -from blocks_synced -left join blocks b on blocks_synced.cid = b.cid -where processed_at is not null -order by height desc -limit 1 -`) - - var c string - var h int64 - if err := rw.Scan(&c, &h); err != nil { - if err == sql.ErrNoRows { - return cid.Undef, 0, nil - } - return cid.Undef, -1, err - } - - ci, err := cid.Parse(c) - if err != nil { - return cid.Undef, -1, err - } - - return ci, h, nil -} - -func (s *Syncer) storeCirculatingSupply(ctx context.Context, tipset *types.TipSet) error { - supply, err := s.node.StateVMCirculatingSupplyInternal(ctx, tipset.Key()) - if err != nil { - return err - } - - ceInsert := `insert into chain_economics (parent_state_root, circulating_fil, vested_fil, mined_fil, burnt_fil, locked_fil) ` + - `values ('%s', '%s', '%s', '%s', '%s', '%s') on conflict on constraint chain_economics_pk do ` + - `update set (circulating_fil, vested_fil, mined_fil, burnt_fil, locked_fil) = ('%[2]s', '%[3]s', '%[4]s', '%[5]s', '%[6]s') ` + - `where chain_economics.parent_state_root = '%[1]s';` - - if _, err := s.db.Exec(fmt.Sprintf(ceInsert, - tipset.ParentState().String(), - supply.FilCirculating.String(), - supply.FilVested.String(), - supply.FilMined.String(), - supply.FilBurnt.String(), - supply.FilLocked.String(), - )); err != nil { - return xerrors.Errorf("insert circulating supply for tipset (%s): %w", tipset.Key().String(), err) - } - - return nil -} - -func (s *Syncer) storeHeaders(bhs map[cid.Cid]*types.BlockHeader, sync bool, timestamp time.Time) error { - s.headerLk.Lock() - defer s.headerLk.Unlock() - if len(bhs) == 0 { - return nil - } - log.Debugw("Storing Headers", "count", len(bhs)) - - tx, err := s.db.Begin() - if err != nil { - return xerrors.Errorf("begin: %w", err) - } - - if _, err := tx.Exec(` - -create temp table bc (like block_cids excluding constraints) on commit drop; -create temp table de (like drand_entries excluding constraints) on commit drop; -create temp table bde (like block_drand_entries excluding constraints) on commit drop; -create temp table tbp (like block_parents excluding constraints) on commit drop; -create temp table bs (like blocks_synced excluding constraints) on commit drop; -create temp table b (like blocks excluding constraints) on commit drop; - - -`); err != nil { - return xerrors.Errorf("prep temp: %w", err) - } - - { - stmt, err := tx.Prepare(`copy bc (cid) from STDIN`) - if err != nil { - return err - } - - for _, bh := range bhs { - if _, err := stmt.Exec(bh.Cid().String()); err != nil { - log.Error(err) - } - } - - if err := stmt.Close(); err != nil { - return err - } - - if _, err := tx.Exec(`insert into block_cids select * from bc on conflict do nothing `); err != nil { - return xerrors.Errorf("drand entries put: %w", err) - } - } - - { - stmt, err := tx.Prepare(`copy de (round, data) from STDIN`) - if err != nil { - return err - } - - for _, bh := range bhs { - for _, ent := range bh.BeaconEntries { - if _, err := stmt.Exec(ent.Round, ent.Data); err != nil { - log.Error(err) - } - } - } - - if err := stmt.Close(); err != nil { - return err - } - - if _, err := tx.Exec(`insert into drand_entries select * from de on conflict do nothing `); err != nil { - return xerrors.Errorf("drand entries put: %w", err) - } - } - - { - stmt, err := tx.Prepare(`copy bde (round, block) from STDIN`) - if err != nil { - return err - } - - for _, bh := range bhs { - for _, ent := range bh.BeaconEntries { - if _, err := stmt.Exec(ent.Round, bh.Cid().String()); err != nil { - log.Error(err) - } - } - } - - if err := stmt.Close(); err != nil { - return err - } - - if _, err := tx.Exec(`insert into block_drand_entries select * from bde on conflict do nothing `); err != nil { - return xerrors.Errorf("block drand entries put: %w", err) - } - } - - { - stmt, err := tx.Prepare(`copy tbp (block, parent) from STDIN`) - if err != nil { - return err - } - - for _, bh := range bhs { - for _, parent := range bh.Parents { - if _, err := stmt.Exec(bh.Cid().String(), parent.String()); err != nil { - log.Error(err) - } - } - } - - if err := stmt.Close(); err != nil { - return err - } - - if _, err := tx.Exec(`insert into block_parents select * from tbp on conflict do nothing `); err != nil { - return xerrors.Errorf("parent put: %w", err) - } - } - - if sync { - - stmt, err := tx.Prepare(`copy bs (cid, synced_at) from stdin `) - if err != nil { - return err - } - - for _, bh := range bhs { - if _, err := stmt.Exec(bh.Cid().String(), timestamp.Unix()); err != nil { - log.Error(err) - } - } - - if err := stmt.Close(); err != nil { - return err - } - - if _, err := tx.Exec(`insert into blocks_synced select * from bs on conflict do nothing `); err != nil { - return xerrors.Errorf("syncd put: %w", err) - } - } - - stmt2, err := tx.Prepare(`copy b (cid, parentWeight, parentStateRoot, height, miner, "timestamp", ticket, election_proof, win_count, parent_base_fee, forksig) from stdin`) - if err != nil { - return err - } - - for _, bh := range bhs { - var eproof, winCount interface{} - if bh.ElectionProof != nil { - eproof = bh.ElectionProof.VRFProof - winCount = bh.ElectionProof.WinCount - } - - if bh.Ticket == nil { - log.Warnf("got a block with nil ticket") - - bh.Ticket = &types.Ticket{ - VRFProof: []byte{}, - } - } - - if _, err := stmt2.Exec( - bh.Cid().String(), - bh.ParentWeight.String(), - bh.ParentStateRoot.String(), - bh.Height, - bh.Miner.String(), - bh.Timestamp, - bh.Ticket.VRFProof, - eproof, - winCount, - bh.ParentBaseFee.String(), - bh.ForkSignaling); err != nil { - log.Error(err) - } - } - - if err := stmt2.Close(); err != nil { - return xerrors.Errorf("s2 close: %w", err) - } - - if _, err := tx.Exec(`insert into blocks select * from b on conflict do nothing `); err != nil { - return xerrors.Errorf("blk put: %w", err) - } - - return tx.Commit() -} diff --git a/cmd/lotus-chainwatch/util/api.go b/cmd/lotus-chainwatch/util/api.go deleted file mode 100644 index f8f22cbbf..000000000 --- a/cmd/lotus-chainwatch/util/api.go +++ /dev/null @@ -1,34 +0,0 @@ -package util - -import ( - "context" - "net/http" - - "github.com/filecoin-project/go-jsonrpc" - "github.com/filecoin-project/lotus/api/client" - "github.com/filecoin-project/lotus/api/v0api" - ma "github.com/multiformats/go-multiaddr" - manet "github.com/multiformats/go-multiaddr/net" -) - -func GetFullNodeAPIUsingCredentials(ctx context.Context, listenAddr, token string) (v0api.FullNode, jsonrpc.ClientCloser, error) { - parsedAddr, err := ma.NewMultiaddr(listenAddr) - if err != nil { - return nil, nil, err - } - - _, addr, err := manet.DialArgs(parsedAddr) - if err != nil { - return nil, nil, err - } - - return client.NewFullNodeRPCV0(ctx, apiURI(addr), apiHeaders(token)) -} -func apiURI(addr string) string { - return "ws://" + addr + "/rpc/v0" -} -func apiHeaders(token string) http.Header { - headers := http.Header{} - headers.Add("Authorization", "Bearer "+token) - return headers -} diff --git a/cmd/lotus-chainwatch/util/contextStore.go b/cmd/lotus-chainwatch/util/contextStore.go deleted file mode 100644 index c93f87f9b..000000000 --- a/cmd/lotus-chainwatch/util/contextStore.go +++ /dev/null @@ -1,51 +0,0 @@ -package util - -import ( - "bytes" - "context" - "fmt" - - "github.com/ipfs/go-cid" - cbg "github.com/whyrusleeping/cbor-gen" - - "github.com/filecoin-project/lotus/api/v0api" -) - -// TODO extract this to a common location in lotus and reuse the code - -// APIIpldStore is required for AMT and HAMT access. -type APIIpldStore struct { - ctx context.Context - api v0api.FullNode -} - -func NewAPIIpldStore(ctx context.Context, api v0api.FullNode) *APIIpldStore { - return &APIIpldStore{ - ctx: ctx, - api: api, - } -} - -func (ht *APIIpldStore) Context() context.Context { - return ht.ctx -} - -func (ht *APIIpldStore) Get(ctx context.Context, c cid.Cid, out interface{}) error { - raw, err := ht.api.ChainReadObj(ctx, c) - if err != nil { - return err - } - - cu, ok := out.(cbg.CBORUnmarshaler) - if ok { - if err := cu.UnmarshalCBOR(bytes.NewReader(raw)); err != nil { - return err - } - return nil - } - return fmt.Errorf("Object does not implement CBORUnmarshaler: %T", out) -} - -func (ht *APIIpldStore) Put(ctx context.Context, v interface{}) (cid.Cid, error) { - return cid.Undef, fmt.Errorf("Put is not implemented on APIIpldStore") -} diff --git a/scripts/lotus-chainwatch.service b/scripts/lotus-chainwatch.service deleted file mode 100644 index e121cb1d1..000000000 --- a/scripts/lotus-chainwatch.service +++ /dev/null @@ -1,15 +0,0 @@ -[Unit] -Description=Chainwatch -After=lotus-daemon.service -Requires=lotus-daemon.service - -[Service] -Environment=GOLOG_FILE="/var/log/lotus/chainwatch.log" -Environment=GOLOG_LOG_FMT="json" -Environment=LOTUS_DB="" -Environment=LOTUS_PATH="%h/.lotus" -EnvironmentFile=-/etc/lotus/chainwatch.env -ExecStart=/usr/local/bin/lotus-chainwatch run - -[Install] -WantedBy=multi-user.target From 3b617f4528eaa8fe221cd0bc59ddea718d270144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 28 Jul 2021 17:59:37 +0200 Subject: [PATCH 20/31] mod tidy --- go.mod | 1 - go.sum | 2 -- 2 files changed, 3 deletions(-) diff --git a/go.mod b/go.mod index 5f968f6e0..a094cd6c3 100644 --- a/go.mod +++ b/go.mod @@ -100,7 +100,6 @@ require ( 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 github.com/libp2p/go-buffer-pool v0.0.2 github.com/libp2p/go-eventbus v0.2.1 github.com/libp2p/go-libp2p v0.14.2 diff --git a/go.sum b/go.sum index b22f3dc15..9757c15f3 100644 --- a/go.sum +++ b/go.sum @@ -835,8 +835,6 @@ 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/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY= -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= From a1a23d8be5f811738959651602cb57e7ea2ba602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 28 Jul 2021 19:12:23 +0200 Subject: [PATCH 21/31] make: fix version check when using gotip --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index dfe4a65c7..dfef9f262 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ all: build unexport GOFLAGS -GOVERSION:=$(shell go version | cut -d' ' -f 3 | sed 's/^go//' | awk -F. '{printf "%d%03d%03d", $$1, $$2, $$3}') +GOVERSION:=$(shell $(GOCC) version | tr ' ' '\n' | grep go1 | sed 's/^go//' | awk -F. '{printf "%d%03d%03d", $$1, $$2, $$3}') ifeq ($(shell expr $(GOVERSION) \< 1016000), 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.16.0) From 07f40b94882e1233d29b9c7868a5755c6fbec688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Kripalani?= Date: Wed, 28 Jul 2021 19:51:45 +0100 Subject: [PATCH 22/31] fix docs and nits. --- api/api_storage.go | 2 + api/api_subsystems.go | 63 ------------------ api/docgen/docgen.go | 10 ++- api/miner_subsystems.go | 79 +++++++++++++++++++++++ build/openrpc/miner.json.gz | Bin 9540 -> 9596 bytes cmd/lotus-miner/info.go | 4 +- documentation/en/api-v0-methods-miner.md | 12 +++- node/builder_miner.go | 2 +- node/impl/storminer.go | 10 +-- node/modules/storageminer.go | 11 ++-- 10 files changed, 113 insertions(+), 80 deletions(-) delete mode 100644 api/api_subsystems.go create mode 100644 api/miner_subsystems.go diff --git a/api/api_storage.go b/api/api_storage.go index d52032650..c39114929 100644 --- a/api/api_storage.go +++ b/api/api_storage.go @@ -166,6 +166,8 @@ type StorageMiner interface { MarketPendingDeals(ctx context.Context) (PendingDealInfo, error) //perm:write MarketPublishPendingDeals(ctx context.Context) error //perm:admin + // RuntimeSubsystems returns the subsystems that are enabled + // in this instance. RuntimeSubsystems(ctx context.Context) (MinerSubsystems, error) //perm:read DealsImportData(ctx context.Context, dealPropCid cid.Cid, file string) error //perm:admin diff --git a/api/api_subsystems.go b/api/api_subsystems.go deleted file mode 100644 index 1894bbdd8..000000000 --- a/api/api_subsystems.go +++ /dev/null @@ -1,63 +0,0 @@ -package api - -import ( - "bytes" - "encoding/json" -) - -type MinerSubsystems []MinerSubsystem - -func (ms MinerSubsystems) Has(entry MinerSubsystem) bool { - for _, v := range ms { - if v == entry { - return true - } - - } - return false -} - -type MinerSubsystem int - -const ( - MarketsSubsystem MinerSubsystem = iota - MiningSubsystem - SealingSubsystem - SectorStorageSubsystem -) - -func (ms MinerSubsystem) String() string { - return MinerSubsystemToString[ms] -} - -var MinerSubsystemToString = map[MinerSubsystem]string{ - MarketsSubsystem: "Markets", - MiningSubsystem: "Mining", - SealingSubsystem: "Sealing", - SectorStorageSubsystem: "SectorStorage", -} - -var MinerSubsystemToID = map[string]MinerSubsystem{ - "Markets": MarketsSubsystem, - "Mining": MiningSubsystem, - "Sealing": SealingSubsystem, - "SectorStorage": SectorStorageSubsystem, -} - -func (ms MinerSubsystem) MarshalJSON() ([]byte, error) { - buffer := bytes.NewBufferString(`"`) - buffer.WriteString(MinerSubsystemToString[ms]) - buffer.WriteString(`"`) - return buffer.Bytes(), nil -} - -func (ms *MinerSubsystem) UnmarshalJSON(b []byte) error { - var j string - err := json.Unmarshal(b, &j) - if err != nil { - return err - } - // TODO: handle zero value - *ms = MinerSubsystemToID[j] - return nil -} diff --git a/api/docgen/docgen.go b/api/docgen/docgen.go index 1e712a0ae..f9addc940 100644 --- a/api/docgen/docgen.go +++ b/api/docgen/docgen.go @@ -16,10 +16,10 @@ import ( "github.com/google/uuid" "github.com/ipfs/go-cid" "github.com/ipfs/go-filestore" - metrics "github.com/libp2p/go-libp2p-core/metrics" + "github.com/libp2p/go-libp2p-core/metrics" "github.com/libp2p/go-libp2p-core/network" "github.com/libp2p/go-libp2p-core/peer" - protocol "github.com/libp2p/go-libp2p-core/protocol" + "github.com/libp2p/go-libp2p-core/protocol" pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/multiformats/go-multiaddr" @@ -265,6 +265,12 @@ func init() { addExample(api.CheckStatusCode(0)) addExample(map[string]interface{}{"abc": 123}) + addExample(api.MinerSubsystems{ + api.SubsystemMining, + api.SubsystemSealing, + api.SubsystemSectorStorage, + api.SubsystemMarkets, + }) } func GetAPIType(name, pkg string) (i interface{}, t reflect.Type, permStruct []reflect.Type) { diff --git a/api/miner_subsystems.go b/api/miner_subsystems.go new file mode 100644 index 000000000..a77de7e3c --- /dev/null +++ b/api/miner_subsystems.go @@ -0,0 +1,79 @@ +package api + +import ( + "encoding/json" +) + +// MinerSubsystem represents a miner subsystem. Int and string values are not +// guaranteed to be stable over time is not +// guaranteed to be stable over time. +type MinerSubsystem int + +const ( + // SubsystemUnknown is a placeholder for the zero value. It should never + // be used. + SubsystemUnknown MinerSubsystem = iota + // SubsystemMarkets signifies the storage and retrieval + // deal-making subsystem. + SubsystemMarkets + // SubsystemMining signifies the mining subsystem. + SubsystemMining + // SubsystemSealing signifies the sealing subsystem. + SubsystemSealing + // SubsystemSectorStorage signifies the sector storage subsystem. + SubsystemSectorStorage +) + +var MinerSubsystemToString = map[MinerSubsystem]string{ + SubsystemUnknown: "Unknown", + SubsystemMarkets: "Markets", + SubsystemMining: "Mining", + SubsystemSealing: "Sealing", + SubsystemSectorStorage: "SectorStorage", +} + +var MinerSubsystemToID = map[string]MinerSubsystem{ + "Unknown": SubsystemUnknown, + "Markets": SubsystemMarkets, + "Mining": SubsystemMining, + "Sealing": SubsystemSealing, + "SectorStorage": SubsystemSectorStorage, +} + +func (ms MinerSubsystem) MarshalJSON() ([]byte, error) { + return json.Marshal(MinerSubsystemToString[ms]) +} + +func (ms *MinerSubsystem) UnmarshalJSON(b []byte) error { + var j string + err := json.Unmarshal(b, &j) + if err != nil { + return err + } + s, ok := MinerSubsystemToID[j] + if !ok { + *ms = SubsystemUnknown + } else { + *ms = s + } + return nil +} + +type MinerSubsystems []MinerSubsystem + +func (ms MinerSubsystems) Has(entry MinerSubsystem) bool { + for _, v := range ms { + if v == entry { + return true + } + } + return false +} + +func (ms MinerSubsystem) String() string { + s, ok := MinerSubsystemToString[ms] + if !ok { + return MinerSubsystemToString[SubsystemUnknown] + } + return s +} diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index 69d471dbd5673f06dc006a4d21f0a193b36bbb30..57a56d94fffd25d9f55069cc5127c44ce67d0b69 100644 GIT binary patch literal 9596 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 Date: Wed, 28 Jul 2021 14:46:13 -0700 Subject: [PATCH 23/31] feat(deps): update to branches with improved logging update sub repos with improved logging around data transfer processing --- go.mod | 8 ++++---- go.sum | 14 ++++++++------ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index b45284c8f..11d24b0f6 100644 --- a/go.mod +++ b/go.mod @@ -33,16 +33,16 @@ require ( github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2 github.com/filecoin-project/go-commp-utils v0.1.1-0.20210427191551-70bf140d31c7 github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 - github.com/filecoin-project/go-data-transfer v1.7.0 + github.com/filecoin-project/go-data-transfer v1.7.1 github.com/filecoin-project/go-fil-commcid v0.1.0 github.com/filecoin-project/go-fil-commp-hashhash v0.1.0 - github.com/filecoin-project/go-fil-markets v1.6.0 + github.com/filecoin-project/go-fil-markets v1.6.1 github.com/filecoin-project/go-jsonrpc v0.1.4-0.20210217175800-45ea43ac2bec github.com/filecoin-project/go-multistore v0.0.3 github.com/filecoin-project/go-padreader v0.0.0-20210723183308-812a16dc01b1 github.com/filecoin-project/go-paramfetch v0.0.2-0.20210614165157-25a6c7769498 github.com/filecoin-project/go-state-types v0.1.1-0.20210722133031-ad9bfe54c124 - github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe + github.com/filecoin-project/go-statemachine v1.0.0 github.com/filecoin-project/go-statestore v0.1.1 github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b github.com/filecoin-project/specs-actors v0.9.14 @@ -78,7 +78,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.6.5 + github.com/ipfs/go-graphsync v0.6.6 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 diff --git a/go.sum b/go.sum index 88a31e57f..ca71b27d2 100644 --- a/go.sum +++ b/go.sum @@ -276,8 +276,9 @@ github.com/filecoin-project/go-commp-utils v0.1.1-0.20210427191551-70bf140d31c7/ 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/go.mod h1:UxvfUAY9v3ub0a21BSK9u3pB2aq30Y0KMsG+w9/ysyo= -github.com/filecoin-project/go-data-transfer v1.7.0 h1:mFRn+UuTdPROmhplLSekzd4rAs9ug8ubtSY4nw9wYkU= github.com/filecoin-project/go-data-transfer v1.7.0/go.mod h1:GLRr5BmLEqsLwXfiRDG7uJvph22KGL2M4iOuF8EINaU= +github.com/filecoin-project/go-data-transfer v1.7.1 h1:Co4bTenvCc3WnOhQWyXRt59FLZvxwH8UeF0ZCOc1ik0= +github.com/filecoin-project/go-data-transfer v1.7.1/go.mod h1:GLRr5BmLEqsLwXfiRDG7uJvph22KGL2M4iOuF8EINaU= 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/go.mod h1:Eaox7Hvus1JgPrL5+M3+h7aSPHc0cVqpSxA+TxIEpZQ= @@ -287,8 +288,8 @@ github.com/filecoin-project/go-fil-commcid v0.1.0/go.mod h1:Eaox7Hvus1JgPrL5+M3+ github.com/filecoin-project/go-fil-commp-hashhash v0.1.0 h1:imrrpZWEHRnNqqv0tN7LXep5bFEVOVmQWHJvl2mgsGo= github.com/filecoin-project/go-fil-commp-hashhash v0.1.0/go.mod h1:73S8WSEWh9vr0fDJVnKADhfIv/d6dCbAGaAGWbdJEI8= 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.6.0 h1:+1usyX7rXz6Ey6hbHd/Fhx616ZvGCI94rW7wneMcptU= -github.com/filecoin-project/go-fil-markets v1.6.0/go.mod h1:ZuFDagROUV6GfvBU//KReTQDw+EZci4rH7jMYTD10vs= +github.com/filecoin-project/go-fil-markets v1.6.1 h1:8xdFyWrELfOzwcGa229bLu/olD+1l4sEWFIsZR7oz5U= +github.com/filecoin-project/go-fil-markets v1.6.1/go.mod h1:ZuFDagROUV6GfvBU//KReTQDw+EZci4rH7jMYTD10vs= 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= @@ -313,8 +314,9 @@ github.com/filecoin-project/go-state-types v0.1.0/go.mod h1:ezYnPf0bNkTsDibL/psS github.com/filecoin-project/go-state-types v0.1.1-0.20210506134452-99b279731c48/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g= github.com/filecoin-project/go-state-types v0.1.1-0.20210722133031-ad9bfe54c124 h1:veGrNABg/9I7prngrowkhwbvW5d5JN55MNKmbsr5FqA= github.com/filecoin-project/go-state-types v0.1.1-0.20210722133031-ad9bfe54c124/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g= -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-statemachine v1.0.0 h1:b8FpFewPSklyAIUqH0oHt4nvKf03bU7asop1bJpjAtQ= +github.com/filecoin-project/go-statemachine v1.0.0/go.mod h1:FGwQgZAt2Gh5mjlwJUlVB62JeYdo+if0xWxSEfBD9ig= github.com/filecoin-project/go-statestore v0.1.0/go.mod h1:LFc9hD+fRxPqiHiaqUEZOinUJB4WARkRfNl10O7kTnI= github.com/filecoin-project/go-statestore v0.1.1 h1:ufMFq00VqnT2CAuDpcGnwLnCX1I/c3OROw/kXVNSTZk= github.com/filecoin-project/go-statestore v0.1.1/go.mod h1:LFc9hD+fRxPqiHiaqUEZOinUJB4WARkRfNl10O7kTnI= @@ -640,8 +642,8 @@ github.com/ipfs/go-graphsync v0.1.0/go.mod h1:jMXfqIEDFukLPZHqDPp8tJMbHO9Rmeb9CE 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.6.4/go.mod h1:5WyaeigpNdpiYQuW2vwpuecOoEfB4h747ZGEOKmAGTg= -github.com/ipfs/go-graphsync v0.6.5 h1:YAJl6Yit23PQcaawzb1rPK9PSnbbq2jjMRPpRpJ0Y5U= -github.com/ipfs/go-graphsync v0.6.5/go.mod h1:GdHT8JeuIZ0R4lSjFR16Oe4zPi5dXwKi9zR9ADVlcdk= +github.com/ipfs/go-graphsync v0.6.6 h1:In7jjzvSXlrAUz4OjN41lxYf/dzkf1bVeVxLpwKMRo8= +github.com/ipfs/go-graphsync v0.6.6/go.mod h1:GdHT8JeuIZ0R4lSjFR16Oe4zPi5dXwKi9zR9ADVlcdk= 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= From 0202192fc054f4cb96160c5249c747fe9206be5a Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Wed, 28 Jul 2021 14:57:20 -0400 Subject: [PATCH 24/31] Log more call context during errors --- chain/vm/runtime.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chain/vm/runtime.go b/chain/vm/runtime.go index 2845c7696..852a34984 100644 --- a/chain/vm/runtime.go +++ b/chain/vm/runtime.go @@ -146,7 +146,7 @@ func (rt *Runtime) shimCall(f func() interface{}) (rval []byte, aerr aerrors.Act defer func() { if r := recover(); r != nil { if ar, ok := r.(aerrors.ActorError); ok { - log.Warnf("VM.Call failure: %+v", ar) + log.Warnf("VM.Call failure in call from: %s to %s: %+v", rt.Caller(), rt.Receiver(), ar) aerr = ar return } @@ -391,7 +391,7 @@ func (rt *Runtime) Send(to address.Address, method abi.MethodNum, m cbor.Marshal if err.IsFatal() { panic(err) } - log.Warnf("vmctx send failed: to: %s, method: %d: ret: %d, err: %s", to, method, ret, err) + log.Warnf("vmctx send failed: from: %s to: %s, method: %d: ret: %d, err: %s", rt.Receiver(), to, method, ret, err) return err.RetCode() } _ = rt.chargeGasSafe(gasOnActorExec) From a9687849082bfb838b4ea6b689b3914d8ebff631 Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Wed, 28 Jul 2021 15:19:32 -0400 Subject: [PATCH 25/31] fix new panics in invoker_test.go --- chain/vm/invoker_test.go | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/chain/vm/invoker_test.go b/chain/vm/invoker_test.go index 6822e2371..8499f001a 100644 --- a/chain/vm/invoker_test.go +++ b/chain/vm/invoker_test.go @@ -6,6 +6,8 @@ import ( "io" "testing" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/network" cbor "github.com/ipfs/go-ipld-cbor" @@ -80,6 +82,30 @@ func (basicContract) InvokeSomething10(rt runtime2.Runtime, params *basicParams) return nil } +type basicRtMessage struct{} + +var _ runtime2.Message = (*basicRtMessage)(nil) + +func (*basicRtMessage) Caller() address.Address { + a, err := address.NewIDAddress(0) + if err != nil { + panic(err) + } + return a +} + +func (*basicRtMessage) Receiver() address.Address { + a, err := address.NewIDAddress(1) + if err != nil { + panic(err) + } + return a +} + +func (*basicRtMessage) ValueReceived() abi.TokenAmount { + return big.NewInt(0) +} + func TestInvokerBasic(t *testing.T) { inv := ActorRegistry{} code, err := inv.transform(basicContract{}) @@ -89,7 +115,7 @@ func TestInvokerBasic(t *testing.T) { bParam, err := actors.SerializeParams(&basicParams{B: 1}) assert.NoError(t, err) - _, aerr := code[0](&Runtime{}, bParam) + _, aerr := code[0](&Runtime{Message: &basicRtMessage{}}, bParam) assert.Equal(t, exitcode.ExitCode(1), aerrors.RetCode(aerr), "return code should be 1") if aerrors.IsFatal(aerr) { @@ -101,7 +127,7 @@ func TestInvokerBasic(t *testing.T) { bParam, err := actors.SerializeParams(&basicParams{B: 2}) assert.NoError(t, err) - _, aerr := code[10](&Runtime{}, bParam) + _, aerr := code[10](&Runtime{Message: &basicRtMessage{}}, bParam) assert.Equal(t, exitcode.ExitCode(12), aerrors.RetCode(aerr), "return code should be 12") if aerrors.IsFatal(aerr) { t.Fatal("err should not be fatal") @@ -113,6 +139,7 @@ func TestInvokerBasic(t *testing.T) { vm: &VM{ntwkVersion: func(ctx context.Context, epoch abi.ChainEpoch) network.Version { return network.Version0 }}, + Message: &basicRtMessage{}, }, []byte{99}) if aerrors.IsFatal(aerr) { t.Fatal("err should not be fatal") @@ -125,6 +152,7 @@ func TestInvokerBasic(t *testing.T) { vm: &VM{ntwkVersion: func(ctx context.Context, epoch abi.ChainEpoch) network.Version { return network.Version7 }}, + Message: &basicRtMessage{}, }, []byte{99}) if aerrors.IsFatal(aerr) { t.Fatal("err should not be fatal") From e09a25ca68a4e061719c1a3f9c48ad1c40651c5a Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Wed, 28 Jul 2021 18:26:09 -0400 Subject: [PATCH 26/31] Remove unnecessary ret log --- chain/vm/runtime.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/vm/runtime.go b/chain/vm/runtime.go index 852a34984..7117f3443 100644 --- a/chain/vm/runtime.go +++ b/chain/vm/runtime.go @@ -391,7 +391,7 @@ func (rt *Runtime) Send(to address.Address, method abi.MethodNum, m cbor.Marshal if err.IsFatal() { panic(err) } - log.Warnf("vmctx send failed: from: %s to: %s, method: %d: ret: %d, err: %s", rt.Receiver(), to, method, ret, err) + log.Warnf("vmctx send failed: from: %s to: %s, method: %d: err: %s", rt.Receiver(), to, method, err) return err.RetCode() } _ = rt.chargeGasSafe(gasOnActorExec) From d3742048f9cff5565f34f9725c7fbd27ca078da9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Kripalani?= Date: Wed, 28 Jul 2021 23:46:21 +0100 Subject: [PATCH 27/31] address nits. --- node/builder_miner.go | 2 +- node/modules/storageminer.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/node/builder_miner.go b/node/builder_miner.go index acb9d3d43..3be055de7 100644 --- a/node/builder_miner.go +++ b/node/builder_miner.go @@ -72,7 +72,7 @@ func ConfigStorageMiner(c interface{}) Option { return Options( ConfigCommon(&cfg.Common, enableLibp2pNode), - Override(new(api.MinerSubsystems), modules.PopulateEnabledMinerSubsystems(cfg.Subsystems)), + Override(new(api.MinerSubsystems), modules.ExtractEnabledMinerSubsystems(cfg.Subsystems)), Override(new(stores.LocalStorage), From(new(repo.LockedRepo))), Override(new(*stores.Local), modules.LocalStorage), Override(new(*stores.Remote), modules.RemoteStorage), diff --git a/node/modules/storageminer.go b/node/modules/storageminer.go index 26792ca19..5497eab58 100644 --- a/node/modules/storageminer.go +++ b/node/modules/storageminer.go @@ -1008,7 +1008,7 @@ func mutateCfg(r repo.LockedRepo, mutator func(*config.StorageMiner)) error { return multierr.Combine(typeErr, setConfigErr) } -func PopulateEnabledMinerSubsystems(cfg config.MinerSubsystemConfig) (res api.MinerSubsystems) { +func ExtractEnabledMinerSubsystems(cfg config.MinerSubsystemConfig) (res api.MinerSubsystems) { if cfg.EnableMining { res = append(res, api.SubsystemMining) } @@ -1021,5 +1021,5 @@ func PopulateEnabledMinerSubsystems(cfg config.MinerSubsystemConfig) (res api.Mi if cfg.EnableMarkets { res = append(res, api.SubsystemMarkets) } - return + return res } From 256d12773be0adc008a0aa4642c77d141126cecc Mon Sep 17 00:00:00 2001 From: frrist Date: Wed, 28 Jul 2021 17:50:22 -0700 Subject: [PATCH 28/31] polish(errors): better state tree errors --- chain/state/statetree.go | 2 +- chain/stmgr/stmgr.go | 6 +++--- chain/store/messages.go | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/chain/state/statetree.go b/chain/state/statetree.go index 8705aeff8..8140cd4db 100644 --- a/chain/state/statetree.go +++ b/chain/state/statetree.go @@ -273,7 +273,7 @@ func LoadStateTree(cst cbor.IpldStore, c cid.Cid) (*StateTree, error) { } if err != nil { log.Errorf("failed to load state tree: %s", err) - return nil, xerrors.Errorf("failed to load state tree: %w", err) + return nil, xerrors.Errorf("failed to load state tree %s: %w", c, err) } s := &StateTree{ diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index b76dd0faf..1748c341e 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -221,7 +221,7 @@ func (sm *StateManager) ResolveToKeyAddress(ctx context.Context, addr address.Ad // First try to resolve the actor in the parent state, so we don't have to compute anything. tree, err := state.LoadStateTree(cst, ts.ParentState()) if err != nil { - return address.Undef, xerrors.Errorf("failed to load parent state tree: %w", err) + return address.Undef, xerrors.Errorf("failed to load parent state tree at tipset %s: %w", ts.Parents(), err) } resolved, err := vm.ResolveToKeyAddr(tree, cst, addr) @@ -232,12 +232,12 @@ func (sm *StateManager) ResolveToKeyAddress(ctx context.Context, addr address.Ad // If that fails, compute the tip-set and try again. st, _, err := sm.TipSetState(ctx, ts) if err != nil { - return address.Undef, xerrors.Errorf("resolve address failed to get tipset state: %w", err) + return address.Undef, xerrors.Errorf("resolve address failed to get tipset %s state: %w", ts, err) } tree, err = state.LoadStateTree(cst, st) if err != nil { - return address.Undef, xerrors.Errorf("failed to load state tree") + return address.Undef, xerrors.Errorf("failed to load state tree at tipset %s: %w", ts, err) } return vm.ResolveToKeyAddr(tree, cst, addr) diff --git a/chain/store/messages.go b/chain/store/messages.go index 50cf0e6a2..9f5160559 100644 --- a/chain/store/messages.go +++ b/chain/store/messages.go @@ -110,7 +110,7 @@ func (cs *ChainStore) BlockMsgsForTipset(ts *types.TipSet) ([]BlockMessages, err cst := cbor.NewCborStore(cs.stateBlockstore) st, err := state.LoadStateTree(cst, ts.Blocks()[0].ParentStateRoot) if err != nil { - return nil, xerrors.Errorf("failed to load state tree") + return nil, xerrors.Errorf("failed to load state tree at tipset %s: %w", ts, err) } selectMsg := func(m *types.Message) (bool, error) { From 5c823e9b4294bb8af602fcb6abf89667489dd1c3 Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Wed, 28 Jul 2021 23:59:59 -0400 Subject: [PATCH 29/31] PR #5109 + suggested fix --- cli/mpool.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cli/mpool.go b/cli/mpool.go index b128ccc15..f8af177ae 100644 --- a/cli/mpool.go +++ b/cli/mpool.go @@ -381,7 +381,7 @@ var MpoolReplaceCmd = &cli.Command{ Usage: "automatically reprice the specified message", }, &cli.StringFlag{ - Name: "max-fee", + Name: "fee-limit", Usage: "Spend up to X attoFIL for this message (applicable for auto mode)", }, }, @@ -457,13 +457,13 @@ var MpoolReplaceCmd = &cli.Command{ minRBF := messagepool.ComputeMinRBF(msg.GasPremium) var mss *lapi.MessageSendSpec - if cctx.IsSet("max-fee") { - maxFee, err := types.BigFromString(cctx.String("max-fee")) + if cctx.IsSet("fee-limit") { + maxFee, err := types.ParseFIL(cctx.String("fee-limit")) if err != nil { return fmt.Errorf("parsing max-spend: %w", err) } mss = &lapi.MessageSendSpec{ - MaxFee: maxFee, + MaxFee: abi.TokenAmount(maxFee), } } From 8ccee5188777350ec1eff01c0b8e29a49b50e943 Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Thu, 29 Jul 2021 00:24:24 -0400 Subject: [PATCH 30/31] docsgen --- documentation/en/cli-lotus.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/en/cli-lotus.md b/documentation/en/cli-lotus.md index 53ddff735..6bb9d55d4 100644 --- a/documentation/en/cli-lotus.md +++ b/documentation/en/cli-lotus.md @@ -1531,7 +1531,7 @@ OPTIONS: --gas-premium value gas price for new message (pay to miner, attoFIL/GasUnit) --gas-limit value gas limit for new message (GasUnit) (default: 0) --auto automatically reprice the specified message (default: false) - --max-fee value Spend up to X attoFIL for this message (applicable for auto mode) + --fee-limit value Spend up to X attoFIL for this message (applicable for auto mode) --help, -h show help (default: false) ``` From 11394aa4aabc171984d84181f4471ae83c506277 Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Thu, 29 Jul 2021 00:28:15 -0400 Subject: [PATCH 31/31] Improve cli docs --- cli/mpool.go | 2 +- documentation/en/cli-lotus.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/mpool.go b/cli/mpool.go index f8af177ae..adefd25a8 100644 --- a/cli/mpool.go +++ b/cli/mpool.go @@ -382,7 +382,7 @@ var MpoolReplaceCmd = &cli.Command{ }, &cli.StringFlag{ Name: "fee-limit", - Usage: "Spend up to X attoFIL for this message (applicable for auto mode)", + Usage: "Spend up to X FIL for this message in units of FIL. Previously when flag was `max-fee` units were in attoFIL. Applicable for auto mode", }, }, ArgsUsage: " | ", diff --git a/documentation/en/cli-lotus.md b/documentation/en/cli-lotus.md index 6bb9d55d4..75f329a0f 100644 --- a/documentation/en/cli-lotus.md +++ b/documentation/en/cli-lotus.md @@ -1531,7 +1531,7 @@ OPTIONS: --gas-premium value gas price for new message (pay to miner, attoFIL/GasUnit) --gas-limit value gas limit for new message (GasUnit) (default: 0) --auto automatically reprice the specified message (default: false) - --fee-limit value Spend up to X attoFIL for this message (applicable for auto mode) + --fee-limit max-fee Spend up to X FIL for this message in units of FIL. Previously when flag was max-fee units were in attoFIL. Applicable for auto mode --help, -h show help (default: false) ```