From b7fab5f937001f125801f4579cfa25bb25a072bb Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 9 Mar 2021 22:03:05 -0800 Subject: [PATCH 1/5] fix: wait a bit before starting to compute window post proofs Due to a (now consensus critical) bug in randomness selection, "beacon" randomness depends on whether the epoch in which the beacon occurred was a null block. Unfortunately, this means: 1. If a challenge lands on a non-null round but that non-null round gets re-orged to a null round, the randomness will change to the beacon in the first preceding non-null block. 2. If a challenge lands on a null round but that null round gets re-orged to a non-null round, the randomness will change to the beacon in the new non-null round. The correct solution is to change the way the beacon is selected: 1. Wait until a non-null round. 2. The block in this round will contain all beacons from the null rounds, select the beacon from the target epoch. Unfortunately, this requires a mandatory network upgrade. Instead, this patch just waits a bit before beginning to compute window post after passing the challenge round to wait-out any reorgs. --- storage/wdpost_changehandler.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/storage/wdpost_changehandler.go b/storage/wdpost_changehandler.go index 285995757..188d7e93a 100644 --- a/storage/wdpost_changehandler.go +++ b/storage/wdpost_changehandler.go @@ -13,7 +13,10 @@ import ( "github.com/filecoin-project/lotus/chain/types" ) -const SubmitConfidence = 4 +const ( + SubmitConfidence = 4 + ChallengeConfidence = 10 +) type CompleteGeneratePoSTCb func(posts []miner.SubmitWindowedPoStParams, err error) type CompleteSubmitPoSTCb func(err error) @@ -230,7 +233,7 @@ func (p *proveHandler) processHeadChange(ctx context.Context, newTS *types.TipSe } // Check if the chain is above the Challenge height for the post window - if newTS.Height() < di.Challenge { + if newTS.Height() < di.Challenge+ChallengeConfidence { return } From 57fe04ba6e97b8343813c709917b3ae3def095f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 10 Mar 2021 09:19:38 +0100 Subject: [PATCH 2/5] wdpost: double-check randomness --- storage/wdpost_run.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/storage/wdpost_run.go b/storage/wdpost_run.go index 2690069d4..79cb13827 100644 --- a/storage/wdpost_run.go +++ b/storage/wdpost_run.go @@ -600,9 +600,18 @@ func (s *WindowPoStScheduler) runPost(ctx context.Context, di dline.Info, ts *ty return nil, xerrors.Errorf("received no proofs back from generate window post") } + checkRand, err := s.api.ChainGetRandomnessFromBeacon(ctx, types.EmptyTSK, crypto.DomainSeparationTag_WindowedPoStChallengeSeed, di.Challenge, buf.Bytes()) + if err != nil { + return nil, xerrors.Errorf("failed to get chain randomness from beacon for window post (ts=%d; deadline=%d): %w", ts.Height(), di, err) + } + + if !bytes.Equal(checkRand, rand) { + log.Warnw("windowpost randomness changed", "old", rand, "new", checkRand, "ts-height", ts.Height(), "challenge-height", di.Challenge, "tsk", ts.Key()) + } + // If we generated an incorrect proof, try again. if correct, err := s.verifier.VerifyWindowPoSt(ctx, proof.WindowPoStVerifyInfo{ - Randomness: abi.PoStRandomness(rand), + Randomness: abi.PoStRandomness(checkRand), Proofs: postOut, ChallengedSectors: sinfos, Prover: abi.ActorID(mid), From ea4a54e083f6a1fea78c8eb6d296af5880b2e93e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 10 Mar 2021 10:01:28 +0100 Subject: [PATCH 3/5] wdpost: Fix tests --- storage/wdpost_changehandler_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/storage/wdpost_changehandler_test.go b/storage/wdpost_changehandler_test.go index 6479c0d7e..6291709ad 100644 --- a/storage/wdpost_changehandler_test.go +++ b/storage/wdpost_changehandler_test.go @@ -395,7 +395,7 @@ func TestChangeHandlerStartProvingNextDeadline(t *testing.T) { // Trigger a head change currentEpoch := abi.ChainEpoch(1) - go triggerHeadAdvance(t, s, currentEpoch) + go triggerHeadAdvance(t, s, currentEpoch + ChallengeConfidence) // Should start proving <-s.ch.proveHdlr.processedHeadChanges @@ -405,7 +405,7 @@ func TestChangeHandlerStartProvingNextDeadline(t *testing.T) { // Trigger a head change that advances the chain beyond the submit // confidence currentEpoch = 1 + SubmitConfidence - go triggerHeadAdvance(t, s, currentEpoch) + go triggerHeadAdvance(t, s, currentEpoch + ChallengeConfidence) // Should be no change to state yet <-s.ch.proveHdlr.processedHeadChanges @@ -424,7 +424,7 @@ func TestChangeHandlerStartProvingNextDeadline(t *testing.T) { // the next deadline go func() { di = nextDeadline(di) - currentEpoch = di.Challenge + currentEpoch = di.Challenge + ChallengeConfidence triggerHeadAdvance(t, s, currentEpoch) }() @@ -446,7 +446,7 @@ func TestChangeHandlerProvingRounds(t *testing.T) { for currentEpoch := abi.ChainEpoch(1); currentEpoch < miner.WPoStChallengeWindow*5; currentEpoch++ { // Trigger a head change di := mock.getDeadline(currentEpoch) - go triggerHeadAdvance(t, s, currentEpoch) + go triggerHeadAdvance(t, s, currentEpoch + ChallengeConfidence) // Wait for prover to process head change <-s.ch.proveHdlr.processedHeadChanges @@ -913,7 +913,7 @@ func TestChangeHandlerSubmitRevertTwoEpochs(t *testing.T) { // Move to the challenge epoch for the next deadline diE2 := nextDeadline(diE1) - currentEpoch = diE2.Challenge + currentEpoch = diE2.Challenge + ChallengeConfidence go triggerHeadAdvance(t, s, currentEpoch) // Should move to submitting state for epoch 1 @@ -1014,7 +1014,7 @@ func TestChangeHandlerSubmitRevertAdvanceLess(t *testing.T) { // Move to the challenge epoch for the next deadline diE2 := nextDeadline(diE1) - currentEpoch = diE2.Challenge + currentEpoch = diE2.Challenge + ChallengeConfidence go triggerHeadAdvance(t, s, currentEpoch) // Should move to submitting state for epoch 1 From ca3d22683a8888ac07e55deb17e1164fae601b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 10 Mar 2021 12:27:37 +0100 Subject: [PATCH 4/5] gofmt --- storage/wdpost_changehandler_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/storage/wdpost_changehandler_test.go b/storage/wdpost_changehandler_test.go index 6291709ad..bae4f40fd 100644 --- a/storage/wdpost_changehandler_test.go +++ b/storage/wdpost_changehandler_test.go @@ -395,7 +395,7 @@ func TestChangeHandlerStartProvingNextDeadline(t *testing.T) { // Trigger a head change currentEpoch := abi.ChainEpoch(1) - go triggerHeadAdvance(t, s, currentEpoch + ChallengeConfidence) + go triggerHeadAdvance(t, s, currentEpoch+ChallengeConfidence) // Should start proving <-s.ch.proveHdlr.processedHeadChanges @@ -405,7 +405,7 @@ func TestChangeHandlerStartProvingNextDeadline(t *testing.T) { // Trigger a head change that advances the chain beyond the submit // confidence currentEpoch = 1 + SubmitConfidence - go triggerHeadAdvance(t, s, currentEpoch + ChallengeConfidence) + go triggerHeadAdvance(t, s, currentEpoch+ChallengeConfidence) // Should be no change to state yet <-s.ch.proveHdlr.processedHeadChanges @@ -446,7 +446,7 @@ func TestChangeHandlerProvingRounds(t *testing.T) { for currentEpoch := abi.ChainEpoch(1); currentEpoch < miner.WPoStChallengeWindow*5; currentEpoch++ { // Trigger a head change di := mock.getDeadline(currentEpoch) - go triggerHeadAdvance(t, s, currentEpoch + ChallengeConfidence) + go triggerHeadAdvance(t, s, currentEpoch+ChallengeConfidence) // Wait for prover to process head change <-s.ch.proveHdlr.processedHeadChanges From a0d7604e7c58ccf08dfdb3d801baa845816d5883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 10 Mar 2021 13:13:03 +0100 Subject: [PATCH 5/5] wdpost: Use chain head tsk in post rand check --- storage/wdpost_run.go | 7 ++++++- storage/wdpost_run_test.go | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/storage/wdpost_run.go b/storage/wdpost_run.go index 79cb13827..ea4d110de 100644 --- a/storage/wdpost_run.go +++ b/storage/wdpost_run.go @@ -600,7 +600,12 @@ func (s *WindowPoStScheduler) runPost(ctx context.Context, di dline.Info, ts *ty return nil, xerrors.Errorf("received no proofs back from generate window post") } - checkRand, err := s.api.ChainGetRandomnessFromBeacon(ctx, types.EmptyTSK, crypto.DomainSeparationTag_WindowedPoStChallengeSeed, di.Challenge, buf.Bytes()) + headTs, err := s.api.ChainHead(ctx) + if err != nil { + return nil, xerrors.Errorf("getting current head: %w", err) + } + + checkRand, err := s.api.ChainGetRandomnessFromBeacon(ctx, headTs.Key(), crypto.DomainSeparationTag_WindowedPoStChallengeSeed, di.Challenge, buf.Bytes()) if err != nil { return nil, xerrors.Errorf("failed to get chain randomness from beacon for window post (ts=%d; deadline=%d): %w", ts.Height(), di, err) } diff --git a/storage/wdpost_run_test.go b/storage/wdpost_run_test.go index 80bd8d3fb..4bf30e3e9 100644 --- a/storage/wdpost_run_test.go +++ b/storage/wdpost_run_test.go @@ -350,7 +350,7 @@ func (m *mockStorageMinerAPI) GasEstimateMessageGas(ctx context.Context, message } func (m *mockStorageMinerAPI) ChainHead(ctx context.Context) (*types.TipSet, error) { - panic("implement me") + return nil, nil } func (m *mockStorageMinerAPI) ChainNotify(ctx context.Context) (<-chan []*api.HeadChange, error) {