From f8c886a611b83091f6d4b699493fb351ff27b6c3 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Tue, 6 Oct 2020 03:49:11 -0400 Subject: [PATCH 1/3] Rename MiningBaseInfo.HasMinPower to EligibleForMining --- api/api_full.go | 16 ++++++++-------- chain/stmgr/utils.go | 16 ++++++++-------- miner/miner.go | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/api/api_full.go b/api/api_full.go index 5cbdde8e3..07b48a7eb 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -806,14 +806,14 @@ type CirculatingSupply struct { } type MiningBaseInfo struct { - MinerPower types.BigInt - NetworkPower types.BigInt - Sectors []builtin.SectorInfo - WorkerKey address.Address - SectorSize abi.SectorSize - PrevBeaconEntry types.BeaconEntry - BeaconEntries []types.BeaconEntry - HasMinPower bool + MinerPower types.BigInt + NetworkPower types.BigInt + Sectors []builtin.SectorInfo + WorkerKey address.Address + SectorSize abi.SectorSize + PrevBeaconEntry types.BeaconEntry + BeaconEntries []types.BeaconEntry + EligibleForMining bool } type BlockTemplate struct { diff --git a/chain/stmgr/utils.go b/chain/stmgr/utils.go index 98f6bc5ac..6b43303f0 100644 --- a/chain/stmgr/utils.go +++ b/chain/stmgr/utils.go @@ -506,14 +506,14 @@ func MinerGetBaseInfo(ctx context.Context, sm *StateManager, bcs beacon.Schedule } return &api.MiningBaseInfo{ - MinerPower: mpow.QualityAdjPower, - NetworkPower: tpow.QualityAdjPower, - Sectors: sectors, - WorkerKey: worker, - SectorSize: info.SectorSize, - PrevBeaconEntry: *prev, - BeaconEntries: entries, - HasMinPower: hmp, + MinerPower: mpow.QualityAdjPower, + NetworkPower: tpow.QualityAdjPower, + Sectors: sectors, + WorkerKey: worker, + SectorSize: info.SectorSize, + PrevBeaconEntry: *prev, + BeaconEntries: entries, + EligibleForMining: hmp, }, nil } diff --git a/miner/miner.go b/miner/miner.go index d4e7b2317..a256e8b3a 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -362,7 +362,7 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg, if mbi == nil { return nil, nil } - if !mbi.HasMinPower { + if !mbi.EligibleForMining { // slashed or just have no power yet return nil, nil } From 4a9155ba854d05f8acccde58ddb059543be7fdc5 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Tue, 6 Oct 2020 04:33:55 -0400 Subject: [PATCH 2/3] Use new MinerEligibleToMine method post-v2 actors --- chain/actors/builtin/miner/miner.go | 2 + chain/actors/builtin/miner/v0.go | 7 +++ chain/actors/builtin/miner/v2.go | 5 ++ chain/actors/policy/policy.go | 1 - chain/stmgr/utils.go | 80 +++++++++++++++++++++++++++-- chain/sub/incoming.go | 6 +-- chain/sync.go | 6 +-- 7 files changed, 97 insertions(+), 10 deletions(-) diff --git a/chain/actors/builtin/miner/miner.go b/chain/actors/builtin/miner/miner.go index 125d142d3..8649d4351 100644 --- a/chain/actors/builtin/miner/miner.go +++ b/chain/actors/builtin/miner/miner.go @@ -58,6 +58,7 @@ type State interface { VestedFunds(abi.ChainEpoch) (abi.TokenAmount, error) // Funds locked for various reasons. LockedFunds() (LockedFunds, error) + FeeDebt() (abi.TokenAmount, error) GetSector(abi.SectorNumber) (*SectorOnChainInfo, error) FindSector(abi.SectorNumber) (*SectorLocation, error) @@ -144,6 +145,7 @@ type MinerInfo struct { SealProofType abi.RegisteredSealProof SectorSize abi.SectorSize WindowPoStPartitionSectors uint64 + ConsensusFaultElapsed abi.ChainEpoch } type SectorExpiration struct { diff --git a/chain/actors/builtin/miner/v0.go b/chain/actors/builtin/miner/v0.go index 902ed20f2..7e71c7611 100644 --- a/chain/actors/builtin/miner/v0.go +++ b/chain/actors/builtin/miner/v0.go @@ -4,6 +4,8 @@ import ( "bytes" "errors" + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-bitfield" "github.com/filecoin-project/go-state-types/abi" @@ -61,6 +63,10 @@ func (s *state0) LockedFunds() (LockedFunds, error) { }, nil } +func (s *state0) FeeDebt() (abi.TokenAmount, error) { + return big.Zero(), nil +} + func (s *state0) InitialPledge() (abi.TokenAmount, error) { return s.State.InitialPledgeRequirement, nil } @@ -287,6 +293,7 @@ func (s *state0) Info() (MinerInfo, error) { SealProofType: info.SealProofType, SectorSize: info.SectorSize, WindowPoStPartitionSectors: info.WindowPoStPartitionSectors, + ConsensusFaultElapsed: -1, } if info.PendingWorkerKey != nil { diff --git a/chain/actors/builtin/miner/v2.go b/chain/actors/builtin/miner/v2.go index 934554d23..e2b53e9a6 100644 --- a/chain/actors/builtin/miner/v2.go +++ b/chain/actors/builtin/miner/v2.go @@ -61,6 +61,10 @@ func (s *state2) LockedFunds() (LockedFunds, error) { }, nil } +func (s *state2) FeeDebt() (abi.TokenAmount, error) { + return s.State.FeeDebt, nil +} + func (s *state2) InitialPledge() (abi.TokenAmount, error) { return s.State.InitialPledge, nil } @@ -288,6 +292,7 @@ func (s *state2) Info() (MinerInfo, error) { SealProofType: info.SealProofType, SectorSize: info.SectorSize, WindowPoStPartitionSectors: info.WindowPoStPartitionSectors, + ConsensusFaultElapsed: info.ConsensusFaultElapsed, } if info.PendingWorkerKey != nil { diff --git a/chain/actors/policy/policy.go b/chain/actors/policy/policy.go index f29da9802..e7f213687 100644 --- a/chain/actors/policy/policy.go +++ b/chain/actors/policy/policy.go @@ -4,7 +4,6 @@ import ( "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/network" "github.com/filecoin-project/lotus/chain/actors" - market0 "github.com/filecoin-project/specs-actors/actors/builtin/market" miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner" power0 "github.com/filecoin-project/specs-actors/actors/builtin/power" diff --git a/chain/stmgr/utils.go b/chain/stmgr/utils.go index 6b43303f0..f2e2bfd19 100644 --- a/chain/stmgr/utils.go +++ b/chain/stmgr/utils.go @@ -9,6 +9,10 @@ import ( "runtime" "strings" + "github.com/filecoin-project/go-state-types/big" + + "github.com/filecoin-project/go-state-types/network" + cid "github.com/ipfs/go-cid" cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" @@ -490,7 +494,7 @@ func MinerGetBaseInfo(ctx context.Context, sm *StateManager, bcs beacon.Schedule return nil, nil } - mpow, tpow, hmp, err := GetPowerRaw(ctx, sm, lbst, maddr) + mpow, tpow, _, err := GetPowerRaw(ctx, sm, lbst, maddr) if err != nil { return nil, xerrors.Errorf("failed to get power: %w", err) } @@ -505,6 +509,12 @@ func MinerGetBaseInfo(ctx context.Context, sm *StateManager, bcs beacon.Schedule return nil, xerrors.Errorf("resolving worker address: %w", err) } + // TODO: Not ideal performance...This method reloads miner and power state (already looked up here and in GetPowerRaw) + eligible, err := MinerEligibleToMine(ctx, sm, maddr, ts, lbts) + if err != nil { + return nil, xerrors.Errorf("determining miner eligibility: %w", err) + } + return &api.MiningBaseInfo{ MinerPower: mpow.QualityAdjPower, NetworkPower: tpow.QualityAdjPower, @@ -513,7 +523,7 @@ func MinerGetBaseInfo(ctx context.Context, sm *StateManager, bcs beacon.Schedule SectorSize: info.SectorSize, PrevBeaconEntry: *prev, BeaconEntries: entries, - EligibleForMining: hmp, + EligibleForMining: eligible, }, nil } @@ -595,7 +605,7 @@ func GetReturnType(ctx context.Context, sm *StateManager, to address.Address, me return reflect.New(m.Ret.Elem()).Interface().(cbg.CBORUnmarshaler), nil } -func MinerHasMinPower(ctx context.Context, sm *StateManager, addr address.Address, ts *types.TipSet) (bool, error) { +func minerHasMinPower(ctx context.Context, sm *StateManager, addr address.Address, ts *types.TipSet) (bool, error) { pact, err := sm.LoadActor(ctx, power.Address, ts) if err != nil { return false, xerrors.Errorf("loading power actor state: %w", err) @@ -609,6 +619,70 @@ func MinerHasMinPower(ctx context.Context, sm *StateManager, addr address.Addres return ps.MinerNominalPowerMeetsConsensusMinimum(addr) } +func MinerEligibleToMine(ctx context.Context, sm *StateManager, addr address.Address, baseTs *types.TipSet, lookbackTs *types.TipSet) (bool, error) { + hmp, err := minerHasMinPower(ctx, sm, addr, lookbackTs) + + // TODO: We're blurring the lines between a "runtime network version" and a "Lotus upgrade epoch", is that unavoidable? + if sm.GetNtwkVersion(ctx, baseTs.Height()) <= network.Version3 { + return hmp, err + } + + if err != nil { + return false, err + } + + if !hmp { + return false, nil + } + + // Post actors v2, also check MinerEligibleForElection with base ts + + pact, err := sm.LoadActor(ctx, power.Address, baseTs) + if err != nil { + return false, xerrors.Errorf("loading power actor state: %w", err) + } + + pstate, err := power.Load(sm.cs.Store(ctx), pact) + if err != nil { + return false, err + } + + mact, err := sm.LoadActor(ctx, addr, baseTs) + if err != nil { + return false, xerrors.Errorf("loading miner actor state: %w", err) + } + + mstate, err := miner.Load(sm.cs.Store(ctx), mact) + if err != nil { + return false, err + } + + // Non-empty power claim. + if claim, found, err := pstate.MinerPower(addr); err != nil { + return false, err + } else if !found { + return false, err + } else if claim.QualityAdjPower.LessThanEqual(big.Zero()) { + return false, err + } + + // No fee debt. + if debt, err := mstate.FeeDebt(); err != nil { + return false, err + } else if !debt.IsZero() { + return false, err + } + + // No active consensus faults. + if mInfo, err := mstate.Info(); err != nil { + return false, err + } else if baseTs.Height() <= mInfo.ConsensusFaultElapsed { + return false, nil + } + + return true, nil +} + func CheckTotalFIL(ctx context.Context, sm *StateManager, ts *types.TipSet) (abi.TokenAmount, error) { str, err := state.LoadStateTree(sm.ChainStore().Store(ctx), ts.ParentState()) if err != nil { diff --git a/chain/sub/incoming.go b/chain/sub/incoming.go index 53bc3d68e..07b3343d2 100644 --- a/chain/sub/incoming.go +++ b/chain/sub/incoming.go @@ -485,14 +485,14 @@ func (bv *BlockValidator) checkPowerAndGetWorkerKey(ctx context.Context, bh *typ return address.Undef, ErrSoftFailure } - hmp, err := stmgr.MinerHasMinPower(ctx, bv.stmgr, bh.Miner, lbts) + eligible, err := stmgr.MinerEligibleToMine(ctx, bv.stmgr, bh.Miner, baseTs, lbts) if err != nil { log.Warnf("failed to determine if incoming block's miner has minimum power: %s", err) return address.Undef, ErrSoftFailure } - if !hmp { - log.Warnf("incoming block's miner does not have minimum power") + if !eligible { + log.Warnf("incoming block's miner is ineligible") return address.Undef, ErrInsufficientPower } diff --git a/chain/sync.go b/chain/sync.go index 46d3934b1..655bb4a1d 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -827,13 +827,13 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock, use return xerrors.Errorf("block is not claiming to be a winner") } - hp, err := stmgr.MinerHasMinPower(ctx, syncer.sm, h.Miner, lbts) + eligible, err := stmgr.MinerEligibleToMine(ctx, syncer.sm, h.Miner, baseTs, lbts) if err != nil { return xerrors.Errorf("determining if miner has min power failed: %w", err) } - if !hp { - return xerrors.New("block's miner does not meet minimum power threshold") + if !eligible { + return xerrors.New("block's miner is ineligible to mine") } rBeacon := *prevBeacon From c53cef15267f8124587e313561aec12b1361af00 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Tue, 6 Oct 2020 04:47:00 -0400 Subject: [PATCH 3/3] Update docs --- documentation/en/api-methods.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/documentation/en/api-methods.md b/documentation/en/api-methods.md index 1bae2c1e6..518d38089 100644 --- a/documentation/en/api-methods.md +++ b/documentation/en/api-methods.md @@ -1611,7 +1611,7 @@ Response: "Data": "Ynl0ZSBhcnJheQ==" }, "BeaconEntries": null, - "HasMinPower": true + "EligibleForMining": true } ``` @@ -3531,7 +3531,8 @@ Response: "Multiaddrs": null, "SealProofType": 3, "SectorSize": 34359738368, - "WindowPoStPartitionSectors": 42 + "WindowPoStPartitionSectors": 42, + "ConsensusFaultElapsed": 10101 } ```