diff --git a/chain/gen/mining.go b/chain/gen/mining.go index 5fc56e8b2..cca4b6169 100644 --- a/chain/gen/mining.go +++ b/chain/gen/mining.go @@ -27,16 +27,11 @@ func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w api.WalletA return nil, xerrors.Errorf("failed to load tipset state: %w", err) } - lbts, err := stmgr.GetLookbackTipSetForRound(ctx, sm, pts, bt.Epoch) + _, lbst, err := stmgr.GetLookbackTipSetForRound(ctx, sm, pts, bt.Epoch) if err != nil { return nil, xerrors.Errorf("getting lookback miner actor state: %w", err) } - lbst, _, err := sm.TipSetState(ctx, lbts) - if err != nil { - return nil, err - } - worker, err := stmgr.GetMinerWorkerRaw(ctx, sm, lbst, bt.Miner) if err != nil { return nil, xerrors.Errorf("failed to get miner worker: %w", err) diff --git a/chain/stmgr/utils.go b/chain/stmgr/utils.go index 54f75c138..486805786 100644 --- a/chain/stmgr/utils.go +++ b/chain/stmgr/utils.go @@ -391,7 +391,7 @@ func ComputeState(ctx context.Context, sm *StateManager, height abi.ChainEpoch, return root, trace, nil } -func GetLookbackTipSetForRound(ctx context.Context, sm *StateManager, ts *types.TipSet, round abi.ChainEpoch) (*types.TipSet, error) { +func GetLookbackTipSetForRound(ctx context.Context, sm *StateManager, ts *types.TipSet, round abi.ChainEpoch) (*types.TipSet, cid.Cid, error) { var lbr abi.ChainEpoch lb := policy.GetWinningPoStSectorSetLookback(sm.GetNtwkVersion(ctx, round)) if round > lb { @@ -399,16 +399,33 @@ func GetLookbackTipSetForRound(ctx context.Context, sm *StateManager, ts *types. } // more null blocks than our lookback - if lbr > ts.Height() { - return ts, nil + if lbr >= ts.Height() { + // This should never happen at this point, but may happen before + // network version 3 (where the lookback was only 10 blocks). + st, _, err := sm.TipSetState(ctx, ts) + if err != nil { + return nil, cid.Undef, err + } + return ts, st, nil } - lbts, err := sm.ChainStore().GetTipsetByHeight(ctx, lbr, ts, true) + // Get the tipset after the lookback tipset, or the next non-null one. + nextTs, err := sm.ChainStore().GetTipsetByHeight(ctx, lbr+1, ts, false) if err != nil { - return nil, xerrors.Errorf("failed to get lookback tipset: %w", err) + return nil, cid.Undef, xerrors.Errorf("failed to get lookback tipset+1: %w", err) } - return lbts, nil + if lbr > nextTs.Height() { + return nil, cid.Undef, xerrors.Errorf("failed to find non-null tipset %s (%d) which is known to exist, found %s (%d)", ts.Key(), ts.Height(), nextTs.Key(), nextTs.Height()) + + } + + lbts, err := sm.ChainStore().GetTipSetFromKey(nextTs.Parents()) + if err != nil { + return nil, cid.Undef, xerrors.Errorf("failed to resolve lookback tipset: %w", err) + } + + return lbts, nextTs.ParentState(), nil } func MinerGetBaseInfo(ctx context.Context, sm *StateManager, bcs beacon.Schedule, tsk types.TipSetKey, round abi.ChainEpoch, maddr address.Address, pv ffiwrapper.Verifier) (*api.MiningBaseInfo, error) { @@ -436,17 +453,11 @@ func MinerGetBaseInfo(ctx context.Context, sm *StateManager, bcs beacon.Schedule rbase = entries[len(entries)-1] } - lbts, err := GetLookbackTipSetForRound(ctx, sm, ts, round) + lbts, lbst, err := GetLookbackTipSetForRound(ctx, sm, ts, round) if err != nil { return nil, xerrors.Errorf("getting lookback miner actor state: %w", err) } - // TODO: load the state instead of computing it? - lbst, _, err := sm.TipSetState(ctx, lbts) - if err != nil { - return nil, err - } - act, err := sm.LoadActorRaw(ctx, maddr, lbst) if xerrors.Is(err, types.ErrActorNotFound) { _, err := sm.LoadActor(ctx, maddr, ts) diff --git a/chain/sub/incoming.go b/chain/sub/incoming.go index 625c8d1e2..1701866eb 100644 --- a/chain/sub/incoming.go +++ b/chain/sub/incoming.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "sync" "time" "golang.org/x/xerrors" @@ -27,14 +26,11 @@ import ( "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain" - "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/messagepool" - "github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/lib/blockstore" - "github.com/filecoin-project/lotus/lib/bufbstore" "github.com/filecoin-project/lotus/lib/sigs" "github.com/filecoin-project/lotus/metrics" "github.com/filecoin-project/lotus/node/impl/client" @@ -227,9 +223,6 @@ type BlockValidator struct { // necessary for block validation chain *store.ChainStore stmgr *stmgr.StateManager - - mx sync.Mutex - keycache map[string]address.Address } func NewBlockValidator(self peer.ID, chain *store.ChainStore, stmgr *stmgr.StateManager, blacklist func(peer.ID)) *BlockValidator { @@ -242,7 +235,6 @@ func NewBlockValidator(self peer.ID, chain *store.ChainStore, stmgr *stmgr.State recvBlocks: newBlockReceiptCache(), chain: chain, stmgr: stmgr, - keycache: make(map[string]address.Address), } } @@ -436,60 +428,25 @@ func (bv *BlockValidator) validateMsgMeta(ctx context.Context, msg *types.BlockM } 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 { - // 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 - } - - mst, err := miner.Load(bv.chain.Store(ctx), act) - if err != nil { - return address.Undef, err - } - - info, err := mst.Info() - 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() - } - // we check that the miner met the minimum power at the lookback tipset baseTs := bv.chain.GetHeaviestTipSet() - lbts, err := stmgr.GetLookbackTipSetForRound(ctx, bv.stmgr, baseTs, bh.Height) + lbts, lbst, err := stmgr.GetLookbackTipSetForRound(ctx, bv.stmgr, baseTs, bh.Height) if err != nil { log.Warnf("failed to load lookback tipset for incoming block: %s", err) return address.Undef, ErrSoftFailure } + key, err := stmgr.GetMinerWorkerRaw(ctx, bv.stmgr, lbst, bh.Miner) + if err != nil { + log.Warnf("failed to resolve worker key for miner %s: %s", bh.Miner, err) + return address.Undef, ErrSoftFailure + } + + // NOTE: we check to see if the miner was eligible in the lookback + // tipset - 1 for historical reasons. DO NOT use the lookback state + // returned by GetLookbackTipSetForRound. + 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) diff --git a/chain/sync.go b/chain/sync.go index 8da093cb6..1410dd2a7 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -730,16 +730,11 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock, use return xerrors.Errorf("load parent tipset failed (%s): %w", h.Parents, err) } - lbts, err := stmgr.GetLookbackTipSetForRound(ctx, syncer.sm, baseTs, h.Height) + lbts, lbst, err := stmgr.GetLookbackTipSetForRound(ctx, syncer.sm, baseTs, h.Height) if err != nil { return xerrors.Errorf("failed to get lookback tipset for block: %w", err) } - lbst, _, err := syncer.sm.TipSetState(ctx, lbts) - if err != nil { - return xerrors.Errorf("failed to compute lookback tipset state (epoch %d): %w", lbts.Height(), err) - } - prevBeacon, err := syncer.store.GetLatestBeaconEntry(baseTs) if err != nil { return xerrors.Errorf("failed to get latest beacon entry: %w", err)