1228 lines
39 KiB
Go
1228 lines
39 KiB
Go
// stm: #unit
|
|
package wdpost
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ipfs/go-cid"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/filecoin-project/go-address"
|
|
"github.com/filecoin-project/go-state-types/abi"
|
|
minertypes "github.com/filecoin-project/go-state-types/builtin/v8/miner"
|
|
"github.com/filecoin-project/go-state-types/crypto"
|
|
"github.com/filecoin-project/go-state-types/dline"
|
|
tutils "github.com/filecoin-project/specs-actors/support/testing"
|
|
|
|
"github.com/filecoin-project/lotus/chain/types"
|
|
)
|
|
|
|
var dummyCid cid.Cid
|
|
|
|
func init() {
|
|
dummyCid, _ = cid.Parse("bafkqaaa")
|
|
}
|
|
|
|
type proveRes struct {
|
|
posts []minertypes.SubmitWindowedPoStParams
|
|
err error
|
|
}
|
|
|
|
type postStatus string
|
|
|
|
const (
|
|
postStatusStart postStatus = "postStatusStart"
|
|
postStatusProving postStatus = "postStatusProving"
|
|
postStatusComplete postStatus = "postStatusComplete"
|
|
)
|
|
|
|
type mockAPI struct {
|
|
ch *changeHandler
|
|
deadline *dline.Info
|
|
proveResult chan *proveRes
|
|
submitResult chan error
|
|
onStateChange chan struct{}
|
|
|
|
tsLock sync.RWMutex
|
|
ts map[types.TipSetKey]*types.TipSet
|
|
|
|
abortCalledLock sync.RWMutex
|
|
abortCalled bool
|
|
|
|
statesLk sync.RWMutex
|
|
postStates map[abi.ChainEpoch]postStatus
|
|
}
|
|
|
|
func newMockAPI() *mockAPI {
|
|
return &mockAPI{
|
|
proveResult: make(chan *proveRes),
|
|
onStateChange: make(chan struct{}),
|
|
submitResult: make(chan error),
|
|
postStates: make(map[abi.ChainEpoch]postStatus),
|
|
ts: make(map[types.TipSetKey]*types.TipSet),
|
|
}
|
|
}
|
|
|
|
func (m *mockAPI) makeTs(t *testing.T, h abi.ChainEpoch) *types.TipSet {
|
|
m.tsLock.Lock()
|
|
defer m.tsLock.Unlock()
|
|
|
|
ts := makeTs(t, h)
|
|
m.ts[ts.Key()] = ts
|
|
return ts
|
|
}
|
|
|
|
func (m *mockAPI) setDeadline(di *dline.Info) {
|
|
m.tsLock.Lock()
|
|
defer m.tsLock.Unlock()
|
|
|
|
m.deadline = di
|
|
}
|
|
|
|
func (m *mockAPI) getDeadline(currentEpoch abi.ChainEpoch) *dline.Info {
|
|
close := minertypes.WPoStChallengeWindow - 1
|
|
dlIdx := uint64(0)
|
|
for close < currentEpoch {
|
|
close += minertypes.WPoStChallengeWindow
|
|
dlIdx++
|
|
}
|
|
return NewDeadlineInfo(0, dlIdx, currentEpoch)
|
|
}
|
|
|
|
func (m *mockAPI) StateMinerProvingDeadline(ctx context.Context, address address.Address, key types.TipSetKey) (*dline.Info, error) {
|
|
m.tsLock.RLock()
|
|
defer m.tsLock.RUnlock()
|
|
|
|
ts, ok := m.ts[key]
|
|
if !ok {
|
|
panic(fmt.Sprintf("unexpected tipset key %s", key))
|
|
}
|
|
|
|
if m.deadline != nil {
|
|
m.deadline.CurrentEpoch = ts.Height()
|
|
return m.deadline, nil
|
|
}
|
|
|
|
return m.getDeadline(ts.Height()), nil
|
|
}
|
|
|
|
func (m *mockAPI) startGeneratePoST(
|
|
ctx context.Context,
|
|
ts *types.TipSet,
|
|
deadline *dline.Info,
|
|
completeGeneratePoST CompleteGeneratePoSTCb,
|
|
) context.CancelFunc {
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
log.Errorf("mock posting\n")
|
|
m.statesLk.Lock()
|
|
defer m.statesLk.Unlock()
|
|
m.postStates[deadline.Open] = postStatusProving
|
|
|
|
go func() {
|
|
defer cancel()
|
|
|
|
select {
|
|
case psRes := <-m.proveResult:
|
|
m.statesLk.Lock()
|
|
{
|
|
if psRes.err == nil {
|
|
m.postStates[deadline.Open] = postStatusComplete
|
|
} else {
|
|
m.postStates[deadline.Open] = postStatusStart
|
|
}
|
|
}
|
|
m.statesLk.Unlock()
|
|
completeGeneratePoST(psRes.posts, psRes.err)
|
|
case <-ctx.Done():
|
|
completeGeneratePoST(nil, ctx.Err())
|
|
}
|
|
}()
|
|
|
|
return cancel
|
|
}
|
|
|
|
func (m *mockAPI) getPostStatus(di *dline.Info) postStatus {
|
|
m.statesLk.RLock()
|
|
defer m.statesLk.RUnlock()
|
|
|
|
status, ok := m.postStates[di.Open]
|
|
if ok {
|
|
return status
|
|
}
|
|
return postStatusStart
|
|
}
|
|
|
|
func (m *mockAPI) startSubmitPoST(
|
|
ctx context.Context,
|
|
ts *types.TipSet,
|
|
deadline *dline.Info,
|
|
posts []minertypes.SubmitWindowedPoStParams,
|
|
completeSubmitPoST CompleteSubmitPoSTCb,
|
|
) context.CancelFunc {
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
|
|
go func() {
|
|
defer cancel()
|
|
|
|
select {
|
|
case err := <-m.submitResult:
|
|
completeSubmitPoST(err)
|
|
case <-ctx.Done():
|
|
completeSubmitPoST(ctx.Err())
|
|
}
|
|
}()
|
|
|
|
return cancel
|
|
}
|
|
|
|
func (m *mockAPI) onAbort(ts *types.TipSet, deadline *dline.Info) {
|
|
m.abortCalledLock.Lock()
|
|
defer m.abortCalledLock.Unlock()
|
|
m.abortCalled = true
|
|
}
|
|
|
|
func (m *mockAPI) wasAbortCalled() bool {
|
|
m.abortCalledLock.RLock()
|
|
defer m.abortCalledLock.RUnlock()
|
|
return m.abortCalled
|
|
}
|
|
|
|
func (m *mockAPI) recordPoStFailure(err error, ts *types.TipSet, deadline *dline.Info) {
|
|
}
|
|
|
|
func (m *mockAPI) setChangeHandler(ch *changeHandler) {
|
|
m.ch = ch
|
|
}
|
|
|
|
// TestChangeHandlerBasic verifies we can generate a proof and submit it
|
|
func TestChangeHandlerBasic(t *testing.T) {
|
|
//stm: @WDPOST_CHANGE_HANDLER_START_001, @WDPOST_CHANGE_HANDLER_UPDATE_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_001, @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_PW_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_SUBMIT_IF_READY_004, @WDPOST_PROVE_HANDLER_PROCESS_POST_RESULT_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_PROCESS_PROCESS_RESULTS_001
|
|
s := makeScaffolding(t)
|
|
mock := s.mock
|
|
|
|
defer s.ch.shutdown()
|
|
s.ch.start()
|
|
|
|
// Trigger a head change
|
|
currentEpoch := abi.ChainEpoch(1)
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Should start proving
|
|
<-s.ch.proveHdlr.processedHeadChanges
|
|
di := mock.getDeadline(currentEpoch)
|
|
require.Equal(t, postStatusProving, s.mock.getPostStatus(di))
|
|
|
|
// Submitter doesn't have anything to do yet
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
require.Equal(t, SubmitStateStart, s.submitState(di))
|
|
|
|
// Send a response to the call to generate proofs
|
|
posts := []minertypes.SubmitWindowedPoStParams{{Deadline: di.Index}}
|
|
mock.proveResult <- &proveRes{posts: posts}
|
|
|
|
// Should move to proving complete
|
|
<-s.ch.proveHdlr.processedPostResults
|
|
require.Equal(t, postStatusComplete, s.mock.getPostStatus(di))
|
|
|
|
// Move to the correct height to submit the proof
|
|
currentEpoch = 1 + SubmitConfidence
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Should move to submitting state
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
di = mock.getDeadline(currentEpoch)
|
|
require.Equal(t, SubmitStateSubmitting, s.submitState(di))
|
|
|
|
// Send a response to the submit call
|
|
mock.submitResult <- nil
|
|
|
|
// Should move to the complete state
|
|
<-s.ch.submitHdlr.processedSubmitResults
|
|
require.Equal(t, SubmitStateComplete, s.submitState(di))
|
|
}
|
|
|
|
// TestChangeHandlerFromProvingToSubmittingNoHeadChange tests that when the
|
|
// chain is already advanced past the confidence interval, we should move from
|
|
// proving to submitting without a head change in between.
|
|
func TestChangeHandlerFromProvingToSubmittingNoHeadChange(t *testing.T) {
|
|
//stm: @WDPOST_CHANGE_HANDLER_START_001, @WDPOST_CHANGE_HANDLER_UPDATE_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_001, @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_PW_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_SUBMIT_IF_READY_004, @WDPOST_SUBMIT_HANDLER_SUBMIT_IF_READY_005
|
|
//stm: @WDPOST_PROVE_HANDLER_PROCESS_POST_RESULT_001
|
|
s := makeScaffolding(t)
|
|
mock := s.mock
|
|
|
|
// Monitor submit handler's processing of incoming postInfo
|
|
s.ch.submitHdlr.processedPostReady = make(chan *postInfo)
|
|
|
|
defer s.ch.shutdown()
|
|
s.ch.start()
|
|
|
|
// Trigger a head change
|
|
currentEpoch := abi.ChainEpoch(1)
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Should start proving
|
|
<-s.ch.proveHdlr.processedHeadChanges
|
|
di := mock.getDeadline(currentEpoch)
|
|
require.Equal(t, postStatusProving, s.mock.getPostStatus(di))
|
|
|
|
// Submitter doesn't have anything to do yet
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
require.Equal(t, SubmitStateStart, s.submitState(di))
|
|
|
|
// Trigger a head change that advances the chain beyond the submit
|
|
// confidence
|
|
currentEpoch = 1 + SubmitConfidence
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Should be no change to state yet
|
|
<-s.ch.proveHdlr.processedHeadChanges
|
|
require.Equal(t, postStatusProving, s.mock.getPostStatus(di))
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
require.Equal(t, SubmitStateStart, s.submitState(di))
|
|
|
|
// Send a response to the call to generate proofs
|
|
posts := []minertypes.SubmitWindowedPoStParams{{Deadline: di.Index}}
|
|
mock.proveResult <- &proveRes{posts: posts}
|
|
|
|
// Should move to proving complete
|
|
<-s.ch.proveHdlr.processedPostResults
|
|
di = mock.getDeadline(currentEpoch)
|
|
require.Equal(t, postStatusComplete, s.mock.getPostStatus(di))
|
|
|
|
// Should move directly to submitting state with no further head changes
|
|
<-s.ch.submitHdlr.processedPostReady
|
|
require.Equal(t, SubmitStateSubmitting, s.submitState(di))
|
|
}
|
|
|
|
// TestChangeHandlerFromProvingEmptyProofsToComplete tests that when there are no
|
|
// proofs generated we should not submit anything to chain but submit state
|
|
// should move to completed
|
|
func TestChangeHandlerFromProvingEmptyProofsToComplete(t *testing.T) {
|
|
//stm: @WDPOST_CHANGE_HANDLER_START_001, @WDPOST_CHANGE_HANDLER_UPDATE_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_001, @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_PW_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_SUBMIT_IF_READY_004, @WDPOST_SUBMIT_HANDLER_SUBMIT_IF_READY_005, @WDPOST_SUBMIT_HANDLER_SUBMIT_IF_READY_006
|
|
//stm: @WDPOST_PROVE_HANDLER_PROCESS_POST_RESULT_001
|
|
s := makeScaffolding(t)
|
|
mock := s.mock
|
|
|
|
// Monitor submit handler's processing of incoming postInfo
|
|
s.ch.submitHdlr.processedPostReady = make(chan *postInfo)
|
|
|
|
defer s.ch.shutdown()
|
|
s.ch.start()
|
|
|
|
// Trigger a head change
|
|
currentEpoch := abi.ChainEpoch(1)
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Should start proving
|
|
<-s.ch.proveHdlr.processedHeadChanges
|
|
di := mock.getDeadline(currentEpoch)
|
|
require.Equal(t, postStatusProving, s.mock.getPostStatus(di))
|
|
|
|
// Submitter doesn't have anything to do yet
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
require.Equal(t, SubmitStateStart, s.submitState(di))
|
|
|
|
// Trigger a head change that advances the chain beyond the submit
|
|
// confidence
|
|
currentEpoch = 1 + SubmitConfidence
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Should be no change to state yet
|
|
<-s.ch.proveHdlr.processedHeadChanges
|
|
require.Equal(t, postStatusProving, s.mock.getPostStatus(di))
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
require.Equal(t, SubmitStateStart, s.submitState(di))
|
|
|
|
// Send a response to the call to generate proofs with an empty proofs array
|
|
posts := []minertypes.SubmitWindowedPoStParams{}
|
|
mock.proveResult <- &proveRes{posts: posts}
|
|
|
|
// Should move to proving complete
|
|
<-s.ch.proveHdlr.processedPostResults
|
|
di = mock.getDeadline(currentEpoch)
|
|
require.Equal(t, postStatusComplete, s.mock.getPostStatus(di))
|
|
|
|
// Should move directly to submitting complete state
|
|
<-s.ch.submitHdlr.processedPostReady
|
|
require.Equal(t, SubmitStateComplete, s.submitState(di))
|
|
}
|
|
|
|
// TestChangeHandlerDontStartUntilProvingPeriod tests that the handler
|
|
// ignores updates until the proving period has been reached.
|
|
func TestChangeHandlerDontStartUntilProvingPeriod(t *testing.T) {
|
|
//stm: @WDPOST_CHANGE_HANDLER_START_001, @WDPOST_CHANGE_HANDLER_UPDATE_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_001, @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_PW_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_SUBMIT_IF_READY_004
|
|
s := makeScaffolding(t)
|
|
mock := s.mock
|
|
|
|
periodStart := minertypes.WPoStProvingPeriod
|
|
dlIdx := uint64(1)
|
|
currentEpoch := abi.ChainEpoch(10)
|
|
di := NewDeadlineInfo(periodStart, dlIdx, currentEpoch)
|
|
mock.setDeadline(di)
|
|
|
|
defer s.ch.shutdown()
|
|
s.ch.start()
|
|
|
|
// Trigger a head change
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Nothing should happen because the proving period has not started
|
|
select {
|
|
case <-s.ch.proveHdlr.processedHeadChanges:
|
|
require.Fail(t, "unexpected prove change")
|
|
case <-s.ch.submitHdlr.processedHeadChanges:
|
|
require.Fail(t, "unexpected submit change")
|
|
case <-time.After(10 * time.Millisecond):
|
|
}
|
|
|
|
// Advance the head to the next proving period's first epoch
|
|
currentEpoch = periodStart + minertypes.WPoStChallengeWindow
|
|
di = NewDeadlineInfo(periodStart, dlIdx, currentEpoch)
|
|
mock.setDeadline(di)
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Should start proving
|
|
<-s.ch.proveHdlr.processedHeadChanges
|
|
require.Equal(t, postStatusProving, s.mock.getPostStatus(di))
|
|
}
|
|
|
|
// TestChangeHandlerStartProvingNextDeadline verifies that the proof handler
|
|
// starts proving the next deadline after the current one
|
|
func TestChangeHandlerStartProvingNextDeadline(t *testing.T) {
|
|
//stm: @WDPOST_CHANGE_HANDLER_START_001, @WDPOST_CHANGE_HANDLER_UPDATE_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_001, @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_PW_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_SUBMIT_IF_READY_004, @WDPOST_PROVE_HANDLER_PROCESS_POST_RESULT_001
|
|
s := makeScaffolding(t)
|
|
mock := s.mock
|
|
|
|
defer s.ch.shutdown()
|
|
s.ch.start()
|
|
|
|
// Trigger a head change
|
|
currentEpoch := abi.ChainEpoch(1)
|
|
go triggerHeadAdvance(t, s, currentEpoch+ChallengeConfidence)
|
|
|
|
// Should start proving
|
|
<-s.ch.proveHdlr.processedHeadChanges
|
|
di := mock.getDeadline(currentEpoch)
|
|
require.Equal(t, postStatusProving, s.mock.getPostStatus(di))
|
|
|
|
// Trigger a head change that advances the chain beyond the submit
|
|
// confidence
|
|
currentEpoch = 1 + SubmitConfidence
|
|
go triggerHeadAdvance(t, s, currentEpoch+ChallengeConfidence)
|
|
|
|
// Should be no change to state yet
|
|
<-s.ch.proveHdlr.processedHeadChanges
|
|
require.Equal(t, postStatusProving, s.mock.getPostStatus(di))
|
|
|
|
// Send a response to the call to generate proofs
|
|
posts := []minertypes.SubmitWindowedPoStParams{{Deadline: di.Index}}
|
|
mock.proveResult <- &proveRes{posts: posts}
|
|
|
|
// Should move to proving complete
|
|
<-s.ch.proveHdlr.processedPostResults
|
|
di = mock.getDeadline(currentEpoch)
|
|
require.Equal(t, postStatusComplete, s.mock.getPostStatus(di))
|
|
|
|
// Trigger head change that advances the chain to the Challenge epoch for
|
|
// the next deadline
|
|
go func() {
|
|
di = nextDeadline(di)
|
|
currentEpoch = di.Challenge + ChallengeConfidence
|
|
triggerHeadAdvance(t, s, currentEpoch)
|
|
}()
|
|
|
|
// Should start generating next window's proof
|
|
<-s.ch.proveHdlr.processedHeadChanges
|
|
require.Equal(t, postStatusProving, s.mock.getPostStatus(di))
|
|
}
|
|
|
|
// TestChangeHandlerProvingRounds verifies we can generate several rounds of
|
|
// proofs as the chain head advances
|
|
func TestChangeHandlerProvingRounds(t *testing.T) {
|
|
//stm: @WDPOST_CHANGE_HANDLER_START_001, @WDPOST_CHANGE_HANDLER_UPDATE_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_001, @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_PW_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_SUBMIT_IF_READY_002, @WDPOST_SUBMIT_HANDLER_SUBMIT_IF_READY_003, @WDPOST_SUBMIT_HANDLER_SUBMIT_IF_READY_005
|
|
//stm: @WDPOST_SUBMIT_HANDLER_SUBMIT_IF_READY_004, @WDPOST_PROVE_HANDLER_PROCESS_POST_RESULT_001
|
|
s := makeScaffolding(t)
|
|
mock := s.mock
|
|
|
|
defer s.ch.shutdown()
|
|
s.ch.start()
|
|
|
|
completeProofIndex := abi.ChainEpoch(10)
|
|
for currentEpoch := abi.ChainEpoch(1); currentEpoch < minertypes.WPoStChallengeWindow*5; currentEpoch++ {
|
|
// Trigger a head change
|
|
di := mock.getDeadline(currentEpoch)
|
|
go triggerHeadAdvance(t, s, currentEpoch+ChallengeConfidence)
|
|
|
|
// Wait for prover to process head change
|
|
<-s.ch.proveHdlr.processedHeadChanges
|
|
|
|
completeProofEpoch := di.Open + completeProofIndex
|
|
next := nextDeadline(di)
|
|
//fmt.Println("epoch", currentEpoch, s.mock.getPostStatus(di), "next", s.mock.getPostStatus(next))
|
|
if currentEpoch >= next.Challenge {
|
|
require.Equal(t, postStatusComplete, s.mock.getPostStatus(di))
|
|
// At the next deadline's challenge epoch, should start proving
|
|
// for that epoch
|
|
require.Equal(t, postStatusProving, s.mock.getPostStatus(next))
|
|
} else if currentEpoch > completeProofEpoch {
|
|
// After proving for the round is complete, should be in complete state
|
|
require.Equal(t, postStatusComplete, s.mock.getPostStatus(di))
|
|
require.Equal(t, postStatusStart, s.mock.getPostStatus(next))
|
|
} else {
|
|
// Until proving completes, should be in the proving state
|
|
require.Equal(t, postStatusProving, s.mock.getPostStatus(di))
|
|
require.Equal(t, postStatusStart, s.mock.getPostStatus(next))
|
|
}
|
|
|
|
// Wait for submitter to process head change
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
|
|
completeSubmitEpoch := completeProofEpoch + 1
|
|
//fmt.Println("epoch", currentEpoch, s.submitState(di))
|
|
if currentEpoch > completeSubmitEpoch {
|
|
require.Equal(t, SubmitStateComplete, s.submitState(di))
|
|
} else if currentEpoch > completeProofEpoch {
|
|
require.Equal(t, SubmitStateSubmitting, s.submitState(di))
|
|
} else {
|
|
require.Equal(t, SubmitStateStart, s.submitState(di))
|
|
}
|
|
|
|
if currentEpoch == completeProofEpoch {
|
|
// Send a response to the call to generate proofs
|
|
posts := []minertypes.SubmitWindowedPoStParams{{Deadline: di.Index}}
|
|
mock.proveResult <- &proveRes{posts: posts}
|
|
|
|
// Should move to proving complete
|
|
<-s.ch.proveHdlr.processedPostResults
|
|
require.Equal(t, postStatusComplete, s.mock.getPostStatus(di))
|
|
}
|
|
|
|
if currentEpoch == completeSubmitEpoch {
|
|
// Send a response to the submit call
|
|
mock.submitResult <- nil
|
|
|
|
// Should move to the complete state
|
|
<-s.ch.submitHdlr.processedSubmitResults
|
|
require.Equal(t, SubmitStateComplete, s.submitState(di))
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestChangeHandlerProvingErrorRecovery verifies that the proof handler
|
|
// recovers correctly from an error
|
|
func TestChangeHandlerProvingErrorRecovery(t *testing.T) {
|
|
//stm: @WDPOST_CHANGE_HANDLER_START_001, @WDPOST_CHANGE_HANDLER_UPDATE_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_001, @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_PW_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_SUBMIT_IF_READY_004, @WDPOST_PROVE_HANDLER_PROCESS_POST_RESULT_001
|
|
s := makeScaffolding(t)
|
|
mock := s.mock
|
|
|
|
defer s.ch.shutdown()
|
|
s.ch.start()
|
|
|
|
// Trigger a head change
|
|
currentEpoch := abi.ChainEpoch(1)
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Should start proving
|
|
<-s.ch.proveHdlr.processedHeadChanges
|
|
di := mock.getDeadline(currentEpoch)
|
|
require.Equal(t, postStatusProving, s.mock.getPostStatus(di))
|
|
|
|
// Send an error response to the call to generate proofs
|
|
mock.proveResult <- &proveRes{err: fmt.Errorf("err")}
|
|
|
|
// Should abort and then move to start state
|
|
<-s.ch.proveHdlr.processedPostResults
|
|
require.Equal(t, postStatusStart, s.mock.getPostStatus(di))
|
|
|
|
// Trigger a head change
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Should start proving
|
|
<-s.ch.proveHdlr.processedHeadChanges
|
|
require.Equal(t, postStatusProving, s.mock.getPostStatus(di))
|
|
|
|
// Send a success response to the call to generate proofs
|
|
posts := []minertypes.SubmitWindowedPoStParams{{Deadline: di.Index}}
|
|
mock.proveResult <- &proveRes{posts: posts}
|
|
|
|
// Should move to proving complete
|
|
<-s.ch.proveHdlr.processedPostResults
|
|
require.Equal(t, postStatusComplete, s.mock.getPostStatus(di))
|
|
}
|
|
|
|
// TestChangeHandlerSubmitErrorRecovery verifies that the submit handler
|
|
// recovers correctly from an error
|
|
func TestChangeHandlerSubmitErrorRecovery(t *testing.T) {
|
|
//stm: @WDPOST_CHANGE_HANDLER_START_001, @WDPOST_CHANGE_HANDLER_UPDATE_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_001, @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_PW_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_SUBMIT_IF_READY_004, @WDPOST_PROVE_HANDLER_PROCESS_POST_RESULT_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_PROCESS_PROCESS_RESULTS_001
|
|
s := makeScaffolding(t)
|
|
mock := s.mock
|
|
|
|
defer s.ch.shutdown()
|
|
s.ch.start()
|
|
|
|
// Trigger a head change
|
|
currentEpoch := abi.ChainEpoch(1)
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Should start proving
|
|
<-s.ch.proveHdlr.processedHeadChanges
|
|
di := mock.getDeadline(currentEpoch)
|
|
require.Equal(t, postStatusProving, s.mock.getPostStatus(di))
|
|
|
|
// Submitter doesn't have anything to do yet
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
require.Equal(t, SubmitStateStart, s.submitState(di))
|
|
|
|
// Send a response to the call to generate proofs
|
|
posts := []minertypes.SubmitWindowedPoStParams{{Deadline: di.Index}}
|
|
mock.proveResult <- &proveRes{posts: posts}
|
|
|
|
// Should move to proving complete
|
|
<-s.ch.proveHdlr.processedPostResults
|
|
require.Equal(t, postStatusComplete, s.mock.getPostStatus(di))
|
|
|
|
// Move to the correct height to submit the proof
|
|
currentEpoch = 1 + SubmitConfidence
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Read from prover incoming channel (so as not to block)
|
|
<-s.ch.proveHdlr.processedHeadChanges
|
|
|
|
// Should move to submitting state
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
di = mock.getDeadline(currentEpoch)
|
|
require.Equal(t, SubmitStateSubmitting, s.submitState(di))
|
|
|
|
// Send an error response to the call to submit
|
|
mock.submitResult <- fmt.Errorf("err")
|
|
|
|
// Should abort and then move back to the start state
|
|
<-s.ch.submitHdlr.processedSubmitResults
|
|
require.Equal(t, SubmitStateStart, s.submitState(di))
|
|
require.True(t, mock.wasAbortCalled())
|
|
|
|
// Trigger another head change
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Read from prover incoming channel (so as not to block)
|
|
<-s.ch.proveHdlr.processedHeadChanges
|
|
|
|
// Should move to submitting state
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
di = mock.getDeadline(currentEpoch)
|
|
require.Equal(t, SubmitStateSubmitting, s.submitState(di))
|
|
|
|
// Send a response to the submit call
|
|
mock.submitResult <- nil
|
|
|
|
// Should move to the complete state
|
|
<-s.ch.submitHdlr.processedSubmitResults
|
|
require.Equal(t, SubmitStateComplete, s.submitState(di))
|
|
}
|
|
|
|
// TestChangeHandlerProveExpiry verifies that the prove handler
|
|
// behaves correctly on expiry
|
|
func TestChangeHandlerProveExpiry(t *testing.T) {
|
|
//stm: @WDPOST_CHANGE_HANDLER_START_001, @WDPOST_CHANGE_HANDLER_UPDATE_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_001, @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_PW_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_SUBMIT_IF_READY_004, @WDPOST_PROVE_HANDLER_PROCESS_POST_RESULT_001
|
|
s := makeScaffolding(t)
|
|
mock := s.mock
|
|
|
|
defer s.ch.shutdown()
|
|
s.ch.start()
|
|
|
|
// Trigger a head change
|
|
currentEpoch := abi.ChainEpoch(1)
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Should start proving
|
|
<-s.ch.proveHdlr.processedHeadChanges
|
|
di := mock.getDeadline(currentEpoch)
|
|
require.Equal(t, postStatusProving, s.mock.getPostStatus(di))
|
|
|
|
// Move to a height that expires the current proof
|
|
currentEpoch = minertypes.WPoStChallengeWindow
|
|
di = mock.getDeadline(currentEpoch)
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Should trigger an abort and start proving for the new deadline
|
|
<-s.ch.proveHdlr.processedHeadChanges
|
|
require.Equal(t, postStatusProving, s.mock.getPostStatus(di))
|
|
<-s.ch.proveHdlr.processedPostResults
|
|
require.True(t, mock.wasAbortCalled())
|
|
|
|
// Send a response to the call to generate proofs
|
|
posts := []minertypes.SubmitWindowedPoStParams{{Deadline: di.Index}}
|
|
mock.proveResult <- &proveRes{posts: posts}
|
|
|
|
// Should move to proving complete
|
|
<-s.ch.proveHdlr.processedPostResults
|
|
require.Equal(t, postStatusComplete, s.mock.getPostStatus(di))
|
|
}
|
|
|
|
// TestChangeHandlerSubmitExpiry verifies that the submit handler
|
|
// behaves correctly on expiry
|
|
func TestChangeHandlerSubmitExpiry(t *testing.T) {
|
|
//stm: @WDPOST_CHANGE_HANDLER_START_001, @WDPOST_CHANGE_HANDLER_UPDATE_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_001, @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_PW_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_SUBMIT_IF_READY_004, @WDPOST_SUBMIT_HANDLER_SUBMIT_IF_READY_002, @WDPOST_PROVE_HANDLER_PROCESS_POST_RESULT_001
|
|
s := makeScaffolding(t)
|
|
mock := s.mock
|
|
|
|
// Ignore prove handler head change processing for this test
|
|
s.ch.proveHdlr.processedHeadChanges = nil
|
|
|
|
defer s.ch.shutdown()
|
|
s.ch.start()
|
|
|
|
// Trigger a head change
|
|
currentEpoch := abi.ChainEpoch(1)
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Submitter doesn't have anything to do yet
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
di := mock.getDeadline(currentEpoch)
|
|
require.Equal(t, SubmitStateStart, s.submitState(di))
|
|
|
|
// Send a response to the call to generate proofs
|
|
posts := []minertypes.SubmitWindowedPoStParams{{Deadline: di.Index}}
|
|
mock.proveResult <- &proveRes{posts: posts}
|
|
|
|
// Should move to proving complete
|
|
<-s.ch.proveHdlr.processedPostResults
|
|
require.Equal(t, postStatusComplete, s.mock.getPostStatus(di))
|
|
|
|
// Move to the correct height to submit the proof
|
|
currentEpoch = 1 + SubmitConfidence
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Should move to submitting state
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
di = mock.getDeadline(currentEpoch)
|
|
require.Equal(t, SubmitStateSubmitting, s.submitState(di))
|
|
|
|
// Move to a height that expires the submit
|
|
currentEpoch = minertypes.WPoStChallengeWindow
|
|
di = mock.getDeadline(currentEpoch)
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Should trigger an abort and move back to start state
|
|
var wg sync.WaitGroup
|
|
wg.Add(2)
|
|
go func() {
|
|
defer wg.Done()
|
|
|
|
<-s.ch.submitHdlr.processedSubmitResults
|
|
require.True(t, mock.wasAbortCalled())
|
|
}()
|
|
|
|
go func() {
|
|
defer wg.Done()
|
|
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
require.Equal(t, SubmitStateStart, s.submitState(di))
|
|
}()
|
|
|
|
wg.Wait()
|
|
}
|
|
|
|
// TestChangeHandlerProveRevert verifies that the prove handler
|
|
// behaves correctly on revert
|
|
func TestChangeHandlerProveRevert(t *testing.T) {
|
|
//stm: @WDPOST_CHANGE_HANDLER_START_001, @WDPOST_CHANGE_HANDLER_UPDATE_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_001, @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_PW_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_SUBMIT_IF_READY_004, @WDPOST_PROVE_HANDLER_PROCESS_POST_RESULT_001
|
|
s := makeScaffolding(t)
|
|
mock := s.mock
|
|
|
|
defer s.ch.shutdown()
|
|
s.ch.start()
|
|
|
|
// Trigger a head change
|
|
currentEpoch := minertypes.WPoStChallengeWindow
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Should start proving
|
|
<-s.ch.proveHdlr.processedHeadChanges
|
|
di := mock.getDeadline(currentEpoch)
|
|
require.Equal(t, postStatusProving, s.mock.getPostStatus(di))
|
|
|
|
// Trigger a revert to the previous epoch
|
|
revertEpoch := di.Open - 5
|
|
go triggerHeadChange(t, s, revertEpoch, currentEpoch)
|
|
|
|
// Should be no change
|
|
<-s.ch.proveHdlr.processedHeadChanges
|
|
require.Equal(t, postStatusProving, s.mock.getPostStatus(di))
|
|
|
|
// Send a response to the call to generate proofs
|
|
posts := []minertypes.SubmitWindowedPoStParams{{Deadline: di.Index}}
|
|
mock.proveResult <- &proveRes{posts: posts}
|
|
|
|
// Should move to proving complete
|
|
<-s.ch.proveHdlr.processedPostResults
|
|
require.Equal(t, postStatusComplete, s.mock.getPostStatus(di))
|
|
require.False(t, mock.wasAbortCalled())
|
|
}
|
|
|
|
// TestChangeHandlerSubmittingRevert verifies that the submit handler
|
|
// behaves correctly when there's a revert from the submitting state
|
|
func TestChangeHandlerSubmittingRevert(t *testing.T) {
|
|
//stm: @WDPOST_CHANGE_HANDLER_START_001, @WDPOST_CHANGE_HANDLER_UPDATE_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_001, @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_PW_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_SUBMIT_IF_READY_004, @WDPOST_PROVE_HANDLER_PROCESS_POST_RESULT_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_PROCESS_PROCESS_RESULTS_001
|
|
s := makeScaffolding(t)
|
|
mock := s.mock
|
|
|
|
// Ignore prove handler head change processing for this test
|
|
s.ch.proveHdlr.processedHeadChanges = nil
|
|
|
|
defer s.ch.shutdown()
|
|
s.ch.start()
|
|
|
|
// Trigger a head change
|
|
currentEpoch := minertypes.WPoStChallengeWindow
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Submitter doesn't have anything to do yet
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
di := mock.getDeadline(currentEpoch)
|
|
require.Equal(t, SubmitStateStart, s.submitState(di))
|
|
|
|
// Send a response to the call to generate proofs
|
|
posts := []minertypes.SubmitWindowedPoStParams{{Deadline: di.Index}}
|
|
mock.proveResult <- &proveRes{posts: posts}
|
|
|
|
// Should move to proving complete
|
|
<-s.ch.proveHdlr.processedPostResults
|
|
require.Equal(t, postStatusComplete, s.mock.getPostStatus(di))
|
|
|
|
// Move to the correct height to submit the proof
|
|
currentEpoch = currentEpoch + 1 + SubmitConfidence
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Should move to submitting state
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
di = mock.getDeadline(currentEpoch)
|
|
require.Equal(t, SubmitStateSubmitting, s.submitState(di))
|
|
|
|
// Trigger a revert to the previous epoch
|
|
revertEpoch := di.Open - 5
|
|
go triggerHeadChange(t, s, revertEpoch, currentEpoch)
|
|
|
|
var wg sync.WaitGroup
|
|
wg.Add(2)
|
|
|
|
// Should trigger an abort
|
|
go func() {
|
|
defer wg.Done()
|
|
|
|
<-s.ch.submitHdlr.processedSubmitResults
|
|
require.True(t, mock.wasAbortCalled())
|
|
}()
|
|
|
|
// Should resubmit current epoch
|
|
go func() {
|
|
defer wg.Done()
|
|
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
require.Equal(t, SubmitStateSubmitting, s.submitState(di))
|
|
}()
|
|
|
|
wg.Wait()
|
|
|
|
// Send a response to the resubmit call
|
|
mock.submitResult <- nil
|
|
|
|
// Should move to the complete state
|
|
<-s.ch.submitHdlr.processedSubmitResults
|
|
require.Equal(t, SubmitStateComplete, s.submitState(di))
|
|
}
|
|
|
|
// TestChangeHandlerSubmitCompleteRevert verifies that the submit handler
|
|
// behaves correctly when there's a revert from the submit complete state
|
|
func TestChangeHandlerSubmitCompleteRevert(t *testing.T) {
|
|
//stm: @WDPOST_CHANGE_HANDLER_START_001, @WDPOST_CHANGE_HANDLER_UPDATE_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_001, @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_PW_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_SUBMIT_IF_READY_004, @WDPOST_PROVE_HANDLER_PROCESS_POST_RESULT_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_PROCESS_PROCESS_RESULTS_001
|
|
s := makeScaffolding(t)
|
|
mock := s.mock
|
|
|
|
// Ignore prove handler head change processing for this test
|
|
s.ch.proveHdlr.processedHeadChanges = nil
|
|
|
|
defer s.ch.shutdown()
|
|
s.ch.start()
|
|
|
|
// Trigger a head change
|
|
currentEpoch := minertypes.WPoStChallengeWindow
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Submitter doesn't have anything to do yet
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
di := mock.getDeadline(currentEpoch)
|
|
require.Equal(t, SubmitStateStart, s.submitState(di))
|
|
|
|
// Send a response to the call to generate proofs
|
|
posts := []minertypes.SubmitWindowedPoStParams{{Deadline: di.Index}}
|
|
mock.proveResult <- &proveRes{posts: posts}
|
|
|
|
// Should move to proving complete
|
|
<-s.ch.proveHdlr.processedPostResults
|
|
require.Equal(t, postStatusComplete, s.mock.getPostStatus(di))
|
|
|
|
// Move to the correct height to submit the proof
|
|
currentEpoch = currentEpoch + 1 + SubmitConfidence
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Should move to submitting state
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
di = mock.getDeadline(currentEpoch)
|
|
require.Equal(t, SubmitStateSubmitting, s.submitState(di))
|
|
|
|
// Send a response to the resubmit call
|
|
mock.submitResult <- nil
|
|
|
|
// Should move to the complete state
|
|
<-s.ch.submitHdlr.processedSubmitResults
|
|
require.Equal(t, SubmitStateComplete, s.submitState(di))
|
|
|
|
// Trigger a revert to the previous epoch
|
|
revertEpoch := di.Open - 5
|
|
go triggerHeadChange(t, s, revertEpoch, currentEpoch)
|
|
|
|
// Should resubmit current epoch
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
require.Equal(t, SubmitStateSubmitting, s.submitState(di))
|
|
|
|
// Send a response to the resubmit call
|
|
mock.submitResult <- nil
|
|
|
|
// Should move to the complete state
|
|
<-s.ch.submitHdlr.processedSubmitResults
|
|
require.Equal(t, SubmitStateComplete, s.submitState(di))
|
|
}
|
|
|
|
// TestChangeHandlerSubmitRevertTwoEpochs verifies that the submit handler
|
|
// behaves correctly when the revert is two epochs deep
|
|
func TestChangeHandlerSubmitRevertTwoEpochs(t *testing.T) {
|
|
//stm: @WDPOST_CHANGE_HANDLER_START_001, @WDPOST_CHANGE_HANDLER_UPDATE_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_001, @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_PW_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_SUBMIT_IF_READY_004, @WDPOST_SUBMIT_HANDLER_SUBMIT_IF_READY_002, @WDPOST_PROVE_HANDLER_PROCESS_POST_RESULT_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_PROCESS_PROCESS_RESULTS_001
|
|
s := makeScaffolding(t)
|
|
mock := s.mock
|
|
|
|
// Ignore prove handler head change processing for this test
|
|
s.ch.proveHdlr.processedHeadChanges = nil
|
|
|
|
defer s.ch.shutdown()
|
|
s.ch.start()
|
|
|
|
// Trigger a head change
|
|
currentEpoch := minertypes.WPoStChallengeWindow
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Submitter doesn't have anything to do yet
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
diE1 := mock.getDeadline(currentEpoch)
|
|
require.Equal(t, SubmitStateStart, s.submitState(diE1))
|
|
|
|
// Send a response to the call to generate proofs
|
|
posts := []minertypes.SubmitWindowedPoStParams{{Deadline: diE1.Index}}
|
|
mock.proveResult <- &proveRes{posts: posts}
|
|
|
|
// Should move to proving complete
|
|
<-s.ch.proveHdlr.processedPostResults
|
|
require.Equal(t, postStatusComplete, s.mock.getPostStatus(diE1))
|
|
|
|
// Move to the challenge epoch for the next deadline
|
|
diE2 := nextDeadline(diE1)
|
|
currentEpoch = diE2.Challenge + ChallengeConfidence
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Should move to submitting state for epoch 1
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
diE1 = mock.getDeadline(currentEpoch)
|
|
require.Equal(t, SubmitStateSubmitting, s.submitState(diE1))
|
|
|
|
// Send a response to the submit call for epoch 1
|
|
mock.submitResult <- nil
|
|
|
|
// Should move to the complete state for epoch 1
|
|
<-s.ch.submitHdlr.processedSubmitResults
|
|
require.Equal(t, SubmitStateComplete, s.submitState(diE1))
|
|
|
|
// Should start proving epoch 2
|
|
// Send a response to the call to generate proofs
|
|
postsE2 := []minertypes.SubmitWindowedPoStParams{{Deadline: diE2.Index}}
|
|
mock.proveResult <- &proveRes{posts: postsE2}
|
|
|
|
// Should move to proving complete for epoch 2
|
|
<-s.ch.proveHdlr.processedPostResults
|
|
require.Equal(t, postStatusComplete, s.mock.getPostStatus(diE2))
|
|
|
|
// Move to the correct height to submit the proof for epoch 2
|
|
currentEpoch = diE2.Open + 1 + SubmitConfidence
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Should move to submitting state for epoch 2
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
diE2 = mock.getDeadline(currentEpoch)
|
|
require.Equal(t, SubmitStateSubmitting, s.submitState(diE2))
|
|
|
|
// Trigger a revert through two epochs (from epoch 2 to epoch 0)
|
|
revertEpoch := diE1.Open - 5
|
|
go triggerHeadChange(t, s, revertEpoch, currentEpoch)
|
|
|
|
var wg sync.WaitGroup
|
|
wg.Add(2)
|
|
|
|
// Should trigger an abort
|
|
go func() {
|
|
defer wg.Done()
|
|
|
|
<-s.ch.submitHdlr.processedSubmitResults
|
|
require.True(t, mock.wasAbortCalled())
|
|
}()
|
|
|
|
go func() {
|
|
defer wg.Done()
|
|
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
|
|
// Should reset epoch 1 (that is expired) to start state
|
|
require.Equal(t, SubmitStateStart, s.submitState(diE1))
|
|
// Should resubmit epoch 2
|
|
require.Equal(t, SubmitStateSubmitting, s.submitState(diE2))
|
|
}()
|
|
|
|
wg.Wait()
|
|
|
|
// Send a response to the resubmit call for epoch 2
|
|
mock.submitResult <- nil
|
|
|
|
// Should move to the complete state for epoch 2
|
|
<-s.ch.submitHdlr.processedSubmitResults
|
|
require.Equal(t, SubmitStateComplete, s.submitState(diE2))
|
|
}
|
|
|
|
// TestChangeHandlerSubmitRevertAdvanceLess verifies that the submit handler
|
|
// behaves correctly when the revert is two epochs deep and the advance is
|
|
// to a lower height than before
|
|
func TestChangeHandlerSubmitRevertAdvanceLess(t *testing.T) {
|
|
//stm: @WDPOST_CHANGE_HANDLER_START_001, @WDPOST_CHANGE_HANDLER_UPDATE_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_001, @WDPOST_SUBMIT_HANDLER_PROCESS_HEAD_CHANGE_PW_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_SUBMIT_IF_READY_004, @WDPOST_SUBMIT_HANDLER_SUBMIT_IF_READY_002, @WDPOST_PROVE_HANDLER_PROCESS_POST_RESULT_001
|
|
//stm: @WDPOST_SUBMIT_HANDLER_PROCESS_PROCESS_RESULTS_001
|
|
s := makeScaffolding(t)
|
|
mock := s.mock
|
|
|
|
// Ignore prove handler head change processing for this test
|
|
s.ch.proveHdlr.processedHeadChanges = nil
|
|
|
|
defer s.ch.shutdown()
|
|
s.ch.start()
|
|
|
|
// Trigger a head change
|
|
currentEpoch := minertypes.WPoStChallengeWindow
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Submitter doesn't have anything to do yet
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
diE1 := mock.getDeadline(currentEpoch)
|
|
require.Equal(t, SubmitStateStart, s.submitState(diE1))
|
|
|
|
// Send a response to the call to generate proofs
|
|
posts := []minertypes.SubmitWindowedPoStParams{{Deadline: diE1.Index}}
|
|
mock.proveResult <- &proveRes{posts: posts}
|
|
|
|
// Should move to proving complete
|
|
<-s.ch.proveHdlr.processedPostResults
|
|
require.Equal(t, postStatusComplete, s.mock.getPostStatus(diE1))
|
|
|
|
// Move to the challenge epoch for the next deadline
|
|
diE2 := nextDeadline(diE1)
|
|
currentEpoch = diE2.Challenge + ChallengeConfidence
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Should move to submitting state for epoch 1
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
diE1 = mock.getDeadline(currentEpoch)
|
|
require.Equal(t, SubmitStateSubmitting, s.submitState(diE1))
|
|
|
|
// Send a response to the submit call for epoch 1
|
|
mock.submitResult <- nil
|
|
|
|
// Should move to the complete state for epoch 1
|
|
<-s.ch.submitHdlr.processedSubmitResults
|
|
require.Equal(t, SubmitStateComplete, s.submitState(diE1))
|
|
|
|
// Should start proving epoch 2
|
|
// Send a response to the call to generate proofs
|
|
postsE2 := []minertypes.SubmitWindowedPoStParams{{Deadline: diE2.Index}}
|
|
mock.proveResult <- &proveRes{posts: postsE2}
|
|
|
|
// Should move to proving complete for epoch 2
|
|
<-s.ch.proveHdlr.processedPostResults
|
|
require.Equal(t, postStatusComplete, s.mock.getPostStatus(diE2))
|
|
|
|
// Move to the correct height to submit the proof for epoch 2
|
|
currentEpoch = diE2.Open + 1 + SubmitConfidence
|
|
go triggerHeadAdvance(t, s, currentEpoch)
|
|
|
|
// Should move to submitting state for epoch 2
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
diE2 = mock.getDeadline(currentEpoch)
|
|
require.Equal(t, SubmitStateSubmitting, s.submitState(diE2))
|
|
|
|
// Trigger a revert through two epochs (from epoch 2 to epoch 0)
|
|
// then advance to the previous epoch (to epoch 1)
|
|
revertEpoch := diE1.Open - 5
|
|
currentEpoch = diE2.Open - 1
|
|
go triggerHeadChange(t, s, revertEpoch, currentEpoch)
|
|
|
|
var wg sync.WaitGroup
|
|
wg.Add(2)
|
|
|
|
// Should trigger an abort
|
|
go func() {
|
|
defer wg.Done()
|
|
|
|
<-s.ch.submitHdlr.processedSubmitResults
|
|
require.True(t, mock.wasAbortCalled())
|
|
}()
|
|
|
|
go func() {
|
|
defer wg.Done()
|
|
|
|
<-s.ch.submitHdlr.processedHeadChanges
|
|
|
|
// Should resubmit epoch 1
|
|
require.Equal(t, SubmitStateSubmitting, s.submitState(diE1))
|
|
// Should reset epoch 2 to start state
|
|
require.Equal(t, SubmitStateStart, s.submitState(diE2))
|
|
}()
|
|
|
|
wg.Wait()
|
|
|
|
// Send a response to the resubmit call for epoch 1
|
|
mock.submitResult <- nil
|
|
|
|
// Should move to the complete state for epoch 1
|
|
<-s.ch.submitHdlr.processedSubmitResults
|
|
require.Equal(t, SubmitStateComplete, s.submitState(diE1))
|
|
}
|
|
|
|
type smScaffolding struct {
|
|
ctx context.Context
|
|
mock *mockAPI
|
|
ch *changeHandler
|
|
}
|
|
|
|
func makeScaffolding(t *testing.T) *smScaffolding {
|
|
ctx := context.Background()
|
|
actor := tutils.NewActorAddr(t, "actor")
|
|
mock := newMockAPI()
|
|
ch := newChangeHandler(mock, actor)
|
|
mock.setChangeHandler(ch)
|
|
|
|
ch.proveHdlr.processedHeadChanges = make(chan *headChange)
|
|
ch.proveHdlr.processedPostResults = make(chan *postResult)
|
|
|
|
ch.submitHdlr.processedHeadChanges = make(chan *headChange)
|
|
ch.submitHdlr.processedSubmitResults = make(chan *submitResult)
|
|
|
|
return &smScaffolding{
|
|
ctx: ctx,
|
|
mock: mock,
|
|
ch: ch,
|
|
}
|
|
}
|
|
|
|
func triggerHeadAdvance(t *testing.T, s *smScaffolding, height abi.ChainEpoch) {
|
|
ts := s.mock.makeTs(t, height)
|
|
err := s.ch.update(s.ctx, nil, ts)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func triggerHeadChange(t *testing.T, s *smScaffolding, revertHeight, advanceHeight abi.ChainEpoch) {
|
|
tsRev := s.mock.makeTs(t, revertHeight)
|
|
tsAdv := s.mock.makeTs(t, advanceHeight)
|
|
err := s.ch.update(s.ctx, tsRev, tsAdv)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func (s *smScaffolding) submitState(di *dline.Info) SubmitState {
|
|
return s.ch.submitHdlr.getPostWindow(di).submitState
|
|
}
|
|
|
|
func makeTs(t *testing.T, h abi.ChainEpoch) *types.TipSet {
|
|
var parents []cid.Cid
|
|
msgcid := dummyCid
|
|
|
|
a, _ := address.NewFromString("t00")
|
|
b, _ := address.NewFromString("t02")
|
|
var ts, err = types.NewTipSet([]*types.BlockHeader{
|
|
{
|
|
Height: h,
|
|
Miner: a,
|
|
|
|
Parents: parents,
|
|
|
|
Ticket: &types.Ticket{VRFProof: []byte{byte(h % 2)}},
|
|
|
|
ParentStateRoot: dummyCid,
|
|
Messages: msgcid,
|
|
ParentMessageReceipts: dummyCid,
|
|
|
|
BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS},
|
|
BLSAggregate: &crypto.Signature{Type: crypto.SigTypeBLS},
|
|
},
|
|
{
|
|
Height: h,
|
|
Miner: b,
|
|
|
|
Parents: parents,
|
|
|
|
Ticket: &types.Ticket{VRFProof: []byte{byte((h + 1) % 2)}},
|
|
|
|
ParentStateRoot: dummyCid,
|
|
Messages: msgcid,
|
|
ParentMessageReceipts: dummyCid,
|
|
|
|
BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS},
|
|
BLSAggregate: &crypto.Signature{Type: crypto.SigTypeBLS},
|
|
},
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
return ts
|
|
}
|