lotus/storage/sealing/checks.go

121 lines
4.1 KiB
Go
Raw Normal View History

2020-01-20 22:03:50 +00:00
package sealing
import (
2020-02-23 00:47:47 +00:00
"bytes"
2020-01-20 22:03:50 +00:00
"context"
2020-03-06 18:50:41 +00:00
"github.com/filecoin-project/go-sectorbuilder"
2020-02-25 20:35:15 +00:00
"github.com/ipfs/go-cid"
cbg "github.com/whyrusleeping/cbor-gen"
2020-01-20 22:03:50 +00:00
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
2020-02-25 20:35:15 +00:00
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/specs-actors/actors/builtin/market"
2020-01-23 14:05:44 +00:00
"github.com/filecoin-project/lotus/build"
2020-01-20 22:03:50 +00:00
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/types"
)
// 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
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 {
2020-01-23 12:17:45 +00:00
head, err := api.ChainHead(ctx)
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-06 18:50:41 +00:00
exp := sectorbuilder.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
}
deal, err := api.StateMarketStorageDeal(ctx, *piece.DealID, types.EmptyTSK)
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
}
2020-02-27 00:42:39 +00:00
if deal.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, deal.Proposal.PieceCID)}
2020-01-20 22:03:50 +00:00
}
if piece.Size != deal.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, deal.Proposal.PieceSize)}
2020-01-23 12:17:45 +00:00
}
if head.Height() >= deal.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, deal.Proposal.StartEpoch, head.Height())}
2020-01-20 22:03:50 +00:00
}
}
return nil
}
2020-01-24 20:15:02 +00:00
// checkSeal checks that data commitment generated in the sealing process
// matches pieces, and that the seal ticket isn't expired
func checkSeal(ctx context.Context, maddr address.Address, si SectorInfo, api sealingApi) (err error) {
2020-01-23 14:05:44 +00:00
head, 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
}
2020-02-25 20:35:15 +00:00
ccparams, err := actors.SerializeParams(&market.ComputeDataCommitmentParams{
2020-01-20 22:03:50 +00:00
DealIDs: si.deals(),
2020-02-27 00:42:39 +00:00
SectorType: si.SectorType,
2020-01-20 22:03:50 +00:00
})
if err != nil {
return xerrors.Errorf("computing params for ComputeDataCommitment: %w", err)
}
ccmt := &types.Message{
2020-02-25 20:54:58 +00:00
To: builtin.StorageMarketActorAddr,
From: maddr,
2020-01-20 22:03:50 +00:00
Value: types.NewInt(0),
GasPrice: types.NewInt(0),
2020-03-18 20:45:37 +00:00
GasLimit: 9999999999,
2020-02-11 02:33:27 +00:00
Method: builtin.MethodsMarket.ComputeDataCommitment,
2020-01-20 22:03:50 +00:00
Params: ccparams,
}
r, err := api.StateCall(ctx, ccmt, types.EmptyTSK)
2020-01-20 22:03:50 +00:00
if err != nil {
return &ErrApi{xerrors.Errorf("calling ComputeDataCommitment: %w", err)}
2020-01-20 22:03:50 +00:00
}
if r.MsgRct.ExitCode != 0 {
return &ErrBadCommD{xerrors.Errorf("receipt for ComputeDataCommitment had exit code %d", r.MsgRct.ExitCode)}
2020-01-20 22:03:50 +00:00
}
2020-02-23 00:47:47 +00:00
var c cbg.CborCid
2020-03-08 00:46:12 +00:00
if err := c.UnmarshalCBOR(bytes.NewReader(r.MsgRct.Return)); err != nil {
2020-02-23 00:47:47 +00:00
return err
}
2020-02-27 00:42:39 +00:00
if cid.Cid(c) != *si.CommD {
2020-03-02 01:09:38 +00:00
return &ErrBadCommD{xerrors.Errorf("on chain CommD differs from sector: %s != %s", cid.Cid(c), si.CommD)}
2020-01-23 14:05:44 +00:00
}
2020-02-27 21:45:31 +00:00
if int64(head.Height())-int64(si.Ticket.Epoch+build.SealRandomnessLookback) > build.SealRandomnessLookbackLimit {
return &ErrExpiredTicket{xerrors.Errorf("ticket expired: seal height: %d, head: %d", si.Ticket.Epoch+build.SealRandomnessLookback, head.Height())}
2020-01-20 22:03:50 +00:00
}
return nil
}