test: improve chain event tests
This commit is contained in:
parent
3846170302
commit
82ac0a24a0
@ -393,7 +393,7 @@ type StateMatchFunc func(oldTs, newTs *types.TipSet) (bool, StateChange, error)
|
||||
// * `StateChangeHandler` is called when the specified state change was observed
|
||||
// on-chain, and a confidence threshold was reached, or the specified `timeout`
|
||||
// height was reached with no state change observed. When this callback is
|
||||
// invoked on a timeout, `oldState` and `newState` are set to nil.
|
||||
// invoked on a timeout, `oldTs` and `states are set to nil.
|
||||
// This callback returns a boolean specifying whether further notifications
|
||||
// should be sent, like `more` return param from `CheckFunc` above.
|
||||
//
|
||||
|
@ -43,10 +43,10 @@ type fakeCS struct {
|
||||
|
||||
tipsets map[types.TipSetKey]*types.TipSet
|
||||
|
||||
sub func(rev, app []*types.TipSet)
|
||||
|
||||
callNumberLk sync.Mutex
|
||||
callNumber map[string]int
|
||||
mu sync.Mutex
|
||||
waitSub chan struct{}
|
||||
subCh chan<- []*api.HeadChange
|
||||
callNumber map[string]int
|
||||
}
|
||||
|
||||
func newFakeCS(t *testing.T) *fakeCS {
|
||||
@ -58,55 +58,82 @@ func newFakeCS(t *testing.T) *fakeCS {
|
||||
tipsets: make(map[types.TipSetKey]*types.TipSet),
|
||||
tsc: newTSCache(nil, 2*build.ForkLengthThreshold),
|
||||
callNumber: map[string]int{},
|
||||
waitSub: make(chan struct{}),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
return fcs
|
||||
}
|
||||
|
||||
func (fcs *fakeCS) ChainHead(ctx context.Context) (*types.TipSet, error) {
|
||||
fcs.callNumberLk.Lock()
|
||||
defer fcs.callNumberLk.Unlock()
|
||||
fcs.mu.Lock()
|
||||
defer fcs.mu.Unlock()
|
||||
fcs.callNumber["ChainHead"] = fcs.callNumber["ChainHead"] + 1
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (fcs *fakeCS) ChainGetPath(ctx context.Context, from, to types.TipSetKey) ([]*api.HeadChange, error) {
|
||||
fcs.callNumberLk.Lock()
|
||||
defer fcs.callNumberLk.Unlock()
|
||||
fcs.mu.Lock()
|
||||
fcs.callNumber["ChainGetPath"] = fcs.callNumber["ChainGetPath"] + 1
|
||||
panic("Not Implemented")
|
||||
fcs.mu.Unlock()
|
||||
|
||||
fromTs, err := fcs.ChainGetTipSet(ctx, from)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
toTs, err := fcs.ChainGetTipSet(ctx, to)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// copied from the chainstore
|
||||
revert, apply, err := store.ReorgOps(func(tsk types.TipSetKey) (*types.TipSet, error) {
|
||||
return fcs.ChainGetTipSet(ctx, tsk)
|
||||
}, fromTs, toTs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
path := make([]*api.HeadChange, len(revert)+len(apply))
|
||||
for i, r := range revert {
|
||||
path[i] = &api.HeadChange{Type: store.HCRevert, Val: r}
|
||||
}
|
||||
for j, i := 0, len(apply)-1; i >= 0; j, i = j+1, i-1 {
|
||||
path[j+len(revert)] = &api.HeadChange{Type: store.HCApply, Val: apply[i]}
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func (fcs *fakeCS) ChainGetTipSet(ctx context.Context, key types.TipSetKey) (*types.TipSet, error) {
|
||||
fcs.callNumberLk.Lock()
|
||||
defer fcs.callNumberLk.Unlock()
|
||||
fcs.mu.Lock()
|
||||
defer fcs.mu.Unlock()
|
||||
fcs.callNumber["ChainGetTipSet"] = fcs.callNumber["ChainGetTipSet"] + 1
|
||||
return fcs.tipsets[key], nil
|
||||
}
|
||||
|
||||
func (fcs *fakeCS) StateSearchMsg(ctx context.Context, from types.TipSetKey, msg cid.Cid, limit abi.ChainEpoch, allowReplaced bool) (*api.MsgLookup, error) {
|
||||
fcs.callNumberLk.Lock()
|
||||
defer fcs.callNumberLk.Unlock()
|
||||
fcs.mu.Lock()
|
||||
defer fcs.mu.Unlock()
|
||||
fcs.callNumber["StateSearchMsg"] = fcs.callNumber["StateSearchMsg"] + 1
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (fcs *fakeCS) StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) {
|
||||
fcs.callNumberLk.Lock()
|
||||
defer fcs.callNumberLk.Unlock()
|
||||
fcs.mu.Lock()
|
||||
defer fcs.mu.Unlock()
|
||||
fcs.callNumber["StateGetActor"] = fcs.callNumber["StateGetActor"] + 1
|
||||
panic("Not Implemented")
|
||||
}
|
||||
|
||||
func (fcs *fakeCS) ChainGetTipSetByHeight(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error) {
|
||||
fcs.callNumberLk.Lock()
|
||||
defer fcs.callNumberLk.Unlock()
|
||||
fcs.mu.Lock()
|
||||
defer fcs.mu.Unlock()
|
||||
fcs.callNumber["ChainGetTipSetByHeight"] = fcs.callNumber["ChainGetTipSetByHeight"] + 1
|
||||
panic("Not Implemented")
|
||||
}
|
||||
func (fcs *fakeCS) ChainGetTipSetAfterHeight(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error) {
|
||||
fcs.callNumberLk.Lock()
|
||||
defer fcs.callNumberLk.Unlock()
|
||||
fcs.mu.Lock()
|
||||
defer fcs.mu.Unlock()
|
||||
fcs.callNumber["ChainGetTipSetAfterHeight"] = fcs.callNumber["ChainGetTipSetAfterHeight"] + 1
|
||||
panic("Not Implemented")
|
||||
}
|
||||
@ -158,47 +185,32 @@ func (fcs *fakeCS) makeTs(t *testing.T, parents []cid.Cid, h abi.ChainEpoch, msg
|
||||
}
|
||||
|
||||
func (fcs *fakeCS) ChainNotify(ctx context.Context) (<-chan []*api.HeadChange, error) {
|
||||
fcs.callNumberLk.Lock()
|
||||
defer fcs.callNumberLk.Unlock()
|
||||
fcs.mu.Lock()
|
||||
defer fcs.mu.Unlock()
|
||||
fcs.callNumber["ChainNotify"] = fcs.callNumber["ChainNotify"] + 1
|
||||
|
||||
out := make(chan []*api.HeadChange, 1)
|
||||
if fcs.subCh != nil {
|
||||
close(out)
|
||||
fcs.t.Error("already subscribed to notifications")
|
||||
return out, nil
|
||||
}
|
||||
|
||||
best, err := fcs.tsc.ChainHead(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out <- []*api.HeadChange{{Type: store.HCCurrent, Val: best}}
|
||||
|
||||
fcs.sub = func(rev, app []*types.TipSet) {
|
||||
notif := make([]*api.HeadChange, len(rev)+len(app))
|
||||
|
||||
for i, r := range rev {
|
||||
notif[i] = &api.HeadChange{
|
||||
Type: store.HCRevert,
|
||||
Val: r,
|
||||
}
|
||||
}
|
||||
for i, r := range app {
|
||||
notif[i+len(rev)] = &api.HeadChange{
|
||||
Type: store.HCApply,
|
||||
Val: r,
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case out <- notif:
|
||||
case <-ctx.Done():
|
||||
// TODO: fail test?
|
||||
return
|
||||
}
|
||||
}
|
||||
fcs.subCh = out
|
||||
close(fcs.waitSub)
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (fcs *fakeCS) ChainGetBlockMessages(ctx context.Context, blk cid.Cid) (*api.BlockMessages, error) {
|
||||
fcs.callNumberLk.Lock()
|
||||
defer fcs.callNumberLk.Unlock()
|
||||
fcs.mu.Lock()
|
||||
defer fcs.mu.Unlock()
|
||||
fcs.callNumber["ChainGetBlockMessages"] = fcs.callNumber["ChainGetBlockMessages"] + 1
|
||||
messages, ok := fcs.blkMsgs[blk]
|
||||
if !ok {
|
||||
@ -235,11 +247,44 @@ func (fcs *fakeCS) fakeMsgs(m fakeMsg) cid.Cid {
|
||||
return c
|
||||
}
|
||||
|
||||
func (fcs *fakeCS) advance(rev, app int, msgs map[int]cid.Cid, nulls ...int) { // todo: allow msgs
|
||||
if fcs.sub == nil {
|
||||
func (fcs *fakeCS) dropSub() {
|
||||
fcs.mu.Lock()
|
||||
|
||||
if fcs.subCh == nil {
|
||||
fcs.mu.Unlock()
|
||||
fcs.t.Fatal("sub not be nil")
|
||||
}
|
||||
|
||||
waitCh := make(chan struct{})
|
||||
fcs.waitSub = waitCh
|
||||
close(fcs.subCh)
|
||||
fcs.subCh = nil
|
||||
fcs.mu.Unlock()
|
||||
|
||||
<-waitCh
|
||||
}
|
||||
|
||||
func (fcs *fakeCS) sub(rev, app []*types.TipSet) {
|
||||
<-fcs.waitSub
|
||||
notif := make([]*api.HeadChange, len(rev)+len(app))
|
||||
|
||||
for i, r := range rev {
|
||||
notif[i] = &api.HeadChange{
|
||||
Type: store.HCRevert,
|
||||
Val: r,
|
||||
}
|
||||
}
|
||||
for i, r := range app {
|
||||
notif[i+len(rev)] = &api.HeadChange{
|
||||
Type: store.HCApply,
|
||||
Val: r,
|
||||
}
|
||||
}
|
||||
|
||||
fcs.subCh <- notif
|
||||
}
|
||||
|
||||
func (fcs *fakeCS) advance(rev, app, drop int, msgs map[int]cid.Cid, nulls ...int) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
@ -255,9 +300,17 @@ func (fcs *fakeCS) advance(rev, app int, msgs map[int]cid.Cid, nulls ...int) { /
|
||||
require.NoError(fcs.t, err)
|
||||
|
||||
if _, ok := nullm[int(from.Height())]; !ok {
|
||||
revs = append(revs, from)
|
||||
|
||||
require.NoError(fcs.t, fcs.tsc.revert(from))
|
||||
|
||||
if drop == 0 {
|
||||
revs = append(revs, from)
|
||||
}
|
||||
}
|
||||
if drop > 0 {
|
||||
drop--
|
||||
if drop == 0 {
|
||||
fcs.dropSub()
|
||||
}
|
||||
}
|
||||
fcs.h--
|
||||
}
|
||||
@ -272,20 +325,27 @@ func (fcs *fakeCS) advance(rev, app int, msgs map[int]cid.Cid, nulls ...int) { /
|
||||
mc = dummyCid
|
||||
}
|
||||
|
||||
if _, ok := nullm[int(fcs.h)]; ok {
|
||||
continue
|
||||
if _, ok := nullm[int(fcs.h)]; !ok {
|
||||
best, err := fcs.tsc.ChainHead(ctx)
|
||||
require.NoError(fcs.t, err)
|
||||
ts := fcs.makeTs(fcs.t, best.Key().Cids(), fcs.h, mc)
|
||||
require.NoError(fcs.t, fcs.tsc.add(ts))
|
||||
|
||||
if hasMsgs {
|
||||
fcs.blkMsgs[ts.Blocks()[0].Cid()] = mc
|
||||
}
|
||||
|
||||
if drop == 0 {
|
||||
apps = append(apps, ts)
|
||||
}
|
||||
}
|
||||
|
||||
best, err := fcs.tsc.ChainHead(ctx)
|
||||
require.NoError(fcs.t, err)
|
||||
ts := fcs.makeTs(fcs.t, best.Key().Cids(), fcs.h, mc)
|
||||
require.NoError(fcs.t, fcs.tsc.add(ts))
|
||||
|
||||
if hasMsgs {
|
||||
fcs.blkMsgs[ts.Blocks()[0].Cid()] = mc
|
||||
if drop > 0 {
|
||||
drop--
|
||||
if drop == 0 {
|
||||
fcs.dropSub()
|
||||
}
|
||||
}
|
||||
|
||||
apps = append(apps, ts)
|
||||
}
|
||||
|
||||
fcs.sub(revs, apps)
|
||||
@ -316,88 +376,47 @@ func TestAt(t *testing.T) {
|
||||
}, 3, 5)
|
||||
require.NoError(t, err)
|
||||
|
||||
fcs.advance(0, 3, nil)
|
||||
fcs.advance(0, 3, 0, nil)
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
fcs.advance(0, 3, nil)
|
||||
fcs.advance(0, 3, 0, nil)
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
fcs.advance(0, 3, nil)
|
||||
fcs.advance(0, 3, 0, nil)
|
||||
require.Equal(t, true, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
applied = false
|
||||
|
||||
fcs.advance(0, 3, nil)
|
||||
fcs.advance(0, 3, 0, nil)
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
fcs.advance(10, 10, nil)
|
||||
fcs.advance(10, 10, 0, nil)
|
||||
require.Equal(t, true, applied)
|
||||
require.Equal(t, true, reverted)
|
||||
applied = false
|
||||
reverted = false
|
||||
|
||||
fcs.advance(10, 1, nil)
|
||||
fcs.advance(10, 1, 0, nil)
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, true, reverted)
|
||||
reverted = false
|
||||
|
||||
fcs.advance(0, 1, nil)
|
||||
fcs.advance(0, 1, 0, nil)
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
fcs.advance(0, 2, nil)
|
||||
fcs.advance(0, 2, 0, nil)
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
fcs.advance(0, 1, nil) // 8
|
||||
fcs.advance(0, 1, 0, nil) // 8
|
||||
require.Equal(t, true, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
}
|
||||
|
||||
func TestAtDoubleTrigger(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
fcs := newFakeCS(t)
|
||||
|
||||
events, err := NewEvents(ctx, fcs)
|
||||
require.NoError(t, err)
|
||||
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err = events.ChainAt(ctx, func(_ context.Context, ts *types.TipSet, curH abi.ChainEpoch) error {
|
||||
require.Equal(t, 5, int(ts.Height()))
|
||||
require.Equal(t, 8, int(curH))
|
||||
applied = true
|
||||
return nil
|
||||
}, func(_ context.Context, ts *types.TipSet) error {
|
||||
reverted = true
|
||||
return nil
|
||||
}, 3, 5)
|
||||
require.NoError(t, err)
|
||||
|
||||
fcs.advance(0, 6, nil)
|
||||
require.False(t, applied)
|
||||
require.False(t, reverted)
|
||||
|
||||
fcs.advance(0, 1, nil)
|
||||
require.True(t, applied)
|
||||
require.False(t, reverted)
|
||||
applied = false
|
||||
|
||||
fcs.advance(2, 2, nil)
|
||||
require.False(t, applied)
|
||||
require.False(t, reverted)
|
||||
|
||||
fcs.advance(4, 4, nil)
|
||||
require.True(t, applied)
|
||||
require.True(t, reverted)
|
||||
}
|
||||
|
||||
func TestAtNullTrigger(t *testing.T) {
|
||||
fcs := newFakeCS(t)
|
||||
events, err := NewEvents(context.Background(), fcs)
|
||||
@ -417,11 +436,11 @@ func TestAtNullTrigger(t *testing.T) {
|
||||
}, 3, 5)
|
||||
require.NoError(t, err)
|
||||
|
||||
fcs.advance(0, 6, nil, 5)
|
||||
fcs.advance(0, 6, 0, nil, 5)
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
fcs.advance(0, 3, nil)
|
||||
fcs.advance(0, 3, 0, nil)
|
||||
require.Equal(t, true, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
applied = false
|
||||
@ -450,16 +469,16 @@ func TestAtNullConf(t *testing.T) {
|
||||
}, 3, 5)
|
||||
require.NoError(t, err)
|
||||
|
||||
fcs.advance(0, 6, nil)
|
||||
fcs.advance(0, 6, 0, nil)
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
fcs.advance(0, 3, nil, 8)
|
||||
fcs.advance(0, 3, 0, nil, 8)
|
||||
require.Equal(t, true, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
applied = false
|
||||
|
||||
fcs.advance(7, 1, nil)
|
||||
fcs.advance(7, 1, 0, nil)
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, true, reverted)
|
||||
reverted = false
|
||||
@ -471,7 +490,7 @@ func TestAtStart(t *testing.T) {
|
||||
events, err := NewEvents(context.Background(), fcs)
|
||||
require.NoError(t, err)
|
||||
|
||||
fcs.advance(0, 5, nil) // 6
|
||||
fcs.advance(0, 5, 0, nil) // 6
|
||||
|
||||
var applied bool
|
||||
var reverted bool
|
||||
@ -490,7 +509,7 @@ func TestAtStart(t *testing.T) {
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
fcs.advance(0, 5, nil) // 11
|
||||
fcs.advance(0, 5, 0, nil) // 11
|
||||
require.Equal(t, true, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
}
|
||||
@ -501,7 +520,7 @@ func TestAtStartConfidence(t *testing.T) {
|
||||
events, err := NewEvents(context.Background(), fcs)
|
||||
require.NoError(t, err)
|
||||
|
||||
fcs.advance(0, 10, nil) // 11
|
||||
fcs.advance(0, 10, 0, nil) // 11
|
||||
|
||||
var applied bool
|
||||
var reverted bool
|
||||
@ -545,7 +564,7 @@ func TestAtChained(t *testing.T) {
|
||||
}, 3, 5)
|
||||
require.NoError(t, err)
|
||||
|
||||
fcs.advance(0, 15, nil)
|
||||
fcs.advance(0, 15, 0, nil)
|
||||
|
||||
require.Equal(t, true, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
@ -557,7 +576,7 @@ func TestAtChainedConfidence(t *testing.T) {
|
||||
events, err := NewEvents(context.Background(), fcs)
|
||||
require.NoError(t, err)
|
||||
|
||||
fcs.advance(0, 15, nil)
|
||||
fcs.advance(0, 15, 0, nil)
|
||||
|
||||
var applied bool
|
||||
var reverted bool
|
||||
@ -587,7 +606,7 @@ func TestAtChainedConfidenceNull(t *testing.T) {
|
||||
events, err := NewEvents(context.Background(), fcs)
|
||||
require.NoError(t, err)
|
||||
|
||||
fcs.advance(0, 15, nil, 5)
|
||||
fcs.advance(0, 15, 0, nil, 5)
|
||||
|
||||
var applied bool
|
||||
var reverted bool
|
||||
@ -644,13 +663,13 @@ func TestCalled(t *testing.T) {
|
||||
|
||||
// create few blocks to make sure nothing get's randomly called
|
||||
|
||||
fcs.advance(0, 4, nil) // H=5
|
||||
fcs.advance(0, 4, 0, nil) // H=5
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// create blocks with message (but below confidence threshold)
|
||||
|
||||
fcs.advance(0, 3, map[int]cid.Cid{ // msg at H=6; H=8 (confidence=2)
|
||||
fcs.advance(0, 3, 0, map[int]cid.Cid{ // msg at H=6; H=8 (confidence=2)
|
||||
0: fcs.fakeMsgs(fakeMsg{
|
||||
bmsgs: []*types.Message{
|
||||
{To: t0123, From: t0123, Method: 5, Nonce: 1},
|
||||
@ -663,14 +682,14 @@ func TestCalled(t *testing.T) {
|
||||
|
||||
// create additional block so we are above confidence threshold
|
||||
|
||||
fcs.advance(0, 2, nil) // H=10 (confidence=3, apply)
|
||||
fcs.advance(0, 2, 0, nil) // H=10 (confidence=3, apply)
|
||||
|
||||
require.Equal(t, true, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
applied = false
|
||||
|
||||
// dip below confidence
|
||||
fcs.advance(2, 2, nil) // H=10 (confidence=3, apply)
|
||||
fcs.advance(2, 2, 0, nil) // H=10 (confidence=3, apply)
|
||||
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
@ -684,13 +703,13 @@ func TestCalled(t *testing.T) {
|
||||
|
||||
// revert some blocks, keep the message
|
||||
|
||||
fcs.advance(3, 1, nil) // H=8 (confidence=1)
|
||||
fcs.advance(3, 1, 0, nil) // H=8 (confidence=1)
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// revert the message
|
||||
|
||||
fcs.advance(2, 1, nil) // H=7, we reverted ts with the msg execution, but not the msg itself
|
||||
fcs.advance(2, 1, 0, nil) // H=7, we reverted ts with the msg execution, but not the msg itself
|
||||
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, true, reverted)
|
||||
@ -704,7 +723,7 @@ func TestCalled(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
fcs.advance(0, 3, map[int]cid.Cid{ // (n2msg confidence=1)
|
||||
fcs.advance(0, 3, 0, map[int]cid.Cid{ // (n2msg confidence=1)
|
||||
0: n2msg,
|
||||
})
|
||||
|
||||
@ -713,7 +732,7 @@ func TestCalled(t *testing.T) {
|
||||
require.Equal(t, abi.ChainEpoch(10), appliedH)
|
||||
applied = false
|
||||
|
||||
fcs.advance(0, 2, nil) // (confidence=3)
|
||||
fcs.advance(0, 2, 0, nil) // (confidence=3)
|
||||
|
||||
require.Equal(t, true, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
@ -728,7 +747,7 @@ func TestCalled(t *testing.T) {
|
||||
|
||||
// revert and apply at different height
|
||||
|
||||
fcs.advance(8, 6, map[int]cid.Cid{ // (confidence=3)
|
||||
fcs.advance(8, 6, 0, map[int]cid.Cid{ // (confidence=3)
|
||||
1: n2msg,
|
||||
})
|
||||
|
||||
@ -749,7 +768,7 @@ func TestCalled(t *testing.T) {
|
||||
|
||||
// call method again
|
||||
|
||||
fcs.advance(0, 5, map[int]cid.Cid{
|
||||
fcs.advance(0, 5, 0, map[int]cid.Cid{
|
||||
0: n2msg,
|
||||
})
|
||||
|
||||
@ -758,7 +777,7 @@ func TestCalled(t *testing.T) {
|
||||
applied = false
|
||||
|
||||
// send and revert below confidence, then cross confidence
|
||||
fcs.advance(0, 2, map[int]cid.Cid{
|
||||
fcs.advance(0, 2, 0, map[int]cid.Cid{
|
||||
0: fcs.fakeMsgs(fakeMsg{
|
||||
bmsgs: []*types.Message{
|
||||
{To: t0123, From: t0123, Method: 5, Nonce: 3},
|
||||
@ -766,14 +785,14 @@ func TestCalled(t *testing.T) {
|
||||
}),
|
||||
})
|
||||
|
||||
fcs.advance(2, 5, nil) // H=19, but message reverted
|
||||
fcs.advance(2, 5, 0, nil) // H=19, but message reverted
|
||||
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// test timeout (it's set to 20 in the call to `events.Called` above)
|
||||
|
||||
fcs.advance(0, 6, nil)
|
||||
fcs.advance(0, 6, 0, nil)
|
||||
|
||||
require.Equal(t, false, applied) // not calling timeout as we received messages
|
||||
require.Equal(t, false, reverted)
|
||||
@ -781,7 +800,7 @@ func TestCalled(t *testing.T) {
|
||||
// test unregistering with more
|
||||
|
||||
more = false
|
||||
fcs.advance(0, 5, map[int]cid.Cid{
|
||||
fcs.advance(0, 5, 0, map[int]cid.Cid{
|
||||
0: fcs.fakeMsgs(fakeMsg{
|
||||
bmsgs: []*types.Message{
|
||||
{To: t0123, From: t0123, Method: 5, Nonce: 4}, // this signals we don't want more
|
||||
@ -793,7 +812,7 @@ func TestCalled(t *testing.T) {
|
||||
require.Equal(t, false, reverted)
|
||||
applied = false
|
||||
|
||||
fcs.advance(0, 5, map[int]cid.Cid{
|
||||
fcs.advance(0, 5, 0, map[int]cid.Cid{
|
||||
0: fcs.fakeMsgs(fakeMsg{
|
||||
bmsgs: []*types.Message{
|
||||
{To: t0123, From: t0123, Method: 5, Nonce: 5},
|
||||
@ -806,12 +825,12 @@ func TestCalled(t *testing.T) {
|
||||
|
||||
// revert after disabled
|
||||
|
||||
fcs.advance(5, 1, nil) // try reverting msg sent after disabling
|
||||
fcs.advance(5, 1, 0, nil) // try reverting msg sent after disabling
|
||||
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
fcs.advance(5, 1, nil) // try reverting msg sent before disabling
|
||||
fcs.advance(5, 1, 0, nil) // try reverting msg sent before disabling
|
||||
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, true, reverted)
|
||||
@ -842,10 +861,10 @@ func TestCalledTimeout(t *testing.T) {
|
||||
}, 3, 20, matchAddrMethod(t0123, 5))
|
||||
require.NoError(t, err)
|
||||
|
||||
fcs.advance(0, 21, nil)
|
||||
fcs.advance(0, 21, 0, nil)
|
||||
require.False(t, called)
|
||||
|
||||
fcs.advance(0, 5, nil)
|
||||
fcs.advance(0, 5, 0, nil)
|
||||
require.True(t, called)
|
||||
called = false
|
||||
|
||||
@ -856,9 +875,7 @@ func TestCalledTimeout(t *testing.T) {
|
||||
events, err = NewEvents(context.Background(), fcs)
|
||||
require.NoError(t, err)
|
||||
|
||||
// XXX: Needed to set the latest head so "check" succeeds". Is that OK? Or do we expect
|
||||
// check to work _before_ we've received any events.
|
||||
fcs.advance(0, 1, nil)
|
||||
fcs.advance(0, 1, 0, nil)
|
||||
|
||||
err = events.Called(context.Background(), func(ctx context.Context, ts *types.TipSet) (d bool, m bool, e error) {
|
||||
return true, true, nil
|
||||
@ -874,10 +891,10 @@ func TestCalledTimeout(t *testing.T) {
|
||||
}, 3, 20, matchAddrMethod(t0123, 5))
|
||||
require.NoError(t, err)
|
||||
|
||||
fcs.advance(0, 21, nil)
|
||||
fcs.advance(0, 21, 0, nil)
|
||||
require.False(t, called)
|
||||
|
||||
fcs.advance(0, 5, nil)
|
||||
fcs.advance(0, 5, 0, nil)
|
||||
require.False(t, called)
|
||||
}
|
||||
|
||||
@ -921,7 +938,7 @@ func TestCalledOrder(t *testing.T) {
|
||||
}, 3, 20, matchAddrMethod(t0123, 5))
|
||||
require.NoError(t, err)
|
||||
|
||||
fcs.advance(0, 10, map[int]cid.Cid{
|
||||
fcs.advance(0, 10, 0, map[int]cid.Cid{
|
||||
1: fcs.fakeMsgs(fakeMsg{
|
||||
bmsgs: []*types.Message{
|
||||
{To: t0123, From: t0123, Method: 5, Nonce: 1},
|
||||
@ -934,7 +951,7 @@ func TestCalledOrder(t *testing.T) {
|
||||
}),
|
||||
})
|
||||
|
||||
fcs.advance(9, 1, nil)
|
||||
fcs.advance(9, 1, 0, nil)
|
||||
}
|
||||
|
||||
func TestCalledNull(t *testing.T) {
|
||||
@ -963,13 +980,13 @@ func TestCalledNull(t *testing.T) {
|
||||
|
||||
// create few blocks to make sure nothing get's randomly called
|
||||
|
||||
fcs.advance(0, 4, nil) // H=5
|
||||
fcs.advance(0, 4, 0, nil) // H=5
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// create blocks with message (but below confidence threshold)
|
||||
|
||||
fcs.advance(0, 3, map[int]cid.Cid{ // msg at H=6; H=8 (confidence=2)
|
||||
fcs.advance(0, 3, 0, map[int]cid.Cid{ // msg at H=6; H=8 (confidence=2)
|
||||
0: fcs.fakeMsgs(fakeMsg{
|
||||
bmsgs: []*types.Message{
|
||||
{To: t0123, From: t0123, Method: 5, Nonce: 1},
|
||||
@ -983,13 +1000,13 @@ func TestCalledNull(t *testing.T) {
|
||||
// create additional blocks so we are above confidence threshold, but with null tipset at the height
|
||||
// of application
|
||||
|
||||
fcs.advance(0, 3, nil, 10) // H=11 (confidence=3, apply)
|
||||
fcs.advance(0, 3, 0, nil, 10) // H=11 (confidence=3, apply)
|
||||
|
||||
require.Equal(t, true, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
applied = false
|
||||
|
||||
fcs.advance(5, 1, nil, 10)
|
||||
fcs.advance(5, 1, 0, nil, 10)
|
||||
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, true, reverted)
|
||||
@ -1021,13 +1038,13 @@ func TestRemoveTriggersOnMessage(t *testing.T) {
|
||||
|
||||
// create few blocks to make sure nothing get's randomly called
|
||||
|
||||
fcs.advance(0, 4, nil) // H=5
|
||||
fcs.advance(0, 4, 0, nil) // H=5
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// create blocks with message (but below confidence threshold)
|
||||
|
||||
fcs.advance(0, 3, map[int]cid.Cid{ // msg occurs at H=5, applied at H=6; H=8 (confidence=2)
|
||||
fcs.advance(0, 3, 0, map[int]cid.Cid{ // msg occurs at H=5, applied at H=6; H=8 (confidence=2)
|
||||
0: fcs.fakeMsgs(fakeMsg{
|
||||
bmsgs: []*types.Message{
|
||||
{To: t0123, From: t0123, Method: 5, Nonce: 1},
|
||||
@ -1039,19 +1056,19 @@ func TestRemoveTriggersOnMessage(t *testing.T) {
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// revert applied TS & message TS
|
||||
fcs.advance(3, 1, nil) // H=6 (tipset message applied in reverted, AND message reverted)
|
||||
fcs.advance(3, 1, 0, nil) // H=6 (tipset message applied in reverted, AND message reverted)
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// create additional blocks so we are above confidence threshold, but message not applied
|
||||
// as it was reverted
|
||||
fcs.advance(0, 5, nil) // H=11 (confidence=3, apply)
|
||||
fcs.advance(0, 5, 0, nil) // H=11 (confidence=3, apply)
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// create blocks with message again (but below confidence threshold)
|
||||
|
||||
fcs.advance(0, 3, map[int]cid.Cid{ // msg occurs at H=12, applied at H=13; H=15 (confidence=2)
|
||||
fcs.advance(0, 3, 0, map[int]cid.Cid{ // msg occurs at H=12, applied at H=13; H=15 (confidence=2)
|
||||
0: fcs.fakeMsgs(fakeMsg{
|
||||
bmsgs: []*types.Message{
|
||||
{To: t0123, From: t0123, Method: 5, Nonce: 2},
|
||||
@ -1062,12 +1079,12 @@ func TestRemoveTriggersOnMessage(t *testing.T) {
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// revert applied height TS, but don't remove message trigger
|
||||
fcs.advance(2, 1, nil) // H=13 (tipset message applied in reverted, by tipset with message not reverted)
|
||||
fcs.advance(2, 1, 0, nil) // H=13 (tipset message applied in reverted, by tipset with message not reverted)
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// create additional blocks so we are above confidence threshold
|
||||
fcs.advance(0, 4, nil) // H=18 (confidence=3, apply)
|
||||
fcs.advance(0, 4, 0, nil) // H=18 (confidence=3, apply)
|
||||
|
||||
require.Equal(t, true, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
@ -1098,6 +1115,9 @@ func TestStateChanged(t *testing.T) {
|
||||
err = events.StateChanged(func(ctx context.Context, ts *types.TipSet) (d bool, m bool, e error) {
|
||||
return false, true, nil
|
||||
}, func(oldTs, newTs *types.TipSet, data StateChange, curH abi.ChainEpoch) (bool, error) {
|
||||
if data != nil {
|
||||
require.Equal(t, oldTs.Key(), newTs.Parents())
|
||||
}
|
||||
require.Equal(t, false, applied)
|
||||
applied = true
|
||||
appliedData = data
|
||||
@ -1109,6 +1129,7 @@ func TestStateChanged(t *testing.T) {
|
||||
reverted = true
|
||||
return nil
|
||||
}, confidence, timeout, func(oldTs, newTs *types.TipSet) (bool, StateChange, error) {
|
||||
require.Equal(t, oldTs.Key(), newTs.Parents())
|
||||
if matchData == nil {
|
||||
return false, matchData, nil
|
||||
}
|
||||
@ -1121,27 +1142,27 @@ func TestStateChanged(t *testing.T) {
|
||||
|
||||
// create few blocks to make sure nothing get's randomly called
|
||||
|
||||
fcs.advance(0, 4, nil) // H=5
|
||||
fcs.advance(0, 4, 0, nil) // H=5
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// create state change (but below confidence threshold)
|
||||
matchData = testStateChange{from: "a", to: "b"}
|
||||
fcs.advance(0, 3, nil)
|
||||
fcs.advance(0, 3, 0, nil)
|
||||
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// create additional block so we are above confidence threshold
|
||||
|
||||
fcs.advance(0, 2, nil) // H=10 (confidence=3, apply)
|
||||
fcs.advance(0, 2, 0, nil) // H=10 (confidence=3, apply)
|
||||
|
||||
require.Equal(t, true, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
applied = false
|
||||
|
||||
// dip below confidence (should not apply again)
|
||||
fcs.advance(2, 2, nil) // H=10 (confidence=3, apply)
|
||||
fcs.advance(2, 2, 0, nil) // H=10 (confidence=3, apply)
|
||||
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
@ -1175,6 +1196,9 @@ func TestStateChangedRevert(t *testing.T) {
|
||||
err = events.StateChanged(func(ctx context.Context, ts *types.TipSet) (d bool, m bool, e error) {
|
||||
return false, true, nil
|
||||
}, func(oldTs, newTs *types.TipSet, data StateChange, curH abi.ChainEpoch) (bool, error) {
|
||||
if data != nil {
|
||||
require.Equal(t, oldTs.Key(), newTs.Parents())
|
||||
}
|
||||
require.Equal(t, false, applied)
|
||||
applied = true
|
||||
return more, nil
|
||||
@ -1182,6 +1206,8 @@ func TestStateChangedRevert(t *testing.T) {
|
||||
reverted = true
|
||||
return nil
|
||||
}, confidence, timeout, func(oldTs, newTs *types.TipSet) (bool, StateChange, error) {
|
||||
require.Equal(t, oldTs.Key(), newTs.Parents())
|
||||
|
||||
if matchData == nil {
|
||||
return false, matchData, nil
|
||||
}
|
||||
@ -1192,18 +1218,18 @@ func TestStateChangedRevert(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
fcs.advance(0, 2, nil) // H=3
|
||||
fcs.advance(0, 2, 0, nil) // H=3
|
||||
|
||||
// Make a state change from TS at height 3 to TS at height 4
|
||||
matchData = testStateChange{from: "a", to: "b"}
|
||||
fcs.advance(0, 1, nil) // H=4
|
||||
fcs.advance(0, 1, 0, nil) // H=4
|
||||
|
||||
// Haven't yet reached confidence
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// Advance to reach confidence level
|
||||
fcs.advance(0, 1, nil) // H=5
|
||||
fcs.advance(0, 1, 0, nil) // H=5
|
||||
|
||||
// Should now have called the handler
|
||||
require.Equal(t, true, applied)
|
||||
@ -1211,19 +1237,19 @@ func TestStateChangedRevert(t *testing.T) {
|
||||
applied = false
|
||||
|
||||
// Advance 3 more TS
|
||||
fcs.advance(0, 3, nil) // H=8
|
||||
fcs.advance(0, 3, 0, nil) // H=8
|
||||
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// Regress but not so far as to cause a revert
|
||||
fcs.advance(3, 1, nil) // H=6
|
||||
fcs.advance(3, 1, 0, nil) // H=6
|
||||
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// Regress back to state where change happened
|
||||
fcs.advance(3, 1, nil) // H=4
|
||||
fcs.advance(3, 1, 0, nil) // H=4
|
||||
|
||||
// Expect revert to have happened
|
||||
require.Equal(t, false, applied)
|
||||
@ -1241,6 +1267,9 @@ func TestStateChangedTimeout(t *testing.T) {
|
||||
err = events.StateChanged(func(ctx context.Context, ts *types.TipSet) (d bool, m bool, e error) {
|
||||
return false, true, nil
|
||||
}, func(oldTs, newTs *types.TipSet, data StateChange, curH abi.ChainEpoch) (bool, error) {
|
||||
if data != nil {
|
||||
require.Equal(t, oldTs.Key(), newTs.Parents())
|
||||
}
|
||||
called = true
|
||||
require.Nil(t, data)
|
||||
require.Equal(t, abi.ChainEpoch(20), newTs.Height())
|
||||
@ -1250,15 +1279,16 @@ func TestStateChangedTimeout(t *testing.T) {
|
||||
t.Fatal("revert on timeout")
|
||||
return nil
|
||||
}, 3, 20, func(oldTs, newTs *types.TipSet) (bool, StateChange, error) {
|
||||
require.Equal(t, oldTs.Key(), newTs.Parents())
|
||||
return false, nil, nil
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
fcs.advance(0, 21, nil)
|
||||
fcs.advance(0, 21, 0, nil)
|
||||
require.False(t, called)
|
||||
|
||||
fcs.advance(0, 5, nil)
|
||||
fcs.advance(0, 5, 0, nil)
|
||||
require.True(t, called)
|
||||
called = false
|
||||
|
||||
@ -1268,13 +1298,14 @@ func TestStateChangedTimeout(t *testing.T) {
|
||||
events, err = NewEvents(context.Background(), fcs)
|
||||
require.NoError(t, err)
|
||||
|
||||
// XXX: Needed to set the latest head so "check" succeeds". Is that OK? Or do we expect
|
||||
// check to work _before_ we've received any events.
|
||||
fcs.advance(0, 1, nil)
|
||||
fcs.advance(0, 1, 0, nil)
|
||||
|
||||
err = events.StateChanged(func(ctx context.Context, ts *types.TipSet) (d bool, m bool, e error) {
|
||||
return true, true, nil
|
||||
}, func(oldTs, newTs *types.TipSet, data StateChange, curH abi.ChainEpoch) (bool, error) {
|
||||
if data != nil {
|
||||
require.Equal(t, oldTs.Key(), newTs.Parents())
|
||||
}
|
||||
called = true
|
||||
require.Nil(t, data)
|
||||
require.Equal(t, abi.ChainEpoch(20), newTs.Height())
|
||||
@ -1284,14 +1315,15 @@ func TestStateChangedTimeout(t *testing.T) {
|
||||
t.Fatal("revert on timeout")
|
||||
return nil
|
||||
}, 3, 20, func(oldTs, newTs *types.TipSet) (bool, StateChange, error) {
|
||||
require.Equal(t, oldTs.Key(), newTs.Parents())
|
||||
return false, nil, nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
fcs.advance(0, 21, nil)
|
||||
fcs.advance(0, 21, 0, nil)
|
||||
require.False(t, called)
|
||||
|
||||
fcs.advance(0, 5, nil)
|
||||
fcs.advance(0, 5, 0, nil)
|
||||
require.False(t, called)
|
||||
}
|
||||
|
||||
@ -1335,7 +1367,7 @@ func TestCalledMultiplePerEpoch(t *testing.T) {
|
||||
}, 3, 20, matchAddrMethod(t0123, 5))
|
||||
require.NoError(t, err)
|
||||
|
||||
fcs.advance(0, 10, map[int]cid.Cid{
|
||||
fcs.advance(0, 10, 0, map[int]cid.Cid{
|
||||
1: fcs.fakeMsgs(fakeMsg{
|
||||
bmsgs: []*types.Message{
|
||||
{To: t0123, From: t0123, Method: 5, Nonce: 1},
|
||||
@ -1344,7 +1376,7 @@ func TestCalledMultiplePerEpoch(t *testing.T) {
|
||||
}),
|
||||
})
|
||||
|
||||
fcs.advance(9, 1, nil)
|
||||
fcs.advance(9, 1, 0, nil)
|
||||
}
|
||||
|
||||
func TestCachedSameBlock(t *testing.T) {
|
||||
@ -1353,9 +1385,63 @@ func TestCachedSameBlock(t *testing.T) {
|
||||
_, err := NewEvents(context.Background(), fcs)
|
||||
require.NoError(t, err)
|
||||
|
||||
fcs.advance(0, 10, map[int]cid.Cid{})
|
||||
fcs.advance(0, 10, 0, map[int]cid.Cid{})
|
||||
assert.Assert(t, fcs.callNumber["ChainGetBlockMessages"] == 20, "expect call ChainGetBlockMessages %d but got ", 20, fcs.callNumber["ChainGetBlockMessages"])
|
||||
|
||||
fcs.advance(5, 10, map[int]cid.Cid{})
|
||||
fcs.advance(5, 10, 0, map[int]cid.Cid{})
|
||||
assert.Assert(t, fcs.callNumber["ChainGetBlockMessages"] == 30, "expect call ChainGetBlockMessages %d but got ", 30, fcs.callNumber["ChainGetBlockMessages"])
|
||||
}
|
||||
|
||||
type testObserver struct {
|
||||
t *testing.T
|
||||
head *types.TipSet
|
||||
}
|
||||
|
||||
func (t *testObserver) Apply(_ context.Context, from, to *types.TipSet) error {
|
||||
if t.head != nil {
|
||||
require.True(t.t, t.head.Equals(from))
|
||||
}
|
||||
t.head = to
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *testObserver) Revert(_ context.Context, from, to *types.TipSet) error {
|
||||
if t.head != nil {
|
||||
require.True(t.t, t.head.Equals(from))
|
||||
}
|
||||
t.head = to
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestReconnect(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
fcs := newFakeCS(t)
|
||||
|
||||
events, err := NewEvents(ctx, fcs)
|
||||
require.NoError(t, err)
|
||||
|
||||
fcs.advance(0, 1, 0, nil)
|
||||
|
||||
events.Observe(&testObserver{t: t})
|
||||
|
||||
fcs.advance(0, 3, 0, nil)
|
||||
|
||||
// Drop on apply
|
||||
fcs.advance(0, 6, 2, nil)
|
||||
require.True(t, fcs.callNumber["ChainGetPath"] == 1)
|
||||
|
||||
// drop across revert/apply boundary
|
||||
fcs.advance(4, 2, 3, nil)
|
||||
require.True(t, fcs.callNumber["ChainGetPath"] == 2)
|
||||
fcs.advance(0, 6, 0, nil)
|
||||
|
||||
// drop on revert
|
||||
fcs.advance(3, 0, 2, nil)
|
||||
require.True(t, fcs.callNumber["ChainGetPath"] == 3)
|
||||
|
||||
// drop with nulls
|
||||
fcs.advance(0, 5, 2, nil, 0, 1, 3)
|
||||
require.True(t, fcs.callNumber["ChainGetPath"] == 4)
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ func (o *observer) listenHeadChangesOnce(ctx context.Context) error {
|
||||
}
|
||||
|
||||
if err := o.applyChanges(ctx, changes); err != nil {
|
||||
return xerrors.Errorf("failed to apply head changes: %w", err)
|
||||
return xerrors.Errorf("failed catch-up head changes: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user