2021-01-18 13:26:03 +00:00
package sealing
import (
"context"
"sort"
2021-01-18 20:59:34 +00:00
"time"
2021-01-18 13:26:03 +00:00
2022-04-12 00:45:13 +00:00
"github.com/ipfs/go-cid"
2022-04-11 23:22:19 +00:00
"golang.org/x/xerrors"
2021-01-18 13:26:03 +00:00
2021-08-26 14:22:43 +00:00
"github.com/filecoin-project/go-commp-utils/zerocomm"
2022-06-14 15:00:51 +00:00
"github.com/filecoin-project/go-padreader"
2021-01-18 13:26:03 +00:00
"github.com/filecoin-project/go-state-types/abi"
2022-03-16 16:33:05 +00:00
"github.com/filecoin-project/go-state-types/big"
2022-11-16 01:57:23 +00:00
"github.com/filecoin-project/go-state-types/network"
2022-06-14 15:00:51 +00:00
"github.com/filecoin-project/go-statemachine"
2021-01-18 13:26:03 +00:00
2021-05-19 11:05:07 +00:00
"github.com/filecoin-project/lotus/api"
2022-01-09 10:23:02 +00:00
"github.com/filecoin-project/lotus/build"
2022-03-16 16:33:05 +00:00
"github.com/filecoin-project/lotus/chain/actors/policy"
"github.com/filecoin-project/lotus/chain/types"
2022-06-14 17:41:59 +00:00
"github.com/filecoin-project/lotus/storage/pipeline/lib/nullreader"
"github.com/filecoin-project/lotus/storage/pipeline/sealiface"
2022-06-14 18:03:38 +00:00
"github.com/filecoin-project/lotus/storage/sealer"
"github.com/filecoin-project/lotus/storage/sealer/ffiwrapper"
2022-06-17 11:31:05 +00:00
"github.com/filecoin-project/lotus/storage/sealer/storiface"
2022-08-09 11:30:34 +00:00
"github.com/filecoin-project/lotus/storage/sectorblocks"
2021-01-18 13:26:03 +00:00
)
func ( m * Sealing ) handleWaitDeals ( ctx statemachine . Context , sector SectorInfo ) error {
2021-01-21 18:59:18 +00:00
var used abi . UnpaddedPieceSize
2022-11-16 01:57:23 +00:00
var lastDealEnd abi . ChainEpoch
2021-01-21 18:59:18 +00:00
for _ , piece := range sector . Pieces {
used += piece . Piece . Size . Unpadded ( )
2022-11-16 01:57:23 +00:00
if piece . DealInfo != nil && piece . DealInfo . DealProposal . EndEpoch > lastDealEnd {
lastDealEnd = piece . DealInfo . DealProposal . EndEpoch
}
2021-01-21 18:59:18 +00:00
}
2021-01-18 13:26:03 +00:00
m . inputLk . Lock ( )
2021-01-18 20:59:34 +00:00
2022-03-16 20:29:57 +00:00
if m . nextDealSector != nil && * m . nextDealSector == sector . SectorNumber {
m . nextDealSector = nil
2021-05-30 16:30:38 +00:00
}
2021-04-14 18:26:07 +00:00
sid := m . minerSectorID ( sector . SectorNumber )
if len ( m . assignedPieces [ sid ] ) > 0 {
m . inputLk . Unlock ( )
// got assigned more pieces in the AddPiece state
return ctx . Send ( SectorAddPiece { } )
}
2021-01-21 18:59:18 +00:00
started , err := m . maybeStartSealing ( ctx , sector , used )
if err != nil || started {
2021-02-09 17:44:41 +00:00
delete ( m . openSectors , m . minerSectorID ( sector . SectorNumber ) )
2021-01-21 18:59:18 +00:00
m . inputLk . Unlock ( )
return err
}
2021-04-14 18:26:07 +00:00
if _ , has := m . openSectors [ sid ] ; ! has {
m . openSectors [ sid ] = & openSector {
used : used ,
maybeAccept : func ( cid cid . Cid ) error {
// todo check deal start deadline (configurable)
m . assignedPieces [ sid ] = append ( m . assignedPieces [ sid ] , cid )
2021-01-21 18:59:18 +00:00
2021-04-14 18:26:07 +00:00
return ctx . Send ( SectorAddPiece { } )
} ,
2021-12-08 17:11:19 +00:00
number : sector . SectorNumber ,
ccUpdate : sector . CCUpdate ,
2021-04-14 18:26:07 +00:00
}
2021-10-18 07:55:28 +00:00
} else {
// make sure we're only accounting for pieces which were correctly added
// (note that m.assignedPieces[sid] will always be empty here)
m . openSectors [ sid ] . used = used
2021-01-21 18:59:18 +00:00
}
2022-11-16 01:57:23 +00:00
m . openSectors [ sid ] . lastDealEnd = lastDealEnd
2021-01-21 18:59:18 +00:00
go func ( ) {
defer m . inputLk . Unlock ( )
if err := m . updateInput ( ctx . Context ( ) , sector . SectorType ) ; err != nil {
log . Errorf ( "%+v" , err )
}
} ( )
return nil
}
func ( m * Sealing ) maybeStartSealing ( ctx statemachine . Context , sector SectorInfo , used abi . UnpaddedPieceSize ) ( bool , error ) {
2021-01-18 20:59:34 +00:00
now := time . Now ( )
st := m . sectorTimers [ m . minerSectorID ( sector . SectorNumber ) ]
if st != nil {
if ! st . Stop ( ) { // timer expired, SectorStartPacking was/is being sent
// we send another SectorStartPacking in case one was sent in the handleAddPiece state
2021-01-20 21:44:18 +00:00
log . Infow ( "starting to seal deal sector" , "sector" , sector . SectorNumber , "trigger" , "wait-timeout" )
2021-01-21 18:59:18 +00:00
return true , ctx . Send ( SectorStartPacking { } )
2021-01-18 20:59:34 +00:00
}
}
2021-01-20 17:18:12 +00:00
ssize , err := sector . SectorType . SectorSize ( )
if err != nil {
2021-01-21 18:59:18 +00:00
return false , xerrors . Errorf ( "getting sector size" )
2021-01-20 17:18:12 +00:00
}
maxDeals , err := getDealPerSectorLimit ( ssize )
2021-01-20 14:20:44 +00:00
if err != nil {
2021-01-21 18:59:18 +00:00
return false , xerrors . Errorf ( "getting per-sector deal limit: %w" , err )
2021-01-20 14:20:44 +00:00
}
if len ( sector . dealIDs ( ) ) >= maxDeals {
2021-01-20 17:18:12 +00:00
// can't accept more deals
2021-01-20 21:44:18 +00:00
log . Infow ( "starting to seal deal sector" , "sector" , sector . SectorNumber , "trigger" , "maxdeals" )
2021-01-21 18:59:18 +00:00
return true , ctx . Send ( SectorStartPacking { } )
2021-01-20 17:18:12 +00:00
}
if used . Padded ( ) == abi . PaddedPieceSize ( ssize ) {
// sector full
2021-01-20 21:44:18 +00:00
log . Infow ( "starting to seal deal sector" , "sector" , sector . SectorNumber , "trigger" , "filled" )
2021-01-21 18:59:18 +00:00
return true , ctx . Send ( SectorStartPacking { } )
2021-01-20 14:20:44 +00:00
}
2021-01-20 13:49:31 +00:00
if sector . CreationTime != 0 {
2021-01-18 20:59:34 +00:00
cfg , err := m . getConfig ( )
if err != nil {
2021-01-21 18:59:18 +00:00
return false , xerrors . Errorf ( "getting storage config: %w" , err )
2021-01-18 20:59:34 +00:00
}
2021-01-20 13:49:31 +00:00
sealTime := time . Unix ( sector . CreationTime , 0 ) . Add ( cfg . WaitDealsDelay )
2021-01-20 17:18:12 +00:00
2022-01-09 10:23:02 +00:00
// check deal age, start sealing when the deal closest to starting is within slack time
2022-06-16 11:15:49 +00:00
ts , err := m . Api . ChainHead ( ctx . Context ( ) )
2022-01-09 10:23:02 +00:00
blockTime := time . Second * time . Duration ( build . BlockDelaySecs )
if err != nil {
return false , xerrors . Errorf ( "API error getting head: %w" , err )
}
for _ , piece := range sector . Pieces {
2022-02-08 17:39:18 +00:00
if piece . DealInfo == nil {
2022-01-09 10:23:02 +00:00
continue
}
dealSafeSealEpoch := piece . DealInfo . DealProposal . StartEpoch - cfg . StartEpochSealingBuffer
2022-06-16 11:15:49 +00:00
dealSafeSealTime := time . Now ( ) . Add ( time . Duration ( dealSafeSealEpoch - ts . Height ( ) ) * blockTime )
2022-02-08 17:39:18 +00:00
if dealSafeSealTime . Before ( sealTime ) {
sealTime = dealSafeSealTime
2022-01-09 10:23:02 +00:00
}
}
2021-01-18 20:59:34 +00:00
if now . After ( sealTime ) {
2021-01-20 21:44:18 +00:00
log . Infow ( "starting to seal deal sector" , "sector" , sector . SectorNumber , "trigger" , "wait-timeout" )
2021-01-21 18:59:18 +00:00
return true , ctx . Send ( SectorStartPacking { } )
2021-01-18 20:59:34 +00:00
}
2021-01-19 18:04:05 +00:00
m . sectorTimers [ m . minerSectorID ( sector . SectorNumber ) ] = time . AfterFunc ( sealTime . Sub ( now ) , func ( ) {
2021-01-20 21:44:18 +00:00
log . Infow ( "starting to seal deal sector" , "sector" , sector . SectorNumber , "trigger" , "wait-timer" )
2021-01-19 18:04:05 +00:00
if err := ctx . Send ( SectorStartPacking { } ) ; err != nil {
log . Errorw ( "sending SectorStartPacking event failed" , "sector" , sector . SectorNumber , "error" , err )
}
} )
2021-01-18 20:59:34 +00:00
}
2021-01-21 18:59:18 +00:00
return false , nil
2021-01-18 13:26:03 +00:00
}
func ( m * Sealing ) handleAddPiece ( ctx statemachine . Context , sector SectorInfo ) error {
ssize , err := sector . SectorType . SectorSize ( )
if err != nil {
return err
}
2021-01-20 15:00:00 +00:00
res := SectorPieceAdded { }
2021-01-18 13:26:03 +00:00
m . inputLk . Lock ( )
2021-01-20 15:00:00 +00:00
pending , ok := m . assignedPieces [ m . minerSectorID ( sector . SectorNumber ) ]
if ok {
delete ( m . assignedPieces , m . minerSectorID ( sector . SectorNumber ) )
}
m . inputLk . Unlock ( )
if ! ok {
2021-01-20 17:42:22 +00:00
// nothing to do here (might happen after a restart in AddPiece)
2021-01-20 15:00:00 +00:00
return ctx . Send ( res )
}
2021-01-18 13:26:03 +00:00
2021-01-18 20:59:34 +00:00
var offset abi . UnpaddedPieceSize
pieceSizes := make ( [ ] abi . UnpaddedPieceSize , len ( sector . Pieces ) )
for i , p := range sector . Pieces {
pieceSizes [ i ] = p . Piece . Size . Unpadded ( )
offset += p . Piece . Size . Unpadded ( )
}
2021-01-20 17:18:12 +00:00
maxDeals , err := getDealPerSectorLimit ( ssize )
2021-01-20 14:20:44 +00:00
if err != nil {
return xerrors . Errorf ( "getting per-sector deal limit: %w" , err )
}
2021-01-20 15:00:00 +00:00
for i , piece := range pending {
2021-01-18 13:26:03 +00:00
m . inputLk . Lock ( )
deal , ok := m . pendingPieces [ piece ]
m . inputLk . Unlock ( )
if ! ok {
return xerrors . Errorf ( "piece %s assigned to sector %d not found" , piece , sector . SectorNumber )
}
2021-01-20 14:20:44 +00:00
if len ( sector . dealIDs ( ) ) + ( i + 1 ) > maxDeals {
2021-01-20 17:18:12 +00:00
// todo: this is rather unlikely to happen, but in case it does, return the deal to waiting queue instead of failing it
2021-01-20 14:20:44 +00:00
deal . accepted ( sector . SectorNumber , offset , xerrors . Errorf ( "too many deals assigned to sector %d, dropping deal" , sector . SectorNumber ) )
continue
}
2021-01-18 20:59:34 +00:00
pads , padLength := ffiwrapper . GetRequiredPadding ( offset . Padded ( ) , deal . size . Padded ( ) )
2021-01-18 13:26:03 +00:00
2021-01-18 20:59:34 +00:00
if offset . Padded ( ) + padLength + deal . size . Padded ( ) > abi . PaddedPieceSize ( ssize ) {
2021-01-20 17:18:12 +00:00
// todo: this is rather unlikely to happen, but in case it does, return the deal to waiting queue instead of failing it
deal . accepted ( sector . SectorNumber , offset , xerrors . Errorf ( "piece %s assigned to sector %d with not enough space" , piece , sector . SectorNumber ) )
continue
2021-01-18 13:26:03 +00:00
}
2021-01-18 20:59:34 +00:00
offset += padLength . Unpadded ( )
2021-01-18 13:26:03 +00:00
for _ , p := range pads {
2021-08-26 14:22:43 +00:00
expectCid := zerocomm . ZeroPieceCommitment ( p . Unpadded ( ) )
2022-06-14 18:03:38 +00:00
ppi , err := m . sealer . AddPiece ( sealer . WithPriority ( ctx . Context ( ) , DealSectorPriority ) ,
2021-01-18 13:26:03 +00:00
m . minerSector ( sector . SectorType , sector . SectorNumber ) ,
pieceSizes ,
p . Unpadded ( ) ,
2022-04-12 15:45:34 +00:00
nullreader . NewNullReader ( p . Unpadded ( ) ) )
2021-01-18 13:26:03 +00:00
if err != nil {
2021-01-20 15:00:00 +00:00
err = xerrors . Errorf ( "writing padding piece: %w" , err )
deal . accepted ( sector . SectorNumber , offset , err )
2021-01-20 17:42:22 +00:00
return ctx . Send ( SectorAddPieceFailed { err } )
2021-01-18 13:26:03 +00:00
}
2021-08-26 14:22:43 +00:00
if ! ppi . PieceCID . Equals ( expectCid ) {
err = xerrors . Errorf ( "got unexpected padding piece CID: expected:%s, got:%s" , expectCid , ppi . PieceCID )
deal . accepted ( sector . SectorNumber , offset , err )
return ctx . Send ( SectorAddPieceFailed { err } )
}
2021-01-18 13:26:03 +00:00
pieceSizes = append ( pieceSizes , p . Unpadded ( ) )
2022-08-26 00:37:36 +00:00
res . NewPieces = append ( res . NewPieces , api . SectorPiece {
2021-01-18 13:26:03 +00:00
Piece : ppi ,
} )
}
2022-06-14 18:03:38 +00:00
ppi , err := m . sealer . AddPiece ( sealer . WithPriority ( ctx . Context ( ) , DealSectorPriority ) ,
2021-01-18 13:26:03 +00:00
m . minerSector ( sector . SectorType , sector . SectorNumber ) ,
pieceSizes ,
deal . size ,
deal . data )
if err != nil {
2021-01-20 15:00:00 +00:00
err = xerrors . Errorf ( "writing piece: %w" , err )
deal . accepted ( sector . SectorNumber , offset , err )
2021-01-20 17:42:22 +00:00
return ctx . Send ( SectorAddPieceFailed { err } )
2021-01-18 13:26:03 +00:00
}
2021-08-26 14:22:43 +00:00
if ! ppi . PieceCID . Equals ( deal . deal . DealProposal . PieceCID ) {
err = xerrors . Errorf ( "got unexpected piece CID: expected:%s, got:%s" , deal . deal . DealProposal . PieceCID , ppi . PieceCID )
deal . accepted ( sector . SectorNumber , offset , err )
return ctx . Send ( SectorAddPieceFailed { err } )
}
2021-01-18 13:26:03 +00:00
2021-01-20 21:44:18 +00:00
log . Infow ( "deal added to a sector" , "deal" , deal . deal . DealID , "sector" , sector . SectorNumber , "piece" , ppi . PieceCID )
2021-01-18 20:59:34 +00:00
deal . accepted ( sector . SectorNumber , offset , nil )
offset += deal . size
2021-01-18 13:26:03 +00:00
pieceSizes = append ( pieceSizes , deal . size )
2021-01-18 20:59:34 +00:00
2022-08-26 00:37:36 +00:00
res . NewPieces = append ( res . NewPieces , api . SectorPiece {
2021-01-18 20:59:34 +00:00
Piece : ppi ,
2021-01-18 13:26:03 +00:00
DealInfo : & deal . deal ,
} )
}
return ctx . Send ( res )
}
2021-01-20 17:42:22 +00:00
func ( m * Sealing ) handleAddPieceFailed ( ctx statemachine . Context , sector SectorInfo ) error {
2021-10-04 18:00:07 +00:00
return ctx . Send ( SectorRetryWaitDeals { } )
2021-01-20 17:42:22 +00:00
}
2022-06-17 11:31:05 +00:00
func ( m * Sealing ) SectorAddPieceToAny ( ctx context . Context , size abi . UnpaddedPieceSize , data storiface . Data , deal api . PieceDealInfo ) ( api . SectorOffset , error ) {
2021-01-18 13:26:03 +00:00
log . Infof ( "Adding piece for deal %d (publish msg: %s)" , deal . DealID , deal . PublishCid )
if ( padreader . PaddedSize ( uint64 ( size ) ) ) != size {
2021-05-19 11:05:07 +00:00
return api . SectorOffset { } , xerrors . Errorf ( "cannot allocate unpadded piece" )
2021-01-18 13:26:03 +00:00
}
sp , err := m . currentSealProof ( ctx )
if err != nil {
2021-05-19 11:05:07 +00:00
return api . SectorOffset { } , xerrors . Errorf ( "getting current seal proof type: %w" , err )
2021-01-18 13:26:03 +00:00
}
ssize , err := sp . SectorSize ( )
if err != nil {
2021-05-19 11:05:07 +00:00
return api . SectorOffset { } , err
2021-01-18 13:26:03 +00:00
}
if size > abi . PaddedPieceSize ( ssize ) . Unpadded ( ) {
2021-05-19 11:05:07 +00:00
return api . SectorOffset { } , xerrors . Errorf ( "piece cannot fit into a sector" )
2021-01-18 13:26:03 +00:00
}
2021-02-09 17:44:41 +00:00
if _ , err := deal . DealProposal . Cid ( ) ; err != nil {
2021-05-19 11:05:07 +00:00
return api . SectorOffset { } , xerrors . Errorf ( "getting proposal CID: %w" , err )
2021-01-18 13:26:03 +00:00
}
2021-09-30 12:35:23 +00:00
cfg , err := m . getConfig ( )
if err != nil {
return api . SectorOffset { } , xerrors . Errorf ( "getting config: %w" , err )
}
2022-06-16 11:15:49 +00:00
ts , err := m . Api . ChainHead ( ctx )
2021-09-30 10:43:02 +00:00
if err != nil {
return api . SectorOffset { } , xerrors . Errorf ( "couldnt get chain head: %w" , err )
}
2022-06-16 11:15:49 +00:00
if ts . Height ( ) + cfg . StartEpochSealingBuffer > deal . DealProposal . StartEpoch {
2021-09-30 10:43:02 +00:00
return api . SectorOffset { } , xerrors . Errorf (
"cannot add piece for deal with piece CID %s: current epoch %d has passed deal proposal start epoch %d" ,
2022-06-16 11:15:49 +00:00
deal . DealProposal . PieceCID , ts . Height ( ) , deal . DealProposal . StartEpoch )
2021-09-30 10:43:02 +00:00
}
2022-11-16 01:57:23 +00:00
claimTerms , err := m . getClaimTerms ( ctx , deal , ts . Key ( ) )
if err != nil {
return api . SectorOffset { } , err
}
2021-01-18 13:26:03 +00:00
m . inputLk . Lock ( )
2022-02-21 07:51:25 +00:00
if pp , exist := m . pendingPieces [ proposalCID ( deal ) ] ; exist {
2021-01-18 13:26:03 +00:00
m . inputLk . Unlock ( )
2022-02-22 10:22:51 +00:00
// we already have a pre-existing add piece call for this deal, let's wait for it to finish and see if it's successful
2022-03-10 19:24:26 +00:00
res , err := waitAddPieceResp ( ctx , pp )
if err != nil {
return api . SectorOffset { } , err
}
if res . err == nil {
2022-02-22 10:22:51 +00:00
// all good, return the response
2022-02-21 07:51:25 +00:00
return api . SectorOffset { Sector : res . sn , Offset : res . offset . Padded ( ) } , res . err
}
2022-03-10 19:24:26 +00:00
// if there was an error waiting for a pre-existing add piece call, let's retry
m . inputLk . Lock ( )
2021-01-18 13:26:03 +00:00
}
2022-03-10 19:24:26 +00:00
// addPendingPiece takes over m.inputLk
2022-11-16 01:57:23 +00:00
pp := m . addPendingPiece ( ctx , size , data , deal , claimTerms , sp )
2022-03-10 19:24:26 +00:00
2022-02-22 10:22:51 +00:00
res , err := waitAddPieceResp ( ctx , pp )
if err != nil {
return api . SectorOffset { } , err
}
return api . SectorOffset { Sector : res . sn , Offset : res . offset . Padded ( ) } , res . err
}
2022-11-16 01:57:23 +00:00
func ( m * Sealing ) getClaimTerms ( ctx context . Context , deal api . PieceDealInfo , tsk types . TipSetKey ) ( pieceClaimBounds , error ) {
nv , err := m . Api . StateNetworkVersion ( ctx , tsk )
if err != nil {
return pieceClaimBounds { } , err
}
if nv >= network . Version17 {
all , err := m . Api . StateGetAllocationForPendingDeal ( ctx , deal . DealID , tsk )
if err != nil {
return pieceClaimBounds { } , err
}
if all != nil {
return pieceClaimBounds {
claimTermEnd : deal . DealProposal . StartEpoch + all . TermMax ,
} , nil
}
}
// no allocation for this deal, so just use a really high number for "term end"
return pieceClaimBounds {
claimTermEnd : deal . DealProposal . EndEpoch + policy . GetSectorMaxLifetime ( abi . RegisteredSealProof_StackedDrg32GiBV1_1 , network . Version17 ) ,
} , nil
}
2022-03-10 19:24:26 +00:00
// called with m.inputLk; transfers the lock to another goroutine!
2022-11-16 01:57:23 +00:00
func ( m * Sealing ) addPendingPiece ( ctx context . Context , size abi . UnpaddedPieceSize , data storiface . Data , deal api . PieceDealInfo , ct pieceClaimBounds , sp abi . RegisteredSealProof ) * pendingPiece {
2022-02-22 09:48:39 +00:00
doneCh := make ( chan struct { } )
2022-02-21 09:51:43 +00:00
pp := & pendingPiece {
2022-11-16 01:57:23 +00:00
size : size ,
deal : deal ,
claimTerms : ct ,
data : data ,
2022-02-21 09:51:43 +00:00
doneCh : doneCh ,
2021-01-18 13:26:03 +00:00
assigned : false ,
2022-02-21 09:51:43 +00:00
}
pp . accepted = func ( sn abi . SectorNumber , offset abi . UnpaddedPieceSize , err error ) {
2022-02-21 14:27:51 +00:00
pp . resp = & pieceAcceptResp { sn , offset , err }
2022-02-21 09:51:43 +00:00
close ( pp . doneCh )
2021-01-18 13:26:03 +00:00
}
2022-02-21 09:51:43 +00:00
m . pendingPieces [ proposalCID ( deal ) ] = pp
2021-01-18 13:26:03 +00:00
go func ( ) {
2022-03-10 19:24:26 +00:00
defer m . inputLk . Unlock ( )
2021-01-18 13:26:03 +00:00
if err := m . updateInput ( ctx , sp ) ; err != nil {
log . Errorf ( "%+v" , err )
}
} ( )
2022-02-22 10:22:51 +00:00
return pp
}
func waitAddPieceResp ( ctx context . Context , pp * pendingPiece ) ( * pieceAcceptResp , error ) {
2022-02-21 07:56:49 +00:00
select {
2022-02-22 10:22:51 +00:00
case <- pp . doneCh :
2022-02-21 14:27:51 +00:00
res := pp . resp
2022-02-22 10:22:51 +00:00
return res , nil
2022-02-21 07:56:49 +00:00
case <- ctx . Done ( ) :
2022-02-22 10:22:51 +00:00
return nil , ctx . Err ( )
2022-02-21 07:56:49 +00:00
}
2021-01-18 13:26:03 +00:00
}
2022-08-09 11:30:34 +00:00
func ( m * Sealing ) SectorMatchPendingPiecesToOpenSectors ( ctx context . Context ) error {
2021-12-08 17:11:19 +00:00
sp , err := m . currentSealProof ( ctx )
if err != nil {
return xerrors . Errorf ( "failed to get current seal proof: %w" , err )
}
log . Debug ( "pieces to sector matching waiting for lock" )
m . inputLk . Lock ( )
defer m . inputLk . Unlock ( )
return m . updateInput ( ctx , sp )
}
2022-03-16 16:33:05 +00:00
type expFn func ( sn abi . SectorNumber ) ( abi . ChainEpoch , abi . TokenAmount , error )
2021-01-18 13:26:03 +00:00
// called with m.inputLk
func ( m * Sealing ) updateInput ( ctx context . Context , sp abi . RegisteredSealProof ) error {
2022-03-16 16:33:05 +00:00
memo := make ( map [ abi . SectorNumber ] struct {
e abi . ChainEpoch
p abi . TokenAmount
} )
2022-09-14 10:13:22 +00:00
getExpirationCached := func ( sn abi . SectorNumber ) ( abi . ChainEpoch , abi . TokenAmount , error ) {
2022-03-16 16:33:05 +00:00
if e , ok := memo [ sn ] ; ok {
return e . e , e . p , nil
}
2022-06-16 09:12:33 +00:00
onChainInfo , err := m . Api . StateSectorGetInfo ( ctx , m . maddr , sn , types . TipSetKey { } )
2022-03-16 16:33:05 +00:00
if err != nil {
return 0 , big . Zero ( ) , err
}
2022-12-09 08:56:11 +00:00
if onChainInfo == nil {
return 0 , big . Zero ( ) , xerrors . Errorf ( "sector info for sector %d not found" , sn )
}
2022-03-16 16:33:05 +00:00
memo [ sn ] = struct {
e abi . ChainEpoch
p abi . TokenAmount
} { e : onChainInfo . Expiration , p : onChainInfo . InitialPledge }
return onChainInfo . Expiration , onChainInfo . InitialPledge , nil
}
2021-01-18 13:26:03 +00:00
ssize , err := sp . SectorSize ( )
if err != nil {
return err
}
type match struct {
sector abi . SectorID
deal cid . Cid
2022-11-16 01:57:23 +00:00
dealEnd abi . ChainEpoch
claimTermEnd abi . ChainEpoch
2021-01-18 13:26:03 +00:00
size abi . UnpaddedPieceSize
padding abi . UnpaddedPieceSize
}
var matches [ ] match
toAssign := map [ cid . Cid ] struct { } { } // used to maybe create new sectors
// todo: this is distinctly O(n^2), may need to be optimized for tiny deals and large scale miners
// (unlikely to be a problem now)
2021-02-09 17:44:41 +00:00
for proposalCid , piece := range m . pendingPieces {
2021-01-18 20:59:34 +00:00
if piece . assigned {
continue // already assigned to a sector, skip
}
2021-01-18 13:26:03 +00:00
2021-02-09 17:44:41 +00:00
toAssign [ proposalCid ] = struct { } { }
2021-01-18 13:26:03 +00:00
2021-01-18 20:59:34 +00:00
for id , sector := range m . openSectors {
avail := abi . PaddedPieceSize ( ssize ) . Unpadded ( ) - sector . used
2021-12-08 17:11:19 +00:00
// check that sector lifetime is long enough to fit deal using latest expiration from on chain
2022-11-16 01:57:23 +00:00
ok , err := sector . checkDealAssignable ( piece , getExpirationCached )
2021-12-08 17:11:19 +00:00
if err != nil {
log . Errorf ( "failed to check expiration for cc Update sector %d" , sector . number )
continue
}
if ! ok {
continue
}
2021-01-18 13:26:03 +00:00
if piece . size <= avail { // (note: if we have enough space for the piece, we also have enough space for inter-piece padding)
matches = append ( matches , match {
sector : id ,
2021-02-09 17:44:41 +00:00
deal : proposalCid ,
2021-01-18 13:26:03 +00:00
2022-11-16 01:57:23 +00:00
dealEnd : piece . deal . DealProposal . EndEpoch ,
claimTermEnd : piece . claimTerms . claimTermEnd ,
2021-01-18 13:26:03 +00:00
size : piece . size ,
padding : avail % piece . size ,
} )
}
}
}
sort . Slice ( matches , func ( i , j int ) bool {
2022-11-16 01:57:23 +00:00
// todo maybe sort by expiration
2021-01-18 13:26:03 +00:00
if matches [ i ] . padding != matches [ j ] . padding { // less padding is better
return matches [ i ] . padding < matches [ j ] . padding
}
if matches [ i ] . size != matches [ j ] . size { // larger pieces are better
return matches [ i ] . size < matches [ j ] . size
}
return matches [ i ] . sector . Number < matches [ j ] . sector . Number // prefer older sectors
} )
2022-03-21 09:32:03 +00:00
log . Debugw ( "updateInput matching" , "matches" , len ( matches ) , "toAssign" , len ( toAssign ) , "openSectors" , len ( m . openSectors ) , "pieces" , len ( m . pendingPieces ) )
2021-01-18 13:26:03 +00:00
var assigned int
for _ , mt := range matches {
if m . pendingPieces [ mt . deal ] . assigned {
assigned ++
continue
}
if _ , found := m . openSectors [ mt . sector ] ; ! found {
continue
}
2022-11-16 01:57:23 +00:00
// late checks
2021-04-14 18:26:07 +00:00
avail := abi . PaddedPieceSize ( ssize ) . Unpadded ( ) - m . openSectors [ mt . sector ] . used
if mt . size > avail {
continue
}
2022-11-16 01:57:23 +00:00
if m . openSectors [ mt . sector ] . lastDealEnd > mt . claimTermEnd {
continue
}
// assign the piece!
2021-01-18 13:26:03 +00:00
err := m . openSectors [ mt . sector ] . maybeAccept ( mt . deal )
if err != nil {
m . pendingPieces [ mt . deal ] . accepted ( mt . sector . Number , 0 , err ) // non-error case in handleAddPiece
}
2021-04-14 18:26:07 +00:00
m . openSectors [ mt . sector ] . used += mt . padding + mt . size
2022-11-16 01:57:23 +00:00
if mt . dealEnd > m . openSectors [ mt . sector ] . lastDealEnd {
m . openSectors [ mt . sector ] . lastDealEnd = mt . dealEnd
}
2021-04-14 18:26:07 +00:00
2021-01-18 13:26:03 +00:00
m . pendingPieces [ mt . deal ] . assigned = true
delete ( toAssign , mt . deal )
if err != nil {
log . Errorf ( "sector %d rejected deal %s: %+v" , mt . sector , mt . deal , err )
continue
}
}
2022-03-21 09:32:03 +00:00
log . Debugw ( "updateInput matching done" , "matches" , len ( matches ) , "toAssign" , len ( toAssign ) , "assigned" , assigned , "openSectors" , len ( m . openSectors ) , "pieces" , len ( m . pendingPieces ) )
2021-01-18 13:26:03 +00:00
if len ( toAssign ) > 0 {
2021-12-08 17:11:19 +00:00
log . Errorf ( "we are trying to create a new sector with open sectors %v" , m . openSectors )
2022-09-14 10:13:22 +00:00
if err := m . tryGetDealSector ( ctx , sp , getExpirationCached ) ; err != nil {
2021-01-18 20:59:34 +00:00
log . Errorw ( "Failed to create a new sector for deals" , "error" , err )
}
2021-01-18 13:26:03 +00:00
}
return nil
}
2022-11-16 01:57:23 +00:00
// pendingPieceIndex is an index in the Sealing.pendingPieces map
type pendingPieceIndex cid . Cid
2022-03-16 16:33:05 +00:00
2022-11-16 01:57:23 +00:00
type pieceBound struct {
epoch abi . ChainEpoch
2022-03-16 16:33:05 +00:00
2022-11-16 01:57:23 +00:00
// boundStart marks deal /end/ epoch; only deals with boundStart lower or equal to expiration of a given sector can be
// put into that sector
boundStart [ ] pendingPieceIndex
// boundEnd marks deal claim TermMax; only deals with boundEnd higher or equal to expiration of a given sector can be
// put into that sector
boundEnd [ ] pendingPieceIndex
2022-03-16 16:33:05 +00:00
2022-11-16 01:57:23 +00:00
dealBytesInBound abi . UnpaddedPieceSize
}
2022-09-14 10:45:22 +00:00
2022-11-16 01:57:23 +00:00
func ( m * Sealing ) pendingPieceEpochBounds ( ) [ ] pieceBound {
boundsByEpoch := map [ abi . ChainEpoch ] * pieceBound { }
2022-03-16 16:33:05 +00:00
2022-11-16 01:57:23 +00:00
for ppi , piece := range m . pendingPieces {
// start bound on deal end
if boundsByEpoch [ piece . deal . DealProposal . EndEpoch ] == nil {
boundsByEpoch [ piece . deal . DealProposal . EndEpoch ] = & pieceBound {
epoch : piece . deal . DealProposal . EndEpoch ,
}
2022-03-16 16:33:05 +00:00
}
2022-11-16 01:57:23 +00:00
boundsByEpoch [ piece . deal . DealProposal . EndEpoch ] . boundStart = append ( boundsByEpoch [ piece . deal . DealProposal . EndEpoch ] . boundStart , pendingPieceIndex ( ppi ) )
2022-03-16 16:33:05 +00:00
2022-11-16 01:57:23 +00:00
// end bound on term max
if boundsByEpoch [ piece . claimTerms . claimTermEnd ] == nil {
boundsByEpoch [ piece . claimTerms . claimTermEnd ] = & pieceBound {
epoch : piece . claimTerms . claimTermEnd ,
}
}
boundsByEpoch [ piece . claimTerms . claimTermEnd ] . boundEnd = append ( boundsByEpoch [ piece . claimTerms . claimTermEnd ] . boundEnd , pendingPieceIndex ( ppi ) )
2022-09-16 10:41:48 +00:00
}
2022-11-16 01:57:23 +00:00
out := make ( [ ] pieceBound , 0 , len ( boundsByEpoch ) )
for _ , bound := range boundsByEpoch {
out = append ( out , * bound )
2022-03-16 16:33:05 +00:00
}
2022-11-16 01:57:23 +00:00
sort . Slice ( out , func ( i , j int ) bool {
return out [ i ] . epoch < out [ j ] . epoch
} )
2022-03-16 16:33:05 +00:00
2022-11-16 01:57:23 +00:00
var curBoundBytes abi . UnpaddedPieceSize
for i , bound := range out {
for _ , ppi := range bound . boundStart {
curBoundBytes += m . pendingPieces [ cid . Cid ( ppi ) ] . size
}
for _ , ppi := range bound . boundEnd {
curBoundBytes -= m . pendingPieces [ cid . Cid ( ppi ) ] . size
}
out [ i ] . dealBytesInBound = curBoundBytes
2022-09-14 10:13:22 +00:00
}
2022-11-16 01:57:23 +00:00
return out
2022-03-16 16:33:05 +00:00
}
2022-09-14 10:45:22 +00:00
func ( m * Sealing ) maybeUpgradeSector ( ctx context . Context , sp abi . RegisteredSealProof , cfg sealiface . Config , ef expFn ) ( bool , error ) {
2022-03-16 16:33:05 +00:00
if len ( m . available ) == 0 {
return false , nil
}
2022-11-16 01:57:23 +00:00
ts , err := m . Api . ChainHead ( ctx )
2022-03-16 19:04:48 +00:00
if err != nil {
2022-11-16 01:57:23 +00:00
return false , err
2022-03-16 19:04:48 +00:00
}
2022-11-16 01:57:23 +00:00
ssize , err := sp . SectorSize ( )
2022-03-16 19:17:20 +00:00
if err != nil {
2022-11-16 01:57:23 +00:00
return false , err
2022-03-16 19:17:20 +00:00
}
2022-03-16 16:33:05 +00:00
2022-11-16 01:57:23 +00:00
pieceBounds := m . pendingPieceEpochBounds ( )
findBound := func ( sectorExp abi . ChainEpoch ) * pieceBound {
if len ( pieceBounds ) == 0 {
return nil
}
f := sort . Search ( len ( pieceBounds ) , func ( i int ) bool {
return sectorExp <= pieceBounds [ i ] . epoch
} )
if f == 0 {
// all piece bounds are after sector expiration
return nil
}
return & pieceBounds [ f - 1 ]
}
targetExpirationEpoch := ts . Height ( ) + abi . ChainEpoch ( cfg . MinTargetUpgradeSectorExpiration )
minExpirationEpoch := ts . Height ( ) + abi . ChainEpoch ( cfg . MinUpgradeSectorExpiration )
2022-03-16 16:33:05 +00:00
var candidate abi . SectorID
var bestExpiration abi . ChainEpoch
2022-11-16 01:57:23 +00:00
var bestDealBytes abi . PaddedPieceSize
2022-03-16 16:33:05 +00:00
bestPledge := types . TotalFilecoinInt
for s := range m . available {
2022-09-16 10:41:48 +00:00
expirationEpoch , pledge , err := ef ( s . Number )
2022-03-16 16:33:05 +00:00
if err != nil {
log . Errorw ( "checking sector expiration" , "error" , err )
continue
}
2022-03-16 19:28:15 +00:00
slowChecks := func ( sid abi . SectorNumber ) bool {
2022-06-16 09:12:33 +00:00
active , err := m . sectorActive ( ctx , types . TipSetKey { } , sid )
2022-03-16 19:09:18 +00:00
if err != nil {
log . Errorw ( "checking sector active" , "error" , err )
return false
}
if ! active {
2022-11-16 01:57:23 +00:00
log . Debugw ( "skipping available sector" , "sector" , sid , "reason" , "not active" )
2022-03-16 19:09:18 +00:00
return false
}
return true
2022-03-16 18:57:37 +00:00
}
2022-11-16 01:57:23 +00:00
if expirationEpoch < minExpirationEpoch {
log . Debugw ( "skipping available sector" , "sector" , s . Number , "reason" , "expiration below MinUpgradeSectorExpiration" )
}
pb := findBound ( expirationEpoch )
if pb == nil {
log . Debugw ( "skipping available sector" , "sector" , s . Number , "reason" , "expiration below deal bounds" )
continue
}
// if the sector has less than one sector worth of candidate deals, and
// the best candidate has more candidate deals, this sector isn't better
if pb . dealBytesInBound . Padded ( ) < abi . PaddedPieceSize ( ssize ) {
if bestDealBytes > pb . dealBytesInBound . Padded ( ) {
continue
}
}
2022-03-16 16:33:05 +00:00
// if best is below target, we want larger expirations
// if best is above target, we want lower pledge, but only if still above target
2022-11-16 01:57:23 +00:00
// todo: after nv17 "target expiration" doesn't really make that much sense
// (tho to be fair it doesn't make too much sense now either)
// we probably want the lowest expiration that's still above the configured
// minimum, and can fit most candidate deals
2022-09-16 10:41:48 +00:00
if bestExpiration < targetExpirationEpoch {
if expirationEpoch > bestExpiration && slowChecks ( s . Number ) {
bestExpiration = expirationEpoch
2022-03-16 16:33:05 +00:00
bestPledge = pledge
2022-11-16 01:57:23 +00:00
bestDealBytes = pb . dealBytesInBound . Padded ( )
2022-03-16 16:33:05 +00:00
candidate = s
}
continue
}
2022-09-16 10:41:48 +00:00
if expirationEpoch >= targetExpirationEpoch && pledge . LessThan ( bestPledge ) && slowChecks ( s . Number ) {
bestExpiration = expirationEpoch
2022-03-16 16:33:05 +00:00
bestPledge = pledge
2022-11-16 01:57:23 +00:00
bestDealBytes = pb . dealBytesInBound . Padded ( )
2022-03-16 16:33:05 +00:00
candidate = s
}
}
2022-09-16 10:41:48 +00:00
if bestExpiration < minExpirationEpoch {
log . Infow ( "Not upgrading any sectors" , "available" , len ( m . available ) , "pieces" , len ( m . pendingPieces ) , "bestExp" , bestExpiration , "target" , targetExpirationEpoch , "min" , minExpirationEpoch , "candidate" , candidate )
2022-03-16 19:04:48 +00:00
// didn't find a good sector / no sectors were available
2022-03-16 16:33:05 +00:00
return false , nil
}
log . Infow ( "Upgrading sector" , "number" , candidate . Number , "type" , "deal" , "proofType" , sp , "expiration" , bestExpiration , "pledge" , types . FIL ( bestPledge ) )
delete ( m . available , candidate )
2022-03-16 20:29:57 +00:00
m . nextDealSector = & candidate . Number
2022-03-16 16:33:05 +00:00
return true , m . sectors . Send ( uint64 ( candidate . Number ) , SectorStartCCUpdate { } )
}
2022-03-17 20:12:42 +00:00
// call with m.inputLk
func ( m * Sealing ) createSector ( ctx context . Context , cfg sealiface . Config , sp abi . RegisteredSealProof ) ( abi . SectorNumber , error ) {
2022-08-17 15:53:44 +00:00
sid , err := m . NextSectorNumber ( ctx )
2022-03-17 20:12:42 +00:00
if err != nil {
return 0 , xerrors . Errorf ( "getting sector number: %w" , err )
}
err = m . sealer . NewSector ( ctx , m . minerSector ( sp , sid ) )
if err != nil {
return 0 , xerrors . Errorf ( "initializing sector: %w" , err )
}
// update stats early, fsm planner would do that async
m . stats . updateSector ( ctx , cfg , m . minerSectorID ( sid ) , UndefinedSectorState )
return sid , err
}
2022-03-16 16:33:05 +00:00
func ( m * Sealing ) tryGetDealSector ( ctx context . Context , sp abi . RegisteredSealProof , ef expFn ) error {
2021-06-15 19:04:11 +00:00
m . startupWait . Wait ( )
2021-06-15 19:04:11 +00:00
2022-03-16 20:29:57 +00:00
if m . nextDealSector != nil {
2021-05-30 16:30:38 +00:00
return nil // new sector is being created right now
}
2021-01-18 13:26:03 +00:00
cfg , err := m . getConfig ( )
if err != nil {
return xerrors . Errorf ( "getting storage config: %w" , err )
}
2022-03-17 20:12:42 +00:00
// if we're above WaitDeals limit, we don't want to add more staging sectors
2022-03-16 16:33:05 +00:00
if cfg . MaxWaitDealsSectors > 0 && m . stats . curStaging ( ) >= cfg . MaxWaitDealsSectors {
2021-01-18 20:59:34 +00:00
return nil
}
2022-03-17 20:12:42 +00:00
maxUpgrading := cfg . MaxSealingSectorsForDeals
if cfg . MaxUpgradingSectors > 0 {
maxUpgrading = cfg . MaxUpgradingSectors
2022-03-16 16:33:05 +00:00
}
2022-03-17 20:12:42 +00:00
canCreate := cfg . MakeNewSectorForDeals && ! ( cfg . MaxSealingSectorsForDeals > 0 && m . stats . curSealing ( ) >= cfg . MaxSealingSectorsForDeals )
canUpgrade := ! ( maxUpgrading > 0 && m . stats . curSealing ( ) >= maxUpgrading )
2021-01-18 13:26:03 +00:00
2022-03-17 20:12:42 +00:00
// we want to try to upgrade when:
// - we can upgrade and prefer upgrades
// - we don't prefer upgrades, but can't create a new sector
shouldUpgrade := canUpgrade && ( ! cfg . PreferNewSectorsForDeals || ! canCreate )
2021-03-12 17:42:17 +00:00
2022-03-17 20:12:42 +00:00
log . Infow ( "new deal sector decision" ,
"sealing" , m . stats . curSealing ( ) ,
"maxSeal" , cfg . MaxSealingSectorsForDeals ,
"maxUpgrade" , maxUpgrading ,
"preferNew" , cfg . PreferNewSectorsForDeals ,
"canCreate" , canCreate ,
"canUpgrade" , canUpgrade ,
"shouldUpgrade" , shouldUpgrade )
2021-01-18 13:26:03 +00:00
2022-03-17 20:12:42 +00:00
if shouldUpgrade {
2022-09-14 10:45:22 +00:00
got , err := m . maybeUpgradeSector ( ctx , sp , cfg , ef )
2022-03-17 20:12:42 +00:00
if err != nil {
return err
}
if got {
return nil
}
2021-01-18 13:26:03 +00:00
}
2022-03-17 20:12:42 +00:00
if canCreate {
sid , err := m . createSector ( ctx , cfg , sp )
if err != nil {
return err
}
m . nextDealSector = & sid
2021-03-12 16:25:24 +00:00
2022-03-17 20:12:42 +00:00
log . Infow ( "Creating sector" , "number" , sid , "type" , "deal" , "proofType" , sp )
if err := m . sectors . Send ( uint64 ( sid ) , SectorStart {
ID : sid ,
SectorType : sp ,
} ) ; err != nil {
return err
}
}
return nil
2021-01-18 13:26:03 +00:00
}
2021-01-18 20:59:34 +00:00
2022-08-09 11:30:34 +00:00
func ( m * Sealing ) StartPackingSector ( sid abi . SectorNumber ) error {
2021-06-15 19:04:11 +00:00
m . startupWait . Wait ( )
2021-05-30 13:13:38 +00:00
log . Infow ( "starting to seal deal sector" , "sector" , sid , "trigger" , "user" )
2021-01-18 20:59:34 +00:00
return m . sectors . Send ( uint64 ( sid ) , SectorStartPacking { } )
}
2021-02-09 17:44:41 +00:00
2022-08-09 11:30:34 +00:00
func ( m * Sealing ) SectorAbortUpgrade ( sid abi . SectorNumber ) error {
2022-01-21 17:39:18 +00:00
m . startupWait . Wait ( )
2022-03-16 19:04:48 +00:00
m . inputLk . Lock ( )
// always do this early
delete ( m . available , m . minerSectorID ( sid ) )
m . inputLk . Unlock ( )
2022-01-21 17:39:18 +00:00
log . Infow ( "aborting upgrade of sector" , "sector" , sid , "trigger" , "user" )
2022-01-21 19:07:11 +00:00
return m . sectors . Send ( uint64 ( sid ) , SectorAbortUpgrade { xerrors . New ( "triggered by user" ) } )
2022-01-21 17:39:18 +00:00
}
2022-08-09 11:30:34 +00:00
func ( m * Sealing ) SectorsStatus ( ctx context . Context , sid abi . SectorNumber , showOnChainInfo bool ) ( api . SectorInfo , error ) {
if showOnChainInfo {
return api . SectorInfo { } , xerrors . Errorf ( "on-chain info not supported" )
}
info , err := m . GetSectorInfo ( sid )
if err != nil {
return api . SectorInfo { } , err
}
deals := make ( [ ] abi . DealID , len ( info . Pieces ) )
pieces := make ( [ ] api . SectorPiece , len ( info . Pieces ) )
for i , piece := range info . Pieces {
pieces [ i ] . Piece = piece . Piece
if piece . DealInfo == nil {
continue
}
pdi := * piece . DealInfo // copy
pieces [ i ] . DealInfo = & pdi
deals [ i ] = piece . DealInfo . DealID
}
log := make ( [ ] api . SectorLog , len ( info . Log ) )
for i , l := range info . Log {
log [ i ] = api . SectorLog {
Kind : l . Kind ,
Timestamp : l . Timestamp ,
Trace : l . Trace ,
Message : l . Message ,
}
}
sInfo := api . SectorInfo {
SectorID : sid ,
State : api . SectorState ( info . State ) ,
CommD : info . CommD ,
CommR : info . CommR ,
Proof : info . Proof ,
Deals : deals ,
Pieces : pieces ,
Ticket : api . SealTicket {
Value : info . TicketValue ,
Epoch : info . TicketEpoch ,
} ,
Seed : api . SealSeed {
Value : info . SeedValue ,
Epoch : info . SeedEpoch ,
} ,
PreCommitMsg : info . PreCommitMessage ,
CommitMsg : info . CommitMessage ,
Retries : info . InvalidProofs ,
ToUpgrade : false ,
ReplicaUpdateMessage : info . ReplicaUpdateMessage ,
LastErr : info . LastErr ,
Log : log ,
// on chain info
SealProof : info . SectorType ,
Activation : 0 ,
Expiration : 0 ,
DealWeight : big . Zero ( ) ,
VerifiedDealWeight : big . Zero ( ) ,
InitialPledge : big . Zero ( ) ,
OnTime : 0 ,
Early : 0 ,
}
return sInfo , nil
}
2021-05-19 11:05:07 +00:00
func proposalCID ( deal api . PieceDealInfo ) cid . Cid {
2021-02-09 17:44:41 +00:00
pc , err := deal . DealProposal . Cid ( )
if err != nil {
log . Errorf ( "DealProposal.Cid error: %+v" , err )
return cid . Undef
}
return pc
}
2022-08-09 11:30:34 +00:00
var _ sectorblocks . SectorBuilder = & Sealing { }