fix(splitstore): fix a panic on revert-only head changes

Calling, e.g., `lotus chain sethead` on an ancestor tipset won't apply
any new blocks, it'll just revert a bunch. This will lead to HeadChange
calls with no new blocks to apply.

fixes #6125
This commit is contained in:
Steven Allen 2021-04-28 19:55:18 -07:00
parent 4688da5178
commit 63db9e1633
2 changed files with 31 additions and 2 deletions

View File

@ -406,6 +406,11 @@ func (s *SplitStore) Close() error {
} }
func (s *SplitStore) HeadChange(_, apply []*types.TipSet) error { func (s *SplitStore) HeadChange(_, apply []*types.TipSet) error {
// Revert only.
if len(apply) == 0 {
return nil
}
s.mx.Lock() s.mx.Lock()
curTs := apply[len(apply)-1] curTs := apply[len(apply)-1]
epoch := curTs.Height() epoch := curTs.Height()

View File

@ -27,7 +27,7 @@ func init() {
} }
func testSplitStore(t *testing.T, cfg *Config) { func testSplitStore(t *testing.T, cfg *Config) {
chain := &mockChain{} chain := &mockChain{t: t}
// genesis // genesis
genBlock := mock.MkBlock(nil, 0, 0) genBlock := mock.MkBlock(nil, 0, 0)
genTs := mock.TipSet(genBlock) genTs := mock.TipSet(genBlock)
@ -169,6 +169,9 @@ func testSplitStore(t *testing.T, cfg *Config) {
t.Errorf("expected %d hot blocks, but got %d", 7, hotCnt) t.Errorf("expected %d hot blocks, but got %d", 7, hotCnt)
} }
} }
// Make sure we can revert without panicing.
chain.revert(2)
} }
func TestSplitStoreSimpleCompaction(t *testing.T) { func TestSplitStoreSimpleCompaction(t *testing.T) {
@ -191,6 +194,8 @@ func TestSplitStoreFullCompactionWithGC(t *testing.T) {
} }
type mockChain struct { type mockChain struct {
t testing.TB
sync.Mutex sync.Mutex
tipsets []*types.TipSet tipsets []*types.TipSet
listener func(revert []*types.TipSet, apply []*types.TipSet) error listener func(revert []*types.TipSet, apply []*types.TipSet) error
@ -204,7 +209,26 @@ func (c *mockChain) push(ts *types.TipSet) {
if c.listener != nil { if c.listener != nil {
err := c.listener(nil, []*types.TipSet{ts}) err := c.listener(nil, []*types.TipSet{ts})
if err != nil { if err != nil {
log.Errorf("mockchain: error dispatching listener: %s", err) c.t.Errorf("mockchain: error dispatching listener: %s", err)
}
}
}
func (c *mockChain) revert(count int) {
c.Lock()
revert := make([]*types.TipSet, count)
if count > len(c.tipsets) {
c.Unlock()
c.t.Fatalf("not enough tipsets to revert")
}
copy(revert, c.tipsets[len(c.tipsets)-count:])
c.tipsets = c.tipsets[:len(c.tipsets)-count]
c.Unlock()
if c.listener != nil {
err := c.listener(revert, nil)
if err != nil {
c.t.Errorf("mockchain: error dispatching listener: %s", err)
} }
} }
} }