package sealing import ( "context" "errors" "sync" "time" "github.com/ipfs/go-cid" "github.com/ipfs/go-datastore" "github.com/ipfs/go-datastore/namespace" logging "github.com/ipfs/go-log/v2" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/go-state-types/dline" "github.com/filecoin-project/go-state-types/network" statemachine "github.com/filecoin-project/go-statemachine" "github.com/filecoin-project/specs-storage/storage" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/actors/builtin/market" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/types" sectorstorage "github.com/filecoin-project/lotus/extern/sector-storage" "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper" "github.com/filecoin-project/lotus/extern/storage-sealing/sealiface" "github.com/filecoin-project/lotus/node/config" ) const SectorStorePrefix = "/sectors" var ErrTooManySectorsSealing = xerrors.New("too many sectors sealing") var log = logging.Logger("sectors") type SectorLocation struct { Deadline uint64 Partition uint64 } var ErrSectorAllocated = errors.New("sectorNumber is allocated, but PreCommit info wasn't found on chain") type SealingAPI interface { StateWaitMsg(context.Context, cid.Cid) (MsgLookup, error) StateSearchMsg(context.Context, cid.Cid) (*MsgLookup, error) StateComputeDataCommitment(ctx context.Context, maddr address.Address, sectorType abi.RegisteredSealProof, deals []abi.DealID, tok TipSetToken) (cid.Cid, error) // Can return ErrSectorAllocated in case precommit info wasn't found, but the sector number is marked as allocated StateSectorPreCommitInfo(ctx context.Context, maddr address.Address, sectorNumber abi.SectorNumber, tok TipSetToken) (*miner.SectorPreCommitOnChainInfo, error) StateSectorGetInfo(ctx context.Context, maddr address.Address, sectorNumber abi.SectorNumber, tok TipSetToken) (*miner.SectorOnChainInfo, error) StateSectorPartition(ctx context.Context, maddr address.Address, sectorNumber abi.SectorNumber, tok TipSetToken) (*SectorLocation, error) StateLookupID(context.Context, address.Address, TipSetToken) (address.Address, error) StateMinerSectorSize(context.Context, address.Address, TipSetToken) (abi.SectorSize, error) StateMinerWorkerAddress(ctx context.Context, maddr address.Address, tok TipSetToken) (address.Address, error) StateMinerPreCommitDepositForPower(context.Context, address.Address, miner.SectorPreCommitInfo, TipSetToken) (big.Int, error) StateMinerInitialPledgeCollateral(context.Context, address.Address, miner.SectorPreCommitInfo, TipSetToken) (big.Int, error) StateMinerInfo(context.Context, address.Address, TipSetToken) (miner.MinerInfo, error) StateMinerAvailableBalance(context.Context, address.Address, TipSetToken) (big.Int, error) StateMinerSectorAllocated(context.Context, address.Address, abi.SectorNumber, TipSetToken) (bool, error) StateMarketStorageDeal(context.Context, abi.DealID, TipSetToken) (*api.MarketDeal, error) StateMarketStorageDealProposal(context.Context, abi.DealID, TipSetToken) (market.DealProposal, error) StateNetworkVersion(ctx context.Context, tok TipSetToken) (network.Version, error) StateMinerProvingDeadline(context.Context, address.Address, TipSetToken) (*dline.Info, error) StateMinerPartitions(ctx context.Context, m address.Address, dlIdx uint64, tok TipSetToken) ([]api.Partition, error) SendMsg(ctx context.Context, from, to address.Address, method abi.MethodNum, value, maxFee abi.TokenAmount, params []byte) (cid.Cid, error) ChainHead(ctx context.Context) (TipSetToken, abi.ChainEpoch, error) ChainBaseFee(context.Context, TipSetToken) (abi.TokenAmount, error) ChainGetMessage(ctx context.Context, mc cid.Cid) (*types.Message, error) ChainGetRandomnessFromBeacon(ctx context.Context, tok TipSetToken, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) ChainGetRandomnessFromTickets(ctx context.Context, tok TipSetToken, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) ChainReadObj(context.Context, cid.Cid) ([]byte, error) } type SectorStateNotifee func(before, after SectorInfo) type AddrSel func(ctx context.Context, mi miner.MinerInfo, use api.AddrUse, goodFunds, minFunds abi.TokenAmount) (address.Address, abi.TokenAmount, error) type Sealing struct { api SealingAPI feeCfg config.MinerFeeConfig events Events startupWait sync.WaitGroup maddr address.Address sealer sectorstorage.SectorManager sectors *statemachine.StateGroup sc SectorIDCounter verif ffiwrapper.Verifier pcp PreCommitPolicy inputLk sync.Mutex openSectors map[abi.SectorID]*openSector sectorTimers map[abi.SectorID]*time.Timer pendingPieces map[cid.Cid]*pendingPiece assignedPieces map[abi.SectorID][]cid.Cid creating *abi.SectorNumber // used to prevent a race where we could create a new sector more than once upgradeLk sync.Mutex toUpgrade map[abi.SectorNumber]struct{} notifee SectorStateNotifee addrSel AddrSel stats SectorStats terminator *TerminateBatcher precommiter *PreCommitBatcher commiter *CommitBatcher getConfig GetSealingConfigFunc dealInfo *CurrentDealInfoManager } type openSector struct { used abi.UnpaddedPieceSize // change to bitfield/rle when AddPiece gains offset support to better fill sectors maybeAccept func(cid.Cid) error // called with inputLk } type pendingPiece struct { size abi.UnpaddedPieceSize deal api.PieceDealInfo data storage.Data assigned bool // assigned to a sector? accepted func(abi.SectorNumber, abi.UnpaddedPieceSize, error) } func New(mctx context.Context, api SealingAPI, fc config.MinerFeeConfig, events Events, maddr address.Address, ds datastore.Batching, sealer sectorstorage.SectorManager, sc SectorIDCounter, verif ffiwrapper.Verifier, prov ffiwrapper.Prover, pcp PreCommitPolicy, gc GetSealingConfigFunc, notifee SectorStateNotifee, as AddrSel) *Sealing { s := &Sealing{ api: api, feeCfg: fc, events: events, maddr: maddr, sealer: sealer, sc: sc, verif: verif, pcp: pcp, openSectors: map[abi.SectorID]*openSector{}, sectorTimers: map[abi.SectorID]*time.Timer{}, pendingPieces: map[cid.Cid]*pendingPiece{}, assignedPieces: map[abi.SectorID][]cid.Cid{}, toUpgrade: map[abi.SectorNumber]struct{}{}, notifee: notifee, addrSel: as, terminator: NewTerminationBatcher(mctx, maddr, api, as, fc, gc), precommiter: NewPreCommitBatcher(mctx, maddr, api, as, fc, gc), commiter: NewCommitBatcher(mctx, maddr, api, as, fc, gc, prov), getConfig: gc, dealInfo: &CurrentDealInfoManager{api}, stats: SectorStats{ bySector: map[abi.SectorID]statSectorState{}, }, } s.startupWait.Add(1) s.sectors = statemachine.New(namespace.Wrap(ds, datastore.NewKey(SectorStorePrefix)), s, SectorInfo{}) return s } func (m *Sealing) Run(ctx context.Context) error { if err := m.restartSectors(ctx); err != nil { log.Errorf("%+v", err) return xerrors.Errorf("failed load sector states: %w", err) } return nil } func (m *Sealing) Stop(ctx context.Context) error { if err := m.terminator.Stop(ctx); err != nil { return err } if err := m.sectors.Stop(ctx); err != nil { return err } return nil } func (m *Sealing) Remove(ctx context.Context, sid abi.SectorNumber) error { m.startupWait.Wait() return m.sectors.Send(uint64(sid), SectorRemove{}) } func (m *Sealing) Terminate(ctx context.Context, sid abi.SectorNumber) error { m.startupWait.Wait() return m.sectors.Send(uint64(sid), SectorTerminate{}) } func (m *Sealing) TerminateFlush(ctx context.Context) (*cid.Cid, error) { return m.terminator.Flush(ctx) } func (m *Sealing) TerminatePending(ctx context.Context) ([]abi.SectorID, error) { return m.terminator.Pending(ctx) } func (m *Sealing) SectorPreCommitFlush(ctx context.Context) ([]sealiface.PreCommitBatchRes, error) { return m.precommiter.Flush(ctx) } func (m *Sealing) SectorPreCommitPending(ctx context.Context) ([]abi.SectorID, error) { return m.precommiter.Pending(ctx) } func (m *Sealing) CommitFlush(ctx context.Context) ([]sealiface.CommitBatchRes, error) { return m.commiter.Flush(ctx) } func (m *Sealing) CommitPending(ctx context.Context) ([]abi.SectorID, error) { return m.commiter.Pending(ctx) } func (m *Sealing) currentSealProof(ctx context.Context) (abi.RegisteredSealProof, error) { mi, err := m.api.StateMinerInfo(ctx, m.maddr, nil) if err != nil { return 0, err } ver, err := m.api.StateNetworkVersion(ctx, nil) if err != nil { return 0, err } return miner.PreferredSealProofTypeFromWindowPoStType(ver, mi.WindowPoStProofType) } func (m *Sealing) minerSector(spt abi.RegisteredSealProof, num abi.SectorNumber) storage.SectorRef { return storage.SectorRef{ ID: m.minerSectorID(num), ProofType: spt, } } func (m *Sealing) minerSectorID(num abi.SectorNumber) abi.SectorID { mid, err := address.IDFromAddress(m.maddr) if err != nil { panic(err) } return abi.SectorID{ Number: num, Miner: abi.ActorID(mid), } } func (m *Sealing) Address() address.Address { return m.maddr } func getDealPerSectorLimit(size abi.SectorSize) (int, error) { if size < 64<<30 { return 256, nil } return 512, nil }