package events import ( "context" "testing" "github.com/filecoin-project/specs-actors/actors/abi" "github.com/filecoin-project/specs-actors/actors/crypto" "github.com/stretchr/testify/require" "github.com/filecoin-project/go-address" "github.com/filecoin-project/lotus/chain/types" ) func TestTsCache(t *testing.T) { tsc := newTSCache(50, &tsCacheAPIFailOnStorageCall{t: t}) h := abi.ChainEpoch(75) a, _ := address.NewFromString("t00") add := func() { ts, err := types.NewTipSet([]*types.BlockHeader{{ Miner: a, Height: h, ParentStateRoot: dummyCid, Messages: dummyCid, ParentMessageReceipts: dummyCid, BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS}, BLSAggregate: &crypto.Signature{Type: crypto.SigTypeBLS}, }}) if err != nil { t.Fatal(err) } if err := tsc.add(ts); err != nil { t.Fatal(err) } h++ } for i := 0; i < 9000; i++ { if i%90 > 60 { best, err := tsc.best() if err != nil { t.Fatal(err, "; i:", i) return } if err := tsc.revert(best); err != nil { t.Fatal(err, "; i:", i) return } h-- } else { add() } } } type tsCacheAPIFailOnStorageCall struct { t *testing.T } func (tc *tsCacheAPIFailOnStorageCall) ChainGetTipSetByHeight(ctx context.Context, epoch abi.ChainEpoch, key types.TipSetKey) (*types.TipSet, error) { tc.t.Fatal("storage call") return &types.TipSet{}, nil } func (tc *tsCacheAPIFailOnStorageCall) ChainHead(ctx context.Context) (*types.TipSet, error) { tc.t.Fatal("storage call") return &types.TipSet{}, nil } func TestTsCacheNulls(t *testing.T) { tsc := newTSCache(50, &tsCacheAPIFailOnStorageCall{t: t}) h := abi.ChainEpoch(75) a, _ := address.NewFromString("t00") add := func() { ts, err := types.NewTipSet([]*types.BlockHeader{{ Miner: a, Height: h, ParentStateRoot: dummyCid, Messages: dummyCid, ParentMessageReceipts: dummyCid, BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS}, BLSAggregate: &crypto.Signature{Type: crypto.SigTypeBLS}, }}) if err != nil { t.Fatal(err) } if err := tsc.add(ts); err != nil { t.Fatal(err) } h++ } add() add() add() h += 5 add() add() best, err := tsc.best() require.NoError(t, err) require.Equal(t, h-1, best.Height()) ts, err := tsc.get(h - 1) require.NoError(t, err) require.Equal(t, h-1, ts.Height()) ts, err = tsc.get(h - 2) require.NoError(t, err) require.Equal(t, h-2, ts.Height()) ts, err = tsc.get(h - 3) require.NoError(t, err) require.Nil(t, ts) ts, err = tsc.get(h - 8) require.NoError(t, err) require.Equal(t, h-8, ts.Height()) best, err = tsc.best() require.NoError(t, err) require.NoError(t, tsc.revert(best)) best, err = tsc.best() require.NoError(t, err) require.NoError(t, tsc.revert(best)) best, err = tsc.best() require.NoError(t, err) require.Equal(t, h-8, best.Height()) h += 50 add() ts, err = tsc.get(h - 1) require.NoError(t, err) require.Equal(t, h-1, ts.Height()) } type tsCacheAPIStorageCallCounter struct { t *testing.T chainGetTipSetByHeight int chainHead int } func (tc *tsCacheAPIStorageCallCounter) ChainGetTipSetByHeight(ctx context.Context, epoch abi.ChainEpoch, key types.TipSetKey) (*types.TipSet, error) { tc.chainGetTipSetByHeight++ return &types.TipSet{}, nil } func (tc *tsCacheAPIStorageCallCounter) ChainHead(ctx context.Context) (*types.TipSet, error) { tc.chainHead++ return &types.TipSet{}, nil } func TestTsCacheEmpty(t *testing.T) { // Calling best on an empty cache should just call out to the chain API callCounter := &tsCacheAPIStorageCallCounter{t: t} tsc := newTSCache(50, callCounter) _, err := tsc.best() require.NoError(t, err) require.Equal(t, 1, callCounter.chainHead) }