2020-01-20 22:03:50 +00:00
package sealing
import (
"context"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
2020-04-06 18:07:26 +00:00
"github.com/filecoin-project/lotus/chain/types"
2020-04-04 01:50:05 +00:00
"github.com/filecoin-project/sector-storage/ffiwrapper"
2020-04-06 18:07:26 +00:00
"github.com/filecoin-project/sector-storage/zerocomm"
2020-04-04 01:50:05 +00:00
"github.com/filecoin-project/specs-actors/actors/abi"
2020-04-04 02:55:19 +00:00
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
2020-04-03 17:45:48 +00:00
"github.com/filecoin-project/specs-actors/actors/crypto"
2020-04-06 18:07:26 +00:00
log "github.com/mgutz/logxi/v1"
2020-01-20 22:03:50 +00:00
)
2020-01-23 15:38:01 +00:00
// TODO: For now we handle this by halting state execution, when we get jsonrpc reconnecting
// We should implement some wait-for-api logic
2020-01-23 16:15:45 +00:00
type ErrApi struct { error }
2020-01-23 12:17:45 +00:00
2020-01-23 16:15:45 +00:00
type ErrInvalidDeals struct { error }
2020-02-23 00:47:47 +00:00
type ErrInvalidPiece struct { error }
2020-01-23 16:15:45 +00:00
type ErrExpiredDeals struct { error }
2020-01-23 12:17:45 +00:00
2020-01-23 16:15:45 +00:00
type ErrBadCommD struct { error }
type ErrExpiredTicket struct { error }
2020-01-23 14:05:44 +00:00
2020-04-03 17:45:48 +00:00
type ErrBadSeed struct { error }
2020-04-04 01:50:05 +00:00
type ErrInvalidProof struct { error }
2020-04-03 17:45:48 +00:00
2020-01-24 20:15:02 +00:00
// checkPieces validates that:
// - Each piece han a corresponding on chain deal
// - Piece commitments match with on chain deals
// - Piece sizes match
// - Deals aren't expired
2020-04-06 18:07:26 +00:00
func checkPieces ( ctx context . Context , si SectorInfo , api SealingAPI ) error {
tok , height , err := api . ChainHead ( ctx )
2020-01-23 12:17:45 +00:00
if err != nil {
2020-01-23 15:57:14 +00:00
return & ErrApi { xerrors . Errorf ( "getting chain head: %w" , err ) }
2020-01-23 12:17:45 +00:00
}
2020-01-20 22:03:50 +00:00
for i , piece := range si . Pieces {
2020-02-23 00:47:47 +00:00
if piece . DealID == nil {
2020-03-26 02:50:56 +00:00
exp := zerocomm . ZeroPieceCommitment ( piece . Size )
2020-02-27 00:42:39 +00:00
if piece . CommP != exp {
2020-02-23 00:47:47 +00:00
return & ErrInvalidPiece { xerrors . Errorf ( "deal %d piece %d had non-zero CommP %+v" , piece . DealID , i , piece . CommP ) }
}
continue
}
2020-04-06 18:07:26 +00:00
proposal , _ , err := api . StateMarketStorageDeal ( ctx , * piece . DealID , tok )
2020-01-20 22:03:50 +00:00
if err != nil {
2020-01-23 15:57:14 +00:00
return & ErrApi { xerrors . Errorf ( "getting deal %d for piece %d: %w" , piece . DealID , i , err ) }
2020-01-20 22:03:50 +00:00
}
2020-04-06 18:07:26 +00:00
if proposal . PieceCID != piece . CommP {
return & ErrInvalidDeals { xerrors . Errorf ( "piece %d (or %d) of sector %d refers deal %d with wrong CommP: %x != %x" , i , len ( si . Pieces ) , si . SectorID , piece . DealID , piece . CommP , proposal . PieceCID ) }
2020-01-20 22:03:50 +00:00
}
2020-04-06 18:07:26 +00:00
if piece . Size != proposal . PieceSize . Unpadded ( ) {
return & ErrInvalidDeals { xerrors . Errorf ( "piece %d (or %d) of sector %d refers deal %d with different size: %d != %d" , i , len ( si . Pieces ) , si . SectorID , piece . DealID , piece . Size , proposal . PieceSize ) }
2020-01-23 12:17:45 +00:00
}
2020-04-06 18:07:26 +00:00
if height >= proposal . StartEpoch {
return & ErrExpiredDeals { xerrors . Errorf ( "piece %d (or %d) of sector %d refers expired deal %d - should start at %d, head %d" , i , len ( si . Pieces ) , si . SectorID , piece . DealID , proposal . StartEpoch , height ) }
2020-01-20 22:03:50 +00:00
}
}
return nil
}
2020-04-03 17:45:48 +00:00
// checkPrecommit checks that data commitment generated in the sealing process
2020-01-24 20:15:02 +00:00
// matches pieces, and that the seal ticket isn't expired
2020-04-06 18:07:26 +00:00
func checkPrecommit ( ctx context . Context , maddr address . Address , si SectorInfo , api SealingAPI ) ( err error ) {
tok , height , err := api . ChainHead ( ctx )
2020-01-20 22:03:50 +00:00
if err != nil {
2020-01-23 15:57:14 +00:00
return & ErrApi { xerrors . Errorf ( "getting chain head: %w" , err ) }
2020-01-23 14:05:44 +00:00
}
2020-04-06 18:07:26 +00:00
commD , err := api . StateComputeDataCommitment ( ctx , maddr , si . SectorType , si . deals ( ) , tok )
2020-01-20 22:03:50 +00:00
if err != nil {
2020-04-06 18:07:26 +00:00
return & ErrApi { xerrors . Errorf ( "calling StateComputeDataCommitment: %w" , err ) }
2020-02-23 00:47:47 +00:00
}
2020-04-06 18:07:26 +00:00
if ! commD . Equals ( * si . CommD ) {
return & ErrBadCommD { xerrors . Errorf ( "on chain CommD differs from sector: %s != %s" , commD , si . CommD ) }
2020-01-23 14:05:44 +00:00
}
2020-04-06 18:07:26 +00:00
if int64 ( height ) - int64 ( si . TicketEpoch + SealRandomnessLookback ) > SealRandomnessLookbackLimit {
return & ErrExpiredTicket { xerrors . Errorf ( "ticket expired: seal height: %d, head: %d" , si . TicketEpoch + SealRandomnessLookback , height ) }
2020-01-20 22:03:50 +00:00
}
return nil
2020-04-03 17:45:48 +00:00
}
2020-04-04 02:55:19 +00:00
func ( m * Sealing ) checkCommit ( ctx context . Context , si SectorInfo , proof [ ] byte ) ( err error ) {
2020-04-06 18:07:26 +00:00
tok , height , err := m . api . ChainHead ( ctx )
2020-04-03 17:45:48 +00:00
if err != nil {
return & ErrApi { xerrors . Errorf ( "getting chain head: %w" , err ) }
}
2020-01-20 22:03:50 +00:00
2020-04-03 17:45:48 +00:00
if si . Seed . Epoch == 0 {
return & ErrBadSeed { xerrors . Errorf ( "seed epoch was not set" ) }
}
2020-04-04 02:55:19 +00:00
pci , err := m . api . StateSectorPreCommitInfo ( ctx , m . maddr , si . SectorID , types . EmptyTSK )
if err != nil {
return xerrors . Errorf ( "getting precommit info: %w" , err )
}
if pci . PreCommitEpoch + miner . PreCommitChallengeDelay != si . Seed . Epoch {
return & ErrBadSeed { xerrors . Errorf ( "seed epoch doesn't match on chain info: %d != %d" , pci . PreCommitEpoch + miner . PreCommitChallengeDelay , si . Seed . Epoch ) }
}
2020-04-04 01:50:05 +00:00
seed , err := m . api . ChainGetRandomness ( ctx , head . Key ( ) , crypto . DomainSeparationTag_InteractiveSealChallengeSeed , si . Seed . Epoch , nil )
2020-04-03 17:45:48 +00:00
if err != nil {
return & ErrApi { xerrors . Errorf ( "failed to get randomness for computing seal proof: %w" , err ) }
}
2020-04-04 01:50:05 +00:00
if string ( seed ) != string ( si . Seed . Value ) {
2020-04-03 17:45:48 +00:00
return & ErrBadSeed { xerrors . Errorf ( "seed has changed" ) }
}
2020-04-04 01:50:05 +00:00
ss , err := m . api . StateMinerSectorSize ( ctx , m . maddr , head . Key ( ) )
if err != nil {
return & ErrApi { err }
}
_ , spt , err := ffiwrapper . ProofTypeFromSectorSize ( ss )
if err != nil {
return err
}
2020-04-04 02:55:19 +00:00
if * si . CommR != pci . Info . SealedCID {
log . Warn ( "on-chain sealed CID doesn't match!" )
}
ok , err := m . verif . VerifySeal ( abi . SealVerifyInfo {
SectorID : m . minerSector ( si . SectorID ) ,
OnChain : abi . OnChainSealVerifyInfo {
SealedCID : pci . Info . SealedCID ,
2020-04-04 01:50:05 +00:00
InteractiveEpoch : si . Seed . Epoch ,
RegisteredProof : spt ,
2020-04-04 02:55:19 +00:00
Proof : proof ,
2020-04-04 01:50:05 +00:00
SectorNumber : si . SectorID ,
SealRandEpoch : si . Ticket . Epoch ,
} ,
Randomness : si . Ticket . Value ,
InteractiveRandomness : si . Seed . Value ,
UnsealedCID : * si . CommD ,
} )
if err != nil {
return xerrors . Errorf ( "verify seal: %w" , err )
}
if ! ok {
return & ErrInvalidProof { xerrors . New ( "invalid proof (compute error?)" ) }
}
2020-04-03 17:45:48 +00:00
return nil
2020-01-20 22:03:50 +00:00
}