lotus/storage/sealing/checks.go

158 lines
5.2 KiB
Go
Raw Normal View History

2020-01-20 22:03:50 +00:00
package sealing
import (
"context"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
2020-04-04 01:50:05 +00:00
"github.com/filecoin-project/sector-storage/ffiwrapper"
"github.com/filecoin-project/sector-storage/zerocomm"
2020-04-04 01:50:05 +00:00
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
"github.com/filecoin-project/specs-actors/actors/crypto"
2020-01-20 22:03:50 +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
type ErrApi struct{ error }
2020-01-23 12:17:45 +00:00
type ErrInvalidDeals struct{ error }
2020-02-23 00:47:47 +00:00
type ErrInvalidPiece struct{ error }
type ErrExpiredDeals struct{ error }
2020-01-23 12:17:45 +00:00
type ErrBadCommD struct{ error }
type ErrExpiredTicket struct{ error }
2020-01-23 14:05:44 +00:00
type ErrBadSeed struct{ error }
2020-04-04 01:50:05 +00:00
type ErrInvalidProof struct{ error }
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
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 {
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
}
proposal, _, err := api.StateMarketStorageDeal(ctx, *piece.DealID, tok)
2020-01-20 22:03:50 +00:00
if err != nil {
return &ErrApi{xerrors.Errorf("getting deal %d for piece %d: %w", piece.DealID, i, err)}
2020-01-20 22:03:50 +00:00
}
if proposal.PieceCID != piece.CommP {
2020-04-06 22:31:33 +00:00
return &ErrInvalidDeals{xerrors.Errorf("piece %d (or %d) of sector %d refers deal %d with wrong CommP: %x != %x", i, len(si.Pieces), si.SectorNumber, piece.DealID, piece.CommP, proposal.PieceCID)}
2020-01-20 22:03:50 +00:00
}
if piece.Size != proposal.PieceSize.Unpadded() {
2020-04-06 22:31:33 +00:00
return &ErrInvalidDeals{xerrors.Errorf("piece %d (or %d) of sector %d refers deal %d with different size: %d != %d", i, len(si.Pieces), si.SectorNumber, piece.DealID, piece.Size, proposal.PieceSize)}
2020-01-23 12:17:45 +00:00
}
if height >= proposal.StartEpoch {
2020-04-06 22:31:33 +00:00
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.SectorNumber, piece.DealID, proposal.StartEpoch, height)}
2020-01-20 22:03:50 +00:00
}
}
return nil
}
// 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
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 {
return &ErrApi{xerrors.Errorf("getting chain head: %w", err)}
2020-01-23 14:05:44 +00:00
}
commD, err := api.StateComputeDataCommitment(ctx, maddr, si.SectorType, si.deals(), tok)
2020-01-20 22:03:50 +00:00
if err != nil {
return &ErrApi{xerrors.Errorf("calling StateComputeDataCommitment: %w", err)}
2020-02-23 00:47:47 +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
}
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
}
func (m *Sealing) checkCommit(ctx context.Context, si SectorInfo, proof []byte) (err error) {
2020-04-06 20:23:37 +00:00
tok, _, err := m.api.ChainHead(ctx)
if err != nil {
return &ErrApi{xerrors.Errorf("getting chain head: %w", err)}
}
2020-01-20 22:03:50 +00:00
2020-04-06 20:23:37 +00:00
if si.SeedEpoch == 0 {
return &ErrBadSeed{xerrors.Errorf("seed epoch was not set")}
}
2020-04-06 22:31:33 +00:00
pci, err := m.api.StateSectorPreCommitInfo(ctx, m.maddr, si.SectorNumber, tok)
if err != nil {
return xerrors.Errorf("getting precommit info: %w", err)
}
2020-04-06 20:23:37 +00:00
if pci.PreCommitEpoch+miner.PreCommitChallengeDelay != si.SeedEpoch {
return &ErrBadSeed{xerrors.Errorf("seed epoch doesn't match on chain info: %d != %d", pci.PreCommitEpoch+miner.PreCommitChallengeDelay, si.SeedEpoch)}
}
2020-04-06 20:23:37 +00:00
seed, err := m.api.ChainGetRandomness(ctx, tok, crypto.DomainSeparationTag_InteractiveSealChallengeSeed, si.SeedEpoch, nil)
if err != nil {
return &ErrApi{xerrors.Errorf("failed to get randomness for computing seal proof: %w", err)}
}
2020-04-06 20:23:37 +00:00
if string(seed) != string(si.SeedValue) {
return &ErrBadSeed{xerrors.Errorf("seed has changed")}
}
2020-04-06 20:23:37 +00:00
ss, err := m.api.StateMinerSectorSize(ctx, m.maddr, tok)
2020-04-04 01:50:05 +00:00
if err != nil {
return &ErrApi{err}
}
_, spt, err := ffiwrapper.ProofTypeFromSectorSize(ss)
if err != nil {
return err
}
if *si.CommR != pci.Info.SealedCID {
log.Warn("on-chain sealed CID doesn't match!")
}
ok, err := m.verif.VerifySeal(abi.SealVerifyInfo{
2020-04-06 22:31:33 +00:00
SectorID: m.minerSector(si.SectorNumber),
OnChain: abi.OnChainSealVerifyInfo{
SealedCID: pci.Info.SealedCID,
2020-04-06 20:23:37 +00:00
InteractiveEpoch: si.SeedEpoch,
2020-04-04 01:50:05 +00:00
RegisteredProof: spt,
Proof: proof,
2020-04-06 22:31:33 +00:00
SectorNumber: si.SectorNumber,
2020-04-06 20:23:37 +00:00
SealRandEpoch: si.TicketEpoch,
2020-04-04 01:50:05 +00:00
},
2020-04-06 20:23:37 +00:00
Randomness: si.TicketValue,
InteractiveRandomness: si.SeedValue,
2020-04-04 01:50:05 +00:00
UnsealedCID: *si.CommD,
})
if err != nil {
return xerrors.Errorf("verify seal: %w", err)
}
if !ok {
return &ErrInvalidProof{xerrors.New("invalid proof (compute error?)")}
}
return nil
2020-01-20 22:03:50 +00:00
}