From 303d4a31fec950d409f6034125aad9157bfa8b50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 4 Apr 2020 03:50:05 +0200 Subject: [PATCH] fsm: Handle invalid Commits --- storage/sealing/cbor_gen.go | 33 ++++++++++++++++++++++++- storage/sealing/checks.go | 42 +++++++++++++++++++++++++++++--- storage/sealing/fsm.go | 1 + storage/sealing/fsm_events.go | 11 ++++++++- storage/sealing/states.go | 4 +++ storage/sealing/states_failed.go | 12 ++++++++- storage/sealing/types.go | 1 + 7 files changed, 97 insertions(+), 7 deletions(-) diff --git a/storage/sealing/cbor_gen.go b/storage/sealing/cbor_gen.go index 2215914a9..ab12aa155 100644 --- a/storage/sealing/cbor_gen.go +++ b/storage/sealing/cbor_gen.go @@ -178,7 +178,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { _, err := w.Write(cbg.CborNull) return err } - if _, err := w.Write([]byte{176}); err != nil { + if _, err := w.Write([]byte{177}); err != nil { return err } @@ -450,6 +450,22 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error { } } + // t.InvalidProofs (uint64) (uint64) + if len("InvalidProofs") > cbg.MaxLength { + return xerrors.Errorf("Value in field \"InvalidProofs\" was too long") + } + + if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len("InvalidProofs")))); err != nil { + return err + } + if _, err := w.Write([]byte("InvalidProofs")); err != nil { + return err + } + + if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.InvalidProofs))); err != nil { + return err + } + // t.FaultReportMsg (cid.Cid) (struct) if len("FaultReportMsg") > cbg.MaxLength { return xerrors.Errorf("Value in field \"FaultReportMsg\" was too long") @@ -802,6 +818,21 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) error { t.CommitMessage = &c } + } + // t.InvalidProofs (uint64) (uint64) + case "InvalidProofs": + + { + + maj, extra, err = cbg.CborReadHeader(br) + if err != nil { + return err + } + if maj != cbg.MajUnsignedInt { + return fmt.Errorf("wrong type for uint64 field") + } + t.InvalidProofs = uint64(extra) + } // t.FaultReportMsg (cid.Cid) (struct) case "FaultReportMsg": diff --git a/storage/sealing/checks.go b/storage/sealing/checks.go index cc68459ba..372fc5847 100644 --- a/storage/sealing/checks.go +++ b/storage/sealing/checks.go @@ -9,6 +9,8 @@ import ( "golang.org/x/xerrors" "github.com/filecoin-project/go-address" + "github.com/filecoin-project/sector-storage/ffiwrapper" + "github.com/filecoin-project/specs-actors/actors/abi" "github.com/filecoin-project/specs-actors/actors/builtin" "github.com/filecoin-project/specs-actors/actors/builtin/market" "github.com/filecoin-project/specs-actors/actors/crypto" @@ -31,6 +33,7 @@ type ErrBadCommD struct{ error } type ErrExpiredTicket struct{ error } type ErrBadSeed struct{ error } +type ErrInvalidProof struct{ error } // checkPieces validates that: // - Each piece han a corresponding on chain deal @@ -121,8 +124,8 @@ func checkPrecommit(ctx context.Context, maddr address.Address, si SectorInfo, a return nil } -func checkCommit(ctx context.Context, si SectorInfo, api sealingApi) (err error) { - head, err := api.ChainHead(ctx) +func (m *Sealing) checkCommit(ctx context.Context, si SectorInfo) (err error) { + head, err := m.api.ChainHead(ctx) if err != nil { return &ErrApi{xerrors.Errorf("getting chain head: %w", err)} } @@ -131,14 +134,45 @@ func checkCommit(ctx context.Context, si SectorInfo, api sealingApi) (err error) return &ErrBadSeed{xerrors.Errorf("seed epoch was not set")} } - rand, err := api.ChainGetRandomness(ctx, head.Key(), crypto.DomainSeparationTag_InteractiveSealChallengeSeed, si.Seed.Epoch, nil) + seed, err := m.api.ChainGetRandomness(ctx, head.Key(), crypto.DomainSeparationTag_InteractiveSealChallengeSeed, si.Seed.Epoch, nil) if err != nil { return &ErrApi{xerrors.Errorf("failed to get randomness for computing seal proof: %w", err)} } - if string(rand) != string(si.Seed.Value) { + if string(seed) != string(si.Seed.Value) { return &ErrBadSeed{xerrors.Errorf("seed has changed")} } + 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 + } + + ok, err := ffiwrapper.ProofVerifier.VerifySeal(abi.SealVerifyInfo{ + SectorID: m.minerSector(si.SectorID), + OnChain: abi.OnChainSealVerifyInfo{ + SealedCID: *si.CommR, + InteractiveEpoch: si.Seed.Epoch, + RegisteredProof: spt, + Proof: si.Proof, + 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?)")} + } + + return nil } diff --git a/storage/sealing/fsm.go b/storage/sealing/fsm.go index f3f79d763..2d1691678 100644 --- a/storage/sealing/fsm.go +++ b/storage/sealing/fsm.go @@ -84,6 +84,7 @@ var fsmPlanners = map[api.SectorState]func(events []statemachine.Event, state *S on(SectorSealPreCommitFailed{}, api.SealFailed), on(SectorRetryWaitSeed{}, api.WaitSeed), on(SectorRetryComputeProof{}, api.Committing), + on(SectorRetryInvalidProof{}, api.Committing), ), api.Faulty: planOne( diff --git a/storage/sealing/fsm_events.go b/storage/sealing/fsm_events.go index 3aac561e3..b6d03d26d 100644 --- a/storage/sealing/fsm_events.go +++ b/storage/sealing/fsm_events.go @@ -96,7 +96,9 @@ func (evt SectorPreCommit2) apply(state *SectorInfo) { type SectorSealPreCommitFailed struct{ error } func (evt SectorSealPreCommitFailed) FormatError(xerrors.Printer) (next error) { return evt.error } -func (evt SectorSealPreCommitFailed) apply(*SectorInfo) {} +func (evt SectorSealPreCommitFailed) apply(si *SectorInfo) { + si.InvalidProofs = 0 // reset counter +} type SectorChainPreCommitFailed struct{ error } @@ -170,6 +172,13 @@ type SectorRetryComputeProof struct{} func (evt SectorRetryComputeProof) apply(state *SectorInfo) {} +type SectorRetryInvalidProof struct{} + +func (evt SectorRetryInvalidProof) apply(state *SectorInfo) { + state.InvalidProofs++ +} + + // Faults type SectorFaulty struct{} diff --git a/storage/sealing/states.go b/storage/sealing/states.go index 07287b9e3..94f227fc3 100644 --- a/storage/sealing/states.go +++ b/storage/sealing/states.go @@ -205,6 +205,10 @@ func (m *Sealing) handleCommitting(ctx statemachine.Context, sector SectorInfo) return ctx.Send(SectorComputeProofFailed{xerrors.Errorf("computing seal proof failed: %w", err)}) } + if err := m.checkCommit(ctx.Context(), sector); err != nil { + return ctx.Send(SectorCommitFailed{xerrors.Errorf("commit check error: %w", err)}) + } + // TODO: Consider splitting states and persist proof for faster recovery params := &miner.ProveCommitSectorParams{ diff --git a/storage/sealing/states_failed.go b/storage/sealing/states_failed.go index 538cebca4..5c26b5a40 100644 --- a/storage/sealing/states_failed.go +++ b/storage/sealing/states_failed.go @@ -147,7 +147,7 @@ func (m *Sealing) handleCommitFailed(ctx statemachine.Context, sector SectorInfo } } - if err := checkCommit(ctx.Context(), sector, m.api); err != nil { + if err := m.checkCommit(ctx.Context(), sector); err != nil { switch err.(type) { case *ErrApi: log.Errorf("handleCommitFailed: api error, not proceeding: %+v", err) @@ -155,6 +155,16 @@ func (m *Sealing) handleCommitFailed(ctx statemachine.Context, sector SectorInfo case *ErrBadSeed: log.Errorf("seed changed, will retry: %+v", err) return ctx.Send(SectorRetryWaitSeed{}) + case *ErrInvalidProof: + if err := failedCooldown(ctx, sector); err != nil { + return err + } + + if sector.InvalidProofs > 0 { + return ctx.Send(SectorSealPreCommitFailed{xerrors.Errorf("consecutive invalid proofs")}) + } + + return ctx.Send(SectorRetryInvalidProof{}) default: return xerrors.Errorf("checkCommit sanity check error: %w", err) } diff --git a/storage/sealing/types.go b/storage/sealing/types.go index 197bf6e15..294802ef0 100644 --- a/storage/sealing/types.go +++ b/storage/sealing/types.go @@ -52,6 +52,7 @@ type SectorInfo struct { // Committing CommitMessage *cid.Cid + InvalidProofs uint64 // failed proof computations (doesn't validate with proof inputs) // Faults FaultReportMsg *cid.Cid