Merge pull request #2618 from filecoin-project/asr/has-power
Use specs actor's MinerNominalPowerMeetsConsensusMinimum
This commit is contained in:
commit
2bc1a7e327
@ -641,6 +641,7 @@ type MiningBaseInfo struct {
|
||||
SectorSize abi.SectorSize
|
||||
PrevBeaconEntry types.BeaconEntry
|
||||
BeaconEntries []types.BeaconEntry
|
||||
HasMinPower bool
|
||||
}
|
||||
|
||||
type BlockTemplate struct {
|
||||
|
@ -567,6 +567,11 @@ func MinerGetBaseInfo(ctx context.Context, sm *StateManager, bcn beacon.RandomBe
|
||||
return nil, xerrors.Errorf("resolving worker address: %w", err)
|
||||
}
|
||||
|
||||
hmp, err := MinerHasMinPower(ctx, sm, maddr, lbts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("determining if miner has min power failed: %w", err)
|
||||
}
|
||||
|
||||
return &api.MiningBaseInfo{
|
||||
MinerPower: mpow.QualityAdjPower,
|
||||
NetworkPower: tpow.QualityAdjPower,
|
||||
@ -575,6 +580,7 @@ func MinerGetBaseInfo(ctx context.Context, sm *StateManager, bcn beacon.RandomBe
|
||||
SectorSize: info.SectorSize,
|
||||
PrevBeaconEntry: *prev,
|
||||
BeaconEntries: entries,
|
||||
HasMinPower: hmp,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -659,3 +665,13 @@ func GetReturnType(ctx context.Context, sm *StateManager, to address.Address, me
|
||||
m := MethodsMap[act.Code][method]
|
||||
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) {
|
||||
var ps power.State
|
||||
_, err := sm.LoadActorState(ctx, builtin.StoragePowerActorAddr, &ps, ts)
|
||||
if err != nil {
|
||||
return false, xerrors.Errorf("loading power actor state: %w", err)
|
||||
}
|
||||
|
||||
return ps.MinerNominalPowerMeetsConsensusMinimum(sm.ChainStore().Store(ctx), addr)
|
||||
}
|
||||
|
@ -191,14 +191,14 @@ func (bv *BlockValidator) Validate(ctx context.Context, pid peer.ID, msg *pubsub
|
||||
// if we can't find it, we check whether we are (near) synced in the chain.
|
||||
// if we are not synced we cannot validate the block and we must ignore it.
|
||||
// if we are synced and the miner is unknown, then the block is rejcected.
|
||||
key, err := bv.getMinerWorkerKey(ctx, blk)
|
||||
key, err := bv.checkPowerAndGetWorkerKey(ctx, blk.Header)
|
||||
if err != nil {
|
||||
if bv.isChainNearSynced() {
|
||||
log.Warnf("received block message from unknown miner over pubsub; rejecting message")
|
||||
log.Warnf("received block from unknown miner or miner that doesn't meet min power over pubsub; rejecting message")
|
||||
recordFailure("unknown_miner")
|
||||
return pubsub.ValidationReject
|
||||
} else {
|
||||
log.Warnf("cannot validate block message; unknown miner in unsynced chain")
|
||||
log.Warnf("cannot validate block message; unknown miner or miner that doesn't meet min power in unsynced chain")
|
||||
return pubsub.ValidationIgnore
|
||||
}
|
||||
}
|
||||
@ -283,60 +283,83 @@ func (bv *BlockValidator) validateMsgMeta(ctx context.Context, msg *types.BlockM
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bv *BlockValidator) getMinerWorkerKey(ctx context.Context, msg *types.BlockMsg) (address.Address, error) {
|
||||
addr := msg.Header.Miner
|
||||
func (bv *BlockValidator) checkPowerAndGetWorkerKey(ctx context.Context, bh *types.BlockHeader) (address.Address, error) {
|
||||
addr := bh.Miner
|
||||
|
||||
bv.mx.Lock()
|
||||
key, ok := bv.keycache[addr.String()]
|
||||
bv.mx.Unlock()
|
||||
if ok {
|
||||
return key, nil
|
||||
if !ok {
|
||||
// TODO I have a feeling all this can be simplified by cleverer DI to use the API
|
||||
ts := bv.chain.GetHeaviestTipSet()
|
||||
st, _, err := bv.stmgr.TipSetState(ctx, ts)
|
||||
if err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
buf := bufbstore.NewBufferedBstore(bv.chain.Blockstore())
|
||||
cst := cbor.NewCborStore(buf)
|
||||
state, err := state.LoadStateTree(cst, st)
|
||||
if err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
act, err := state.GetActor(addr)
|
||||
if err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
|
||||
blk, err := bv.chain.Blockstore().Get(act.Head)
|
||||
if err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
aso := blk.RawData()
|
||||
|
||||
var mst miner.State
|
||||
err = mst.UnmarshalCBOR(bytes.NewReader(aso))
|
||||
if err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
|
||||
info, err := mst.GetInfo(adt.WrapStore(ctx, cst))
|
||||
if err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
|
||||
worker := info.Worker
|
||||
key, err = bv.stmgr.ResolveToKeyAddress(ctx, worker, ts)
|
||||
if err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
|
||||
bv.mx.Lock()
|
||||
bv.keycache[addr.String()] = key
|
||||
bv.mx.Unlock()
|
||||
}
|
||||
|
||||
// TODO I have a feeling all this can be simplified by cleverer DI to use the API
|
||||
ts := bv.chain.GetHeaviestTipSet()
|
||||
st, _, err := bv.stmgr.TipSetState(ctx, ts)
|
||||
if err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
buf := bufbstore.NewBufferedBstore(bv.chain.Blockstore())
|
||||
cst := cbor.NewCborStore(buf)
|
||||
state, err := state.LoadStateTree(cst, st)
|
||||
if err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
act, err := state.GetActor(addr)
|
||||
// we check that the miner met the minimum power at the lookback tipset
|
||||
|
||||
baseTs, err := bv.stmgr.ChainStore().LoadTipSet(types.NewTipSetKey(bh.Parents...))
|
||||
if err != nil {
|
||||
log.Warnf("failed to load parent tipset of incoming block")
|
||||
return address.Undef, err
|
||||
}
|
||||
|
||||
blk, err := bv.chain.Blockstore().Get(act.Head)
|
||||
if err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
aso := blk.RawData()
|
||||
|
||||
var mst miner.State
|
||||
err = mst.UnmarshalCBOR(bytes.NewReader(aso))
|
||||
lbts, err := stmgr.GetLookbackTipSetForRound(ctx, bv.stmgr, baseTs, bh.Height)
|
||||
if err != nil {
|
||||
log.Warnf("failed to load lookback tipset for incoming block")
|
||||
return address.Undef, err
|
||||
}
|
||||
|
||||
info, err := mst.GetInfo(adt.WrapStore(ctx, cst))
|
||||
hmp, err := stmgr.MinerHasMinPower(ctx, bv.stmgr, bh.Miner, lbts)
|
||||
if err != nil {
|
||||
log.Warnf("failed to determine if incoming block's miner has minimum power")
|
||||
return address.Undef, err
|
||||
}
|
||||
|
||||
worker := info.Worker
|
||||
key, err = bv.stmgr.ResolveToKeyAddress(ctx, worker, ts)
|
||||
if err != nil {
|
||||
return address.Undef, err
|
||||
if !hmp {
|
||||
log.Warnf("incoming block's miner does not have minimum power")
|
||||
return address.Undef, xerrors.New("incoming block's miner does not have minimum power")
|
||||
}
|
||||
|
||||
bv.mx.Lock()
|
||||
bv.keycache[addr.String()] = key
|
||||
bv.mx.Unlock()
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
|
@ -739,6 +739,15 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) (er
|
||||
return xerrors.Errorf("block is not claiming to be a winner")
|
||||
}
|
||||
|
||||
hp, err := stmgr.MinerHasMinPower(ctx, syncer.sm, h.Miner, 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")
|
||||
}
|
||||
|
||||
rBeacon := *prevBeacon
|
||||
if len(h.BeaconEntries) != 0 {
|
||||
rBeacon = h.BeaconEntries[len(h.BeaconEntries)-1]
|
||||
|
@ -14,7 +14,6 @@ import (
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
|
||||
@ -269,15 +268,6 @@ func (m *Miner) GetBestMiningCandidate(ctx context.Context) (*MiningBase, error)
|
||||
return m.lastWork, nil
|
||||
}
|
||||
|
||||
func (m *Miner) hasPower(ctx context.Context, addr address.Address, ts *types.TipSet) (bool, error) {
|
||||
mpower, err := m.api.StateMinerPower(ctx, addr, ts.Key())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return mpower.MinerPower.QualityAdjPower.GreaterThanEqual(power.ConsensusMinerMinPower), nil
|
||||
}
|
||||
|
||||
// mineOne attempts to mine a single block, and does so synchronously, if and
|
||||
// only if we are eligible to mine.
|
||||
//
|
||||
@ -301,6 +291,10 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg,
|
||||
if mbi == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if !mbi.HasMinPower {
|
||||
// slashed or just have no power yet
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
tMBI := build.Clock.Now()
|
||||
|
||||
@ -309,15 +303,6 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg,
|
||||
tDrand := build.Clock.Now()
|
||||
bvals := mbi.BeaconEntries
|
||||
|
||||
hasPower, err := m.hasPower(ctx, m.address, base.TipSet)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("checking if miner is slashed: %w", err)
|
||||
}
|
||||
if !hasPower {
|
||||
// slashed or just have no power yet
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
tPowercheck := build.Clock.Now()
|
||||
|
||||
log.Infof("Time delta between now and our mining base: %ds (nulls: %d)", uint64(build.Clock.Now().Unix())-base.TipSet.MinTimestamp(), base.NullRounds)
|
||||
|
Loading…
Reference in New Issue
Block a user