diff --git a/api/api_full.go b/api/api_full.go index 543edb5d7..a75c8c39e 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -71,7 +71,7 @@ type FullNode interface { // miner - MinerGetBaseInfo(context.Context, address.Address, types.TipSetKey) (*MiningBaseInfo, error) + MinerGetBaseInfo(context.Context, address.Address, abi.ChainEpoch, types.TipSetKey) (*MiningBaseInfo, error) MinerCreateBlock(context.Context, *BlockTemplate) (*types.BlockMsg, error) // // UX ? @@ -382,7 +382,7 @@ type ComputeStateOutput struct { type MiningBaseInfo struct { MinerPower types.BigInt NetworkPower types.BigInt - Sectors []*ChainSectorInfo + Sectors []abi.SectorInfo WorkerKey address.Address SectorSize abi.SectorSize PrevBeaconEntry types.BeaconEntry diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index f74dda916..70ed95fb4 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -87,8 +87,8 @@ type FullNodeStruct struct { MpoolGetNonce func(context.Context, address.Address) (uint64, error) `perm:"read"` MpoolSub func(context.Context) (<-chan api.MpoolUpdate, error) `perm:"read"` - MinerGetBaseInfo func(context.Context, address.Address, types.TipSetKey) (*api.MiningBaseInfo, error) `perm:"read"` - MinerCreateBlock func(context.Context, *api.BlockTemplate) (*types.BlockMsg, error) `perm:"write"` + MinerGetBaseInfo func(context.Context, address.Address, abi.ChainEpoch, types.TipSetKey) (*api.MiningBaseInfo, error) `perm:"read"` + MinerCreateBlock func(context.Context, *api.BlockTemplate) (*types.BlockMsg, error) `perm:"write"` WalletNew func(context.Context, crypto.SigType) (address.Address, error) `perm:"write"` WalletHas func(context.Context, address.Address) (bool, error) `perm:"write"` @@ -332,8 +332,8 @@ func (c *FullNodeStruct) MpoolSub(ctx context.Context) (<-chan api.MpoolUpdate, return c.Internal.MpoolSub(ctx) } -func (c *FullNodeStruct) MinerGetBaseInfo(ctx context.Context, maddr address.Address, tsk types.TipSetKey) (*api.MiningBaseInfo, error) { - return c.Internal.MinerGetBaseInfo(ctx, maddr, tsk) +func (c *FullNodeStruct) MinerGetBaseInfo(ctx context.Context, maddr address.Address, epoch abi.ChainEpoch, tsk types.TipSetKey) (*api.MiningBaseInfo, error) { + return c.Internal.MinerGetBaseInfo(ctx, maddr, epoch, tsk) } func (c *FullNodeStruct) MinerCreateBlock(ctx context.Context, bt *api.BlockTemplate) (*types.BlockMsg, error) { diff --git a/build/params_shared.go b/build/params_shared.go index 310333710..52ce98d53 100644 --- a/build/params_shared.go +++ b/build/params_shared.go @@ -71,6 +71,8 @@ const MaxSealLookback = SealRandomnessLookbackLimit + 2000 // TODO: Get from spe // Epochs const TicketRandomnessLookback = 1 +const WinningPoStSectorSetLookback = 10 + // ///// // Devnet settings diff --git a/chain/gen/gen.go b/chain/gen/gen.go index 0e216d7ce..210342055 100644 --- a/chain/gen/gen.go +++ b/chain/gen/gen.go @@ -277,9 +277,9 @@ func CarWalkFunc(nd format.Node) (out []*format.Link, err error) { } func (cg *ChainGen) nextBlockProof(ctx context.Context, pts *types.TipSet, m address.Address, round abi.ChainEpoch) ([]types.BeaconEntry, *types.ElectionProof, *types.Ticket, error) { - mc := &mca{w: cg.w, sm: cg.sm} + mc := &mca{w: cg.w, sm: cg.sm, pv: ffiwrapper.ProofVerifier} - mbi, err := mc.MinerGetBaseInfo(ctx, m, pts.Key()) + mbi, err := mc.MinerGetBaseInfo(ctx, m, round, pts.Key()) if err != nil { return nil, nil, nil, xerrors.Errorf("get miner base info: %w", err) } @@ -476,7 +476,7 @@ func (cg *ChainGen) YieldRepo() (repo.Repo, error) { type MiningCheckAPI interface { ChainGetRandomness(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) - MinerGetBaseInfo(context.Context, address.Address, types.TipSetKey) (*api.MiningBaseInfo, error) + MinerGetBaseInfo(context.Context, address.Address, abi.ChainEpoch, types.TipSetKey) (*api.MiningBaseInfo, error) WalletSign(context.Context, address.Address, []byte) (*crypto.Signature, error) } @@ -484,6 +484,7 @@ type MiningCheckAPI interface { type mca struct { w *wallet.Wallet sm *stmgr.StateManager + pv ffiwrapper.Verifier } func (mca mca) ChainGetRandomness(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) { @@ -495,8 +496,8 @@ func (mca mca) ChainGetRandomness(ctx context.Context, tsk types.TipSetKey, pers return mca.sm.ChainStore().GetRandomness(ctx, pts.Cids(), personalization, randEpoch, entropy) } -func (mca mca) MinerGetBaseInfo(ctx context.Context, maddr address.Address, tsk types.TipSetKey) (*api.MiningBaseInfo, error) { - return stmgr.MinerGetBaseInfo(ctx, mca.sm, tsk, maddr) +func (mca mca) MinerGetBaseInfo(ctx context.Context, maddr address.Address, epoch abi.ChainEpoch, tsk types.TipSetKey) (*api.MiningBaseInfo, error) { + return stmgr.MinerGetBaseInfo(ctx, mca.sm, tsk, epoch, maddr, mca.pv) } func (mca mca) WalletSign(ctx context.Context, a address.Address, v []byte) (*crypto.Signature, error) { diff --git a/chain/stmgr/utils.go b/chain/stmgr/utils.go index 007a809ed..81900cdfc 100644 --- a/chain/stmgr/utils.go +++ b/chain/stmgr/utils.go @@ -2,6 +2,7 @@ package stmgr import ( "context" + amt "github.com/filecoin-project/go-amt-ipld/v2" "github.com/filecoin-project/sector-storage/ffiwrapper" "github.com/filecoin-project/specs-actors/actors/abi" @@ -16,6 +17,7 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" @@ -56,10 +58,10 @@ func GetMinerWorkerRaw(ctx context.Context, sm *StateManager, st cid.Cid, maddr } func GetPower(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (power.Claim, power.Claim, error) { - return getPowerRaw(ctx, sm, ts.ParentState(), maddr) + return GetPowerRaw(ctx, sm, ts.ParentState(), maddr) } -func getPowerRaw(ctx context.Context, sm *StateManager, st cid.Cid, maddr address.Address) (power.Claim, power.Claim, error) { +func GetPowerRaw(ctx context.Context, sm *StateManager, st cid.Cid, maddr address.Address) (power.Claim, power.Claim, error) { var ps power.State _, err := sm.LoadActorStateRaw(ctx, builtin.StoragePowerActorAddr, &ps, st) if err != nil { @@ -133,16 +135,11 @@ func GetMinerSectorSet(ctx context.Context, sm *StateManager, ts *types.TipSet, return LoadSectorsFromSet(ctx, sm.ChainStore().Blockstore(), mas.Sectors, filter) } -func GetSectorsForWinningPoSt(ctx context.Context, pv ffiwrapper.Verifier, sm *StateManager, ts *types.TipSet, maddr address.Address) ([]abi.SectorInfo, error) { - pts, err := sm.cs.LoadTipSet(ts.Parents()) // TODO: Review: check correct lookback for winningPost sector set - if err != nil { - return nil, xerrors.Errorf("loading parent tipset: %w", err) - } - +func GetSectorsForWinningPoSt(ctx context.Context, pv ffiwrapper.Verifier, sm *StateManager, st cid.Cid, maddr address.Address, rand abi.PoStRandomness) ([]abi.SectorInfo, error) { var mas miner.State - _, err = sm.LoadActorStateRaw(ctx, maddr, &mas, pts.ParentState()) + _, err := sm.LoadActorStateRaw(ctx, maddr, &mas, st) if err != nil { - return nil, xerrors.Errorf("(get ssize) failed to load miner actor state: %w", err) + return nil, xerrors.Errorf("(get sectors) failed to load miner actor state: %w", err) } // TODO: Optimization: we could avoid loaditg the whole proving set here if we had AMT.GetNth with bitfield filtering @@ -166,12 +163,6 @@ func GetSectorsForWinningPoSt(ctx context.Context, pv ffiwrapper.Verifier, sm *S return nil, xerrors.Errorf("getting miner ID: %w", err) } - // TODO: use the right dst, also NB: not using any 'entropy' in this call because nicola really didnt want it - rand, err := sm.cs.GetRandomness(ctx, ts.Cids(), crypto.DomainSeparationTag_ElectionPoStChallengeSeed, ts.Height()-1, nil) - if err != nil { - return nil, xerrors.Errorf("failed to get randomness for winning post: %w", err) - } - ids, err := pv.GenerateWinningPoStSectorChallenge(ctx, wpt, abi.ActorID(mid), rand, uint64(len(sectorSet))) if err != nil { return nil, xerrors.Errorf("generating winning post challenges: %w", err) @@ -424,29 +415,58 @@ func GetProvingSetRaw(ctx context.Context, sm *StateManager, mas miner.State) ([ return provset, nil } -func MinerGetBaseInfo(ctx context.Context, sm *StateManager, tsk types.TipSetKey, maddr address.Address) (*api.MiningBaseInfo, error) { +func GetLookbackTipSetForRound(ctx context.Context, sm *StateManager, ts *types.TipSet, round abi.ChainEpoch) (*types.TipSet, error) { + var lbr abi.ChainEpoch + if round > build.WinningPoStSectorSetLookback { + lbr = round - build.WinningPoStSectorSetLookback + } + + // more null blocks than our lookback + if lbr > ts.Height() { + return ts, nil + } + + lbts, err := sm.ChainStore().GetTipsetByHeight(ctx, lbr, ts) + if err != nil { + return nil, xerrors.Errorf("failed to get lookback tipset: %w", err) + } + + return lbts, nil +} + +func MinerGetBaseInfo(ctx context.Context, sm *StateManager, tsk types.TipSetKey, round abi.ChainEpoch, maddr address.Address, pv ffiwrapper.Verifier) (*api.MiningBaseInfo, error) { ts, err := sm.ChainStore().LoadTipSet(tsk) if err != nil { return nil, xerrors.Errorf("failed to load tipset for mining base: %w", err) } - st, _, err := sm.TipSetState(ctx, ts) + lbts, err := GetLookbackTipSetForRound(ctx, sm, ts, round) + 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 } var mas miner.State - _, err = sm.LoadActorState(ctx, maddr, &mas, ts) - if err != nil { - return nil, xerrors.Errorf("(get sset) failed to load miner actor state: %w", err) - } - - provset, err := GetProvingSetRaw(ctx, sm, mas) - if err != nil { + if _, err := sm.LoadActorStateRaw(ctx, maddr, &mas, lbst); err != nil { return nil, err } - mpow, tpow, err := getPowerRaw(ctx, sm, st, maddr) + // TODO: use the right dst, also NB: not using any 'entropy' in this call because nicola really didnt want it + prand, err := sm.cs.GetRandomness(ctx, ts.Cids(), crypto.DomainSeparationTag_ElectionPoStChallengeSeed, round-1, nil) + if err != nil { + return nil, xerrors.Errorf("failed to get randomness for winning post: %w", err) + } + + sectors, err := GetSectorsForWinningPoSt(ctx, pv, sm, lbst, maddr, prand) + if err != nil { + return nil, xerrors.Errorf("getting wpost proving set: %w", err) + } + + mpow, tpow, err := GetPowerRaw(ctx, sm, lbst, maddr) if err != nil { return nil, xerrors.Errorf("failed to get power: %w", err) } @@ -464,7 +484,7 @@ func MinerGetBaseInfo(ctx context.Context, sm *StateManager, tsk types.TipSetKey return &api.MiningBaseInfo{ MinerPower: mpow.QualityAdjPower, NetworkPower: tpow.QualityAdjPower, - Sectors: provset, + Sectors: sectors, WorkerKey: worker, SectorSize: mas.Info.SectorSize, PrevBeaconEntry: *prev, diff --git a/chain/store/store.go b/chain/store/store.go index d076d2599..d58d8cf2d 100644 --- a/chain/store/store.go +++ b/chain/store/store.go @@ -942,6 +942,10 @@ func (cs *ChainStore) GetTipsetByHeight(ctx context.Context, h abi.ChainEpoch, t return nil, xerrors.Errorf("looking for tipset with height less than start point") } + if h == ts.Height() { + return ts, nil + } + if ts.Height()-h > build.ForkLengthThreshold { log.Warnf("expensive call to GetTipsetByHeight, seeking %d levels", ts.Height()-h) } @@ -955,6 +959,9 @@ func (cs *ChainStore) GetTipsetByHeight(ctx context.Context, h abi.ChainEpoch, t if h > pts.Height() { return ts, nil } + if h == pts.Height() { + return pts, nil + } ts = pts } diff --git a/chain/sync.go b/chain/sync.go index 45dd562ae..21ec32402 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -519,6 +519,16 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err return xerrors.Errorf("load parent tipset failed (%s): %w", h.Parents, err) } + lbts, 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: %w", err) + } + prevBeacon, err := syncer.store.GetLatestBeaconEntry(baseTs) if err != nil { return xerrors.Errorf("failed to get latest beacon entry: %w", err) @@ -585,7 +595,7 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err return xerrors.Errorf("parent receipts root did not match computed value (%s != %s)", precp, h.ParentMessageReceipts) } - waddr, err := stmgr.GetMinerWorkerRaw(ctx, syncer.sm, stateroot, h.Miner) + waddr, err := stmgr.GetMinerWorkerRaw(ctx, syncer.sm, lbst, h.Miner) if err != nil { return xerrors.Errorf("GetMinerWorkerRaw failed: %w", err) } @@ -620,7 +630,7 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err return xerrors.Errorf("received block was from slashed or invalid miner") } - mpow, tpow, err := stmgr.GetPower(ctx, syncer.sm, baseTs, h.Miner) + mpow, tpow, err := stmgr.GetPowerRaw(ctx, syncer.sm, lbst, h.Miner) if err != nil { return xerrors.Errorf("failed getting power: %w", err) } @@ -666,7 +676,7 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err }) wproofCheck := async.Err(func() error { - if err := syncer.VerifyWinningPoStProof(ctx, h, waddr); err != nil { + if err := syncer.VerifyWinningPoStProof(ctx, h, lbst, waddr); err != nil { return xerrors.Errorf("invalid election post: %w", err) } return nil @@ -709,7 +719,7 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err return merr } -func (syncer *Syncer) VerifyWinningPoStProof(ctx context.Context, h *types.BlockHeader, waddr address.Address) error { +func (syncer *Syncer) VerifyWinningPoStProof(ctx context.Context, h *types.BlockHeader, lbst cid.Cid, waddr address.Address) error { if build.InsecurePoStValidation { if len(h.WinPoStProof) == 0 { return xerrors.Errorf("[TESTING] No winning post proof given") @@ -737,7 +747,7 @@ func (syncer *Syncer) VerifyWinningPoStProof(ctx context.Context, h *types.Block return xerrors.Errorf("failed to get ID from miner address %s: %w", h.Miner, err) } - sectors, err := stmgr.GetSectorsForWinningPoSt(ctx, syncer.verifier, syncer.sm, curTs, h.Miner) + sectors, err := stmgr.GetSectorsForWinningPoSt(ctx, syncer.verifier, syncer.sm, lbst, h.Miner, rand) if err != nil { return xerrors.Errorf("getting winning post sector set: %w", err) } diff --git a/miner/miner.go b/miner/miner.go index 3501bd347..161fbddff 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -294,14 +294,15 @@ func (m *Miner) mineOne(ctx context.Context, addr address.Address, base *MiningB log.Debugw("attempting to mine a block", "tipset", types.LogCids(base.ts.Cids())) start := time.Now() - mbi, err := m.api.MinerGetBaseInfo(ctx, addr, base.ts.Key()) + round := base.ts.Height() + base.nullRounds + 1 + + mbi, err := m.api.MinerGetBaseInfo(ctx, addr, round, base.ts.Key()) if err != nil { return nil, xerrors.Errorf("failed to get mining base info: %w", err) } beaconPrev := mbi.PrevBeaconEntry - round := base.ts.Height() + base.nullRounds + 1 bvals, err := beacon.BeaconEntriesForBlock(ctx, m.beacon, round, beaconPrev) if err != nil { return nil, xerrors.Errorf("get beacon entries failed: %w", err) @@ -347,14 +348,7 @@ func (m *Miner) mineOne(ctx context.Context, addr address.Address, base *MiningB prand := abi.PoStRandomness(rand) - sx, err := m.epp.GenerateCandidates(ctx, prand, uint64(len(mbi.Sectors))) - if err != nil { - return nil, xerrors.Errorf("failed to generate candidates for winning post: %w", err) - } - - si := mbi.Sectors[sx[0]] - postInp := []abi.SectorInfo{si.Info.AsSectorInfo()} - postProof, err := m.epp.ComputeProof(ctx, postInp, prand) + postProof, err := m.epp.ComputeProof(ctx, mbi.Sectors, prand) if err != nil { return nil, xerrors.Errorf("failed to compute winning post proof: %w", err) } diff --git a/node/impl/full/state.go b/node/impl/full/state.go index 0a48ca7cd..168c8d536 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -25,6 +25,7 @@ import ( "github.com/filecoin-project/lotus/chain/wallet" "github.com/filecoin-project/lotus/lib/bufbstore" "github.com/filecoin-project/lotus/node/modules/dtypes" + "github.com/filecoin-project/sector-storage/ffiwrapper" "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" @@ -40,8 +41,9 @@ type StateAPI struct { // API attached to the state API. It probably should live somewhere better Wallet *wallet.Wallet - StateManager *stmgr.StateManager - Chain *store.ChainStore + ProofVerifier ffiwrapper.Verifier + StateManager *stmgr.StateManager + Chain *store.ChainStore } func (a *StateAPI) StateNetworkName(ctx context.Context) (dtypes.NetworkName, error) { @@ -252,8 +254,8 @@ func (a *StateAPI) StateReadState(ctx context.Context, act *types.Actor, tsk typ } // This is on StateAPI because miner.Miner requires this, and MinerAPI requires miner.Miner -func (a *StateAPI) MinerGetBaseInfo(ctx context.Context, maddr address.Address, tsk types.TipSetKey) (*api.MiningBaseInfo, error) { - return stmgr.MinerGetBaseInfo(ctx, a.StateManager, tsk, maddr) +func (a *StateAPI) MinerGetBaseInfo(ctx context.Context, maddr address.Address, epoch abi.ChainEpoch, tsk types.TipSetKey) (*api.MiningBaseInfo, error) { + return stmgr.MinerGetBaseInfo(ctx, a.StateManager, tsk, epoch, maddr, a.ProofVerifier) } func (a *StateAPI) MinerCreateBlock(ctx context.Context, bt *api.BlockTemplate) (*types.BlockMsg, error) {