Merge pull request #4188 from filecoin-project/asr/miner-eligible
Update miner eligibility checks for v2 actors
This commit is contained in:
commit
9857e42206
@ -813,7 +813,7 @@ type MiningBaseInfo struct {
|
||||
SectorSize abi.SectorSize
|
||||
PrevBeaconEntry types.BeaconEntry
|
||||
BeaconEntries []types.BeaconEntry
|
||||
HasMinPower bool
|
||||
EligibleForMining bool
|
||||
}
|
||||
|
||||
type BlockTemplate struct {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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"
|
||||
|
@ -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,
|
||||
HasMinPower: 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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user