More post-review changes, lots of tests for SubscribeActorEvents
Use BlockDelay as the window for receiving events on the SubscribeActorEvents channel. We expect the user to have received the initial batch of historical events (if any) in one block's time. For real-time events we expect them to not fall behind by roughly one block's time.
This commit is contained in:
parent
5633861ce6
commit
318695a44c
@ -27,7 +27,16 @@ func isIndexedValue(b uint8) bool {
|
|||||||
return b&(types.EventFlagIndexedKey|types.EventFlagIndexedValue) > 0
|
return b&(types.EventFlagIndexedKey|types.EventFlagIndexedValue) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
type EventFilter struct {
|
type AddressResolver func(context.Context, abi.ActorID, *types.TipSet) (address.Address, bool)
|
||||||
|
|
||||||
|
type EventFilter interface {
|
||||||
|
Filter
|
||||||
|
|
||||||
|
TakeCollectedEvents(context.Context) []*CollectedEvent
|
||||||
|
CollectEvents(context.Context, *TipSetEvents, bool, AddressResolver) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type eventFilter struct {
|
||||||
id types.FilterID
|
id types.FilterID
|
||||||
minHeight abi.ChainEpoch // minimum epoch to apply filter or -1 if no minimum
|
minHeight abi.ChainEpoch // minimum epoch to apply filter or -1 if no minimum
|
||||||
maxHeight abi.ChainEpoch // maximum epoch to apply filter or -1 if no maximum
|
maxHeight abi.ChainEpoch // maximum epoch to apply filter or -1 if no maximum
|
||||||
@ -43,7 +52,7 @@ type EventFilter struct {
|
|||||||
ch chan<- interface{}
|
ch chan<- interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Filter = (*EventFilter)(nil)
|
var _ Filter = (*eventFilter)(nil)
|
||||||
|
|
||||||
type CollectedEvent struct {
|
type CollectedEvent struct {
|
||||||
Entries []types.EventEntry
|
Entries []types.EventEntry
|
||||||
@ -56,24 +65,24 @@ type CollectedEvent struct {
|
|||||||
MsgCid cid.Cid // cid of message that produced event
|
MsgCid cid.Cid // cid of message that produced event
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *EventFilter) ID() types.FilterID {
|
func (f *eventFilter) ID() types.FilterID {
|
||||||
return f.id
|
return f.id
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *EventFilter) SetSubChannel(ch chan<- interface{}) {
|
func (f *eventFilter) SetSubChannel(ch chan<- interface{}) {
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
defer f.mu.Unlock()
|
defer f.mu.Unlock()
|
||||||
f.ch = ch
|
f.ch = ch
|
||||||
f.collected = nil
|
f.collected = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *EventFilter) ClearSubChannel() {
|
func (f *eventFilter) ClearSubChannel() {
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
defer f.mu.Unlock()
|
defer f.mu.Unlock()
|
||||||
f.ch = nil
|
f.ch = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *EventFilter) CollectEvents(ctx context.Context, te *TipSetEvents, revert bool, resolver func(ctx context.Context, emitter abi.ActorID, ts *types.TipSet) (address.Address, bool)) error {
|
func (f *eventFilter) CollectEvents(ctx context.Context, te *TipSetEvents, revert bool, resolver AddressResolver) error {
|
||||||
if !f.matchTipset(te) {
|
if !f.matchTipset(te) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -138,13 +147,13 @@ func (f *EventFilter) CollectEvents(ctx context.Context, te *TipSetEvents, rever
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *EventFilter) setCollectedEvents(ces []*CollectedEvent) {
|
func (f *eventFilter) setCollectedEvents(ces []*CollectedEvent) {
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
f.collected = ces
|
f.collected = ces
|
||||||
f.mu.Unlock()
|
f.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *EventFilter) TakeCollectedEvents(ctx context.Context) []*CollectedEvent {
|
func (f *eventFilter) TakeCollectedEvents(ctx context.Context) []*CollectedEvent {
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
collected := f.collected
|
collected := f.collected
|
||||||
f.collected = nil
|
f.collected = nil
|
||||||
@ -154,14 +163,14 @@ func (f *EventFilter) TakeCollectedEvents(ctx context.Context) []*CollectedEvent
|
|||||||
return collected
|
return collected
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *EventFilter) LastTaken() time.Time {
|
func (f *eventFilter) LastTaken() time.Time {
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
defer f.mu.Unlock()
|
defer f.mu.Unlock()
|
||||||
return f.lastTaken
|
return f.lastTaken
|
||||||
}
|
}
|
||||||
|
|
||||||
// matchTipset reports whether this filter matches the given tipset
|
// matchTipset reports whether this filter matches the given tipset
|
||||||
func (f *EventFilter) matchTipset(te *TipSetEvents) bool {
|
func (f *eventFilter) matchTipset(te *TipSetEvents) bool {
|
||||||
if f.tipsetCid != cid.Undef {
|
if f.tipsetCid != cid.Undef {
|
||||||
tsCid, err := te.Cid()
|
tsCid, err := te.Cid()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -179,7 +188,7 @@ func (f *EventFilter) matchTipset(te *TipSetEvents) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *EventFilter) matchAddress(o address.Address) bool {
|
func (f *eventFilter) matchAddress(o address.Address) bool {
|
||||||
if len(f.addresses) == 0 {
|
if len(f.addresses) == 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -194,7 +203,7 @@ func (f *EventFilter) matchAddress(o address.Address) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *EventFilter) matchKeys(ees []types.EventEntry) bool {
|
func (f *eventFilter) matchKeys(ees []types.EventEntry) bool {
|
||||||
if len(f.keysWithCodec) == 0 {
|
if len(f.keysWithCodec) == 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -297,7 +306,7 @@ type EventFilterManager struct {
|
|||||||
EventIndex *EventIndex
|
EventIndex *EventIndex
|
||||||
|
|
||||||
mu sync.Mutex // guards mutations to filters
|
mu sync.Mutex // guards mutations to filters
|
||||||
filters map[types.FilterID]*EventFilter
|
filters map[types.FilterID]EventFilter
|
||||||
currentHeight abi.ChainEpoch
|
currentHeight abi.ChainEpoch
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,7 +373,7 @@ func (m *EventFilterManager) Revert(ctx context.Context, from, to *types.TipSet)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *EventFilterManager) Install(ctx context.Context, minHeight, maxHeight abi.ChainEpoch, tipsetCid cid.Cid, addresses []address.Address,
|
func (m *EventFilterManager) Install(ctx context.Context, minHeight, maxHeight abi.ChainEpoch, tipsetCid cid.Cid, addresses []address.Address,
|
||||||
keysWithCodec map[string][]types.ActorEventBlock, excludeReverted bool) (*EventFilter, error) {
|
keysWithCodec map[string][]types.ActorEventBlock, excludeReverted bool) (EventFilter, error) {
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
currentHeight := m.currentHeight
|
currentHeight := m.currentHeight
|
||||||
m.mu.Unlock()
|
m.mu.Unlock()
|
||||||
@ -378,7 +387,7 @@ func (m *EventFilterManager) Install(ctx context.Context, minHeight, maxHeight a
|
|||||||
return nil, xerrors.Errorf("new filter id: %w", err)
|
return nil, xerrors.Errorf("new filter id: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
f := &EventFilter{
|
f := &eventFilter{
|
||||||
id: id,
|
id: id,
|
||||||
minHeight: minHeight,
|
minHeight: minHeight,
|
||||||
maxHeight: maxHeight,
|
maxHeight: maxHeight,
|
||||||
@ -390,14 +399,14 @@ func (m *EventFilterManager) Install(ctx context.Context, minHeight, maxHeight a
|
|||||||
|
|
||||||
if m.EventIndex != nil && minHeight != -1 && minHeight < currentHeight {
|
if m.EventIndex != nil && minHeight != -1 && minHeight < currentHeight {
|
||||||
// Filter needs historic events
|
// Filter needs historic events
|
||||||
if err := m.EventIndex.PrefillFilter(ctx, f, excludeReverted); err != nil {
|
if err := m.EventIndex.prefillFilter(ctx, f, excludeReverted); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
if m.filters == nil {
|
if m.filters == nil {
|
||||||
m.filters = make(map[types.FilterID]*EventFilter)
|
m.filters = make(map[types.FilterID]EventFilter)
|
||||||
}
|
}
|
||||||
m.filters[id] = f
|
m.filters[id] = f
|
||||||
m.mu.Unlock()
|
m.mu.Unlock()
|
||||||
|
@ -86,13 +86,13 @@ func TestEventFilterCollectEvents(t *testing.T) {
|
|||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
filter *EventFilter
|
filter *eventFilter
|
||||||
te *TipSetEvents
|
te *TipSetEvents
|
||||||
want []*CollectedEvent
|
want []*CollectedEvent
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "nomatch tipset min height",
|
name: "nomatch tipset min height",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: 14001,
|
minHeight: 14001,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
},
|
},
|
||||||
@ -101,7 +101,7 @@ func TestEventFilterCollectEvents(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch tipset max height",
|
name: "nomatch tipset max height",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: 13999,
|
maxHeight: 13999,
|
||||||
},
|
},
|
||||||
@ -110,7 +110,7 @@ func TestEventFilterCollectEvents(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match tipset min height",
|
name: "match tipset min height",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: 14000,
|
minHeight: 14000,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
},
|
},
|
||||||
@ -119,7 +119,7 @@ func TestEventFilterCollectEvents(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match tipset cid",
|
name: "match tipset cid",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
tipsetCid: cid14000,
|
tipsetCid: cid14000,
|
||||||
@ -129,7 +129,7 @@ func TestEventFilterCollectEvents(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch address",
|
name: "nomatch address",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
addresses: []address.Address{a2},
|
addresses: []address.Address{a2},
|
||||||
@ -139,7 +139,7 @@ func TestEventFilterCollectEvents(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match address",
|
name: "match address",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
addresses: []address.Address{a1},
|
addresses: []address.Address{a1},
|
||||||
@ -149,7 +149,7 @@ func TestEventFilterCollectEvents(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match one entry",
|
name: "match one entry",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -163,7 +163,7 @@ func TestEventFilterCollectEvents(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match one entry with alternate values",
|
name: "match one entry with alternate values",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -179,7 +179,7 @@ func TestEventFilterCollectEvents(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch one entry by missing value",
|
name: "nomatch one entry by missing value",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -194,7 +194,7 @@ func TestEventFilterCollectEvents(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch one entry by missing key",
|
name: "nomatch one entry by missing key",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -208,7 +208,7 @@ func TestEventFilterCollectEvents(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match one entry with multiple keys",
|
name: "match one entry with multiple keys",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -225,7 +225,7 @@ func TestEventFilterCollectEvents(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch one entry with one mismatching key",
|
name: "nomatch one entry with one mismatching key",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -242,7 +242,7 @@ func TestEventFilterCollectEvents(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch one entry with one mismatching value",
|
name: "nomatch one entry with one mismatching value",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -259,7 +259,7 @@ func TestEventFilterCollectEvents(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch one entry with one unindexed key",
|
name: "nomatch one entry with one unindexed key",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
|
@ -481,7 +481,7 @@ func (ei *EventIndex) CollectEvents(ctx context.Context, te *TipSetEvents, rever
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PrefillFilter fills a filter's collection of events from the historic index
|
// PrefillFilter fills a filter's collection of events from the historic index
|
||||||
func (ei *EventIndex) PrefillFilter(ctx context.Context, f *EventFilter, excludeReverted bool) error {
|
func (ei *EventIndex) prefillFilter(ctx context.Context, f *eventFilter, excludeReverted bool) error {
|
||||||
clauses := []string{}
|
clauses := []string{}
|
||||||
values := []any{}
|
values := []any{}
|
||||||
joins := []string{}
|
joins := []string{}
|
||||||
|
@ -82,13 +82,13 @@ func TestEventIndexPrefillFilter(t *testing.T) {
|
|||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
filter *EventFilter
|
filter *eventFilter
|
||||||
te *TipSetEvents
|
te *TipSetEvents
|
||||||
want []*CollectedEvent
|
want []*CollectedEvent
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "nomatch tipset min height",
|
name: "nomatch tipset min height",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: 14001,
|
minHeight: 14001,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
},
|
},
|
||||||
@ -97,7 +97,7 @@ func TestEventIndexPrefillFilter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch tipset max height",
|
name: "nomatch tipset max height",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: 13999,
|
maxHeight: 13999,
|
||||||
},
|
},
|
||||||
@ -106,7 +106,7 @@ func TestEventIndexPrefillFilter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match tipset min height",
|
name: "match tipset min height",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: 14000,
|
minHeight: 14000,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
},
|
},
|
||||||
@ -115,7 +115,7 @@ func TestEventIndexPrefillFilter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match tipset cid",
|
name: "match tipset cid",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
tipsetCid: cid14000,
|
tipsetCid: cid14000,
|
||||||
@ -125,7 +125,7 @@ func TestEventIndexPrefillFilter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch address",
|
name: "nomatch address",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
addresses: []address.Address{a2},
|
addresses: []address.Address{a2},
|
||||||
@ -135,7 +135,7 @@ func TestEventIndexPrefillFilter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match address",
|
name: "match address",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
addresses: []address.Address{a1},
|
addresses: []address.Address{a1},
|
||||||
@ -145,7 +145,7 @@ func TestEventIndexPrefillFilter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match one entry",
|
name: "match one entry",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -159,7 +159,7 @@ func TestEventIndexPrefillFilter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match one entry with alternate values",
|
name: "match one entry with alternate values",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -175,7 +175,7 @@ func TestEventIndexPrefillFilter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch one entry by missing value",
|
name: "nomatch one entry by missing value",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -190,7 +190,7 @@ func TestEventIndexPrefillFilter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch one entry by missing key",
|
name: "nomatch one entry by missing key",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -204,7 +204,7 @@ func TestEventIndexPrefillFilter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match one entry with multiple keys",
|
name: "match one entry with multiple keys",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -221,7 +221,7 @@ func TestEventIndexPrefillFilter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch one entry with one mismatching key",
|
name: "nomatch one entry with one mismatching key",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -238,7 +238,7 @@ func TestEventIndexPrefillFilter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch one entry with one mismatching value",
|
name: "nomatch one entry with one mismatching value",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -255,7 +255,7 @@ func TestEventIndexPrefillFilter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch one entry with one unindexed key",
|
name: "nomatch one entry with one unindexed key",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -272,7 +272,7 @@ func TestEventIndexPrefillFilter(t *testing.T) {
|
|||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
tc := tc // appease lint
|
tc := tc // appease lint
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
if err := ei.PrefillFilter(context.Background(), tc.filter, false); err != nil {
|
if err := ei.prefillFilter(context.Background(), tc.filter, false); err != nil {
|
||||||
require.NoError(t, err, "prefill filter events")
|
require.NoError(t, err, "prefill filter events")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -409,13 +409,13 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
|
|
||||||
inclusiveTestCases := []struct {
|
inclusiveTestCases := []struct {
|
||||||
name string
|
name string
|
||||||
filter *EventFilter
|
filter *eventFilter
|
||||||
te *TipSetEvents
|
te *TipSetEvents
|
||||||
want []*CollectedEvent
|
want []*CollectedEvent
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "nomatch tipset min height",
|
name: "nomatch tipset min height",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: 14001,
|
minHeight: 14001,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
},
|
},
|
||||||
@ -424,7 +424,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch tipset max height",
|
name: "nomatch tipset max height",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: 13999,
|
maxHeight: 13999,
|
||||||
},
|
},
|
||||||
@ -433,7 +433,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match tipset min height",
|
name: "match tipset min height",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: 14000,
|
minHeight: 14000,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
},
|
},
|
||||||
@ -442,7 +442,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match tipset cid",
|
name: "match tipset cid",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
tipsetCid: cid14000,
|
tipsetCid: cid14000,
|
||||||
@ -452,7 +452,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match tipset cid",
|
name: "match tipset cid",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
tipsetCid: reveredCID14000,
|
tipsetCid: reveredCID14000,
|
||||||
@ -462,7 +462,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch address",
|
name: "nomatch address",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
addresses: []address.Address{a3},
|
addresses: []address.Address{a3},
|
||||||
@ -472,7 +472,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match address 2",
|
name: "match address 2",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
addresses: []address.Address{a2},
|
addresses: []address.Address{a2},
|
||||||
@ -482,7 +482,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match address 1",
|
name: "match address 1",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
addresses: []address.Address{a1},
|
addresses: []address.Address{a1},
|
||||||
@ -492,7 +492,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match one entry",
|
name: "match one entry",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -506,7 +506,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match one entry with alternate values",
|
name: "match one entry with alternate values",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -522,7 +522,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch one entry by missing value",
|
name: "nomatch one entry by missing value",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -537,7 +537,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch one entry by missing key",
|
name: "nomatch one entry by missing key",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -551,7 +551,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match one entry with multiple keys",
|
name: "match one entry with multiple keys",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -568,7 +568,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match one entry with multiple keys",
|
name: "match one entry with multiple keys",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -585,7 +585,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch one entry with one mismatching key",
|
name: "nomatch one entry with one mismatching key",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -602,7 +602,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch one entry with one mismatching value",
|
name: "nomatch one entry with one mismatching value",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -619,7 +619,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch one entry with one unindexed key",
|
name: "nomatch one entry with one unindexed key",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -633,7 +633,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch one entry with one unindexed key",
|
name: "nomatch one entry with one unindexed key",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -649,13 +649,13 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
|
|
||||||
exclusiveTestCases := []struct {
|
exclusiveTestCases := []struct {
|
||||||
name string
|
name string
|
||||||
filter *EventFilter
|
filter *eventFilter
|
||||||
te *TipSetEvents
|
te *TipSetEvents
|
||||||
want []*CollectedEvent
|
want []*CollectedEvent
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "nomatch tipset min height",
|
name: "nomatch tipset min height",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: 14001,
|
minHeight: 14001,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
},
|
},
|
||||||
@ -664,7 +664,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch tipset max height",
|
name: "nomatch tipset max height",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: 13999,
|
maxHeight: 13999,
|
||||||
},
|
},
|
||||||
@ -673,7 +673,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match tipset min height",
|
name: "match tipset min height",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: 14000,
|
minHeight: 14000,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
},
|
},
|
||||||
@ -682,7 +682,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match tipset cid",
|
name: "match tipset cid",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
tipsetCid: cid14000,
|
tipsetCid: cid14000,
|
||||||
@ -692,7 +692,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match tipset cid but reverted",
|
name: "match tipset cid but reverted",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
tipsetCid: reveredCID14000,
|
tipsetCid: reveredCID14000,
|
||||||
@ -702,7 +702,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch address",
|
name: "nomatch address",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
addresses: []address.Address{a3},
|
addresses: []address.Address{a3},
|
||||||
@ -712,7 +712,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch address 2 but reverted",
|
name: "nomatch address 2 but reverted",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
addresses: []address.Address{a2},
|
addresses: []address.Address{a2},
|
||||||
@ -722,7 +722,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match address",
|
name: "match address",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
addresses: []address.Address{a1},
|
addresses: []address.Address{a1},
|
||||||
@ -732,7 +732,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match one entry",
|
name: "match one entry",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -746,7 +746,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match one entry with alternate values",
|
name: "match one entry with alternate values",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -762,7 +762,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch one entry by missing value",
|
name: "nomatch one entry by missing value",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -777,7 +777,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch one entry by missing key",
|
name: "nomatch one entry by missing key",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -791,7 +791,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "match one entry with multiple keys",
|
name: "match one entry with multiple keys",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -808,7 +808,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch one entry with one mismatching key",
|
name: "nomatch one entry with one mismatching key",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -825,7 +825,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch one entry with matching reverted value",
|
name: "nomatch one entry with matching reverted value",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -842,7 +842,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch one entry with one mismatching value",
|
name: "nomatch one entry with one mismatching value",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -859,7 +859,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nomatch one entry with one unindexed key",
|
name: "nomatch one entry with one unindexed key",
|
||||||
filter: &EventFilter{
|
filter: &eventFilter{
|
||||||
minHeight: -1,
|
minHeight: -1,
|
||||||
maxHeight: -1,
|
maxHeight: -1,
|
||||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||||
@ -876,7 +876,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
for _, tc := range inclusiveTestCases {
|
for _, tc := range inclusiveTestCases {
|
||||||
tc := tc // appease lint
|
tc := tc // appease lint
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
if err := ei.PrefillFilter(context.Background(), tc.filter, false); err != nil {
|
if err := ei.prefillFilter(context.Background(), tc.filter, false); err != nil {
|
||||||
require.NoError(t, err, "prefill filter events")
|
require.NoError(t, err, "prefill filter events")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -888,7 +888,7 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
|||||||
for _, tc := range exclusiveTestCases {
|
for _, tc := range exclusiveTestCases {
|
||||||
tc := tc // appease lint
|
tc := tc // appease lint
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
if err := ei.PrefillFilter(context.Background(), tc.filter, true); err != nil {
|
if err := ei.prefillFilter(context.Background(), tc.filter, true); err != nil {
|
||||||
require.NoError(t, err, "prefill filter events")
|
require.NoError(t, err, "prefill filter events")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,7 +372,7 @@ func TestOnboardRawPieceVerified_WithActorEvents(t *testing.T) {
|
|||||||
|
|
||||||
// verify that we can trace a datacap allocation through to a claim with the events, since this
|
// verify that we can trace a datacap allocation through to a claim with the events, since this
|
||||||
// information is not completely available from the state tree
|
// information is not completely available from the state tree
|
||||||
claims := buildClaimsFromEvents(ctx, t, eventsFromMessages, miner.FullNode)
|
claims := buildClaimsFromMessages(ctx, t, eventsFromMessages, miner.FullNode)
|
||||||
for _, claim := range claims {
|
for _, claim := range claims {
|
||||||
p, err := address.NewIDAddress(uint64(claim.Provider))
|
p, err := address.NewIDAddress(uint64(claim.Provider))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -395,6 +395,7 @@ func TestOnboardRawPieceVerified_WithActorEvents(t *testing.T) {
|
|||||||
}, claims)
|
}, claims)
|
||||||
|
|
||||||
// construct ActorEvents from GetActorEvents API
|
// construct ActorEvents from GetActorEvents API
|
||||||
|
t.Logf("Inspecting full events list from GetActorEvents")
|
||||||
allEvtsFromGetAPI, err := miner.FullNode.GetActorEvents(ctx, &types.ActorEventFilter{
|
allEvtsFromGetAPI, err := miner.FullNode.GetActorEvents(ctx, &types.ActorEventFilter{
|
||||||
FromHeight: epochPtr(0),
|
FromHeight: epochPtr(0),
|
||||||
})
|
})
|
||||||
@ -405,6 +406,7 @@ func TestOnboardRawPieceVerified_WithActorEvents(t *testing.T) {
|
|||||||
require.Equal(t, eventsFromMessages, allEvtsFromGetAPI)
|
require.Equal(t, eventsFromMessages, allEvtsFromGetAPI)
|
||||||
|
|
||||||
// construct ActorEvents from subscription channel for just the miner actor
|
// construct ActorEvents from subscription channel for just the miner actor
|
||||||
|
t.Logf("Inspecting only miner's events list from SubscribeActorEvents")
|
||||||
var subMinerEvts []*types.ActorEvent
|
var subMinerEvts []*types.ActorEvent
|
||||||
for evt := range minerEvtsChan {
|
for evt := range minerEvtsChan {
|
||||||
subMinerEvts = append(subMinerEvts, evt)
|
subMinerEvts = append(subMinerEvts, evt)
|
||||||
@ -421,15 +423,7 @@ func TestOnboardRawPieceVerified_WithActorEvents(t *testing.T) {
|
|||||||
// compare events from messages and receipts with events from subscription channel
|
// compare events from messages and receipts with events from subscription channel
|
||||||
require.Equal(t, allMinerEvts, subMinerEvts)
|
require.Equal(t, allMinerEvts, subMinerEvts)
|
||||||
|
|
||||||
// construct ActorEvents from subscription channel for just the sector-activated events
|
// construct ActorEvents from subscription channels for just the sector-activated events
|
||||||
var prefillSectorActivatedEvts []*types.ActorEvent
|
|
||||||
for evt := range sectorActivatedEvtsChan {
|
|
||||||
prefillSectorActivatedEvts = append(prefillSectorActivatedEvts, evt)
|
|
||||||
if len(prefillSectorActivatedEvts) == 2 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
require.Len(t, prefillSectorActivatedEvts, 2)
|
|
||||||
var sectorActivatedEvts []*types.ActorEvent
|
var sectorActivatedEvts []*types.ActorEvent
|
||||||
for _, evt := range eventsFromMessages {
|
for _, evt := range eventsFromMessages {
|
||||||
for _, entry := range evt.Entries {
|
for _, entry := range evt.Entries {
|
||||||
@ -439,10 +433,42 @@ func TestOnboardRawPieceVerified_WithActorEvents(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
require.Len(t, sectorActivatedEvts, 2) // sanity check
|
||||||
|
|
||||||
|
t.Logf("Inspecting only sector-activated events list from real-time SubscribeActorEvents")
|
||||||
|
var subscribedSectorActivatedEvts []*types.ActorEvent
|
||||||
|
for evt := range sectorActivatedEvtsChan {
|
||||||
|
subscribedSectorActivatedEvts = append(subscribedSectorActivatedEvts, evt)
|
||||||
|
if len(subscribedSectorActivatedEvts) == 2 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
// compare events from messages and receipts with events from subscription channel
|
// compare events from messages and receipts with events from subscription channel
|
||||||
require.Equal(t, sectorActivatedEvts, prefillSectorActivatedEvts)
|
require.Equal(t, sectorActivatedEvts, subscribedSectorActivatedEvts)
|
||||||
|
|
||||||
|
// same thing but use historical event fetching to see the same list
|
||||||
|
t.Logf("Inspecting only sector-activated events list from historical SubscribeActorEvents")
|
||||||
|
sectorActivatedEvtsChan, err = miner.FullNode.SubscribeActorEvents(ctx, &types.ActorEventFilter{
|
||||||
|
Fields: map[string][]types.ActorEventBlock{
|
||||||
|
"$type": {
|
||||||
|
{Codec: 0x51, Value: sectorActivatedCbor},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
FromHeight: epochPtr(0),
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
subscribedSectorActivatedEvts = subscribedSectorActivatedEvts[:0]
|
||||||
|
for evt := range sectorActivatedEvtsChan {
|
||||||
|
subscribedSectorActivatedEvts = append(subscribedSectorActivatedEvts, evt)
|
||||||
|
if len(subscribedSectorActivatedEvts) == 2 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// compare events from messages and receipts with events from subscription channel
|
||||||
|
require.Equal(t, sectorActivatedEvts, subscribedSectorActivatedEvts)
|
||||||
|
|
||||||
// check that our `ToHeight` filter works as expected
|
// check that our `ToHeight` filter works as expected
|
||||||
|
t.Logf("Inspecting only initial list of events SubscribeActorEvents with ToHeight")
|
||||||
var initialEvents []*types.ActorEvent
|
var initialEvents []*types.ActorEvent
|
||||||
for evt := range initialEventsChan {
|
for evt := range initialEventsChan {
|
||||||
initialEvents = append(initialEvents, evt)
|
initialEvents = append(initialEvents, evt)
|
||||||
@ -451,6 +477,7 @@ func TestOnboardRawPieceVerified_WithActorEvents(t *testing.T) {
|
|||||||
require.Equal(t, eventsFromMessages[0:5], initialEvents)
|
require.Equal(t, eventsFromMessages[0:5], initialEvents)
|
||||||
|
|
||||||
// construct ActorEvents from subscription channel for all actor events
|
// construct ActorEvents from subscription channel for all actor events
|
||||||
|
t.Logf("Inspecting full events list from historical SubscribeActorEvents")
|
||||||
allEvtsChan, err := miner.FullNode.SubscribeActorEvents(ctx, &types.ActorEventFilter{
|
allEvtsChan, err := miner.FullNode.SubscribeActorEvents(ctx, &types.ActorEventFilter{
|
||||||
FromHeight: epochPtr(0),
|
FromHeight: epochPtr(0),
|
||||||
})
|
})
|
||||||
@ -464,9 +491,15 @@ func TestOnboardRawPieceVerified_WithActorEvents(t *testing.T) {
|
|||||||
}
|
}
|
||||||
// compare events from messages and receipts with events from subscription channel
|
// compare events from messages and receipts with events from subscription channel
|
||||||
require.Equal(t, eventsFromMessages, prefillEvts)
|
require.Equal(t, eventsFromMessages, prefillEvts)
|
||||||
|
t.Logf("All done comparing events")
|
||||||
|
|
||||||
|
// NOTE: There is a delay in finishing this test because the SubscribeActorEvents
|
||||||
|
// with the ToHeight (initialEventsChan) has to wait at least a full actual epoch before
|
||||||
|
// realising that there's no more events for that filter. itests run with a different block
|
||||||
|
// speed than the ActorEventHandler is aware of.
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildClaimsFromEvents(ctx context.Context, t *testing.T, eventsFromMessages []*types.ActorEvent, node v1api.FullNode) []*verifregtypes9.Claim {
|
func buildClaimsFromMessages(ctx context.Context, t *testing.T, eventsFromMessages []*types.ActorEvent, node v1api.FullNode) []*verifregtypes9.Claim {
|
||||||
claimKeyCbor := stringToEventKey(t, "claim")
|
claimKeyCbor := stringToEventKey(t, "claim")
|
||||||
claims := make([]*verifregtypes9.Claim, 0)
|
claims := make([]*verifregtypes9.Claim, 0)
|
||||||
for _, event := range eventsFromMessages {
|
for _, event := range eventsFromMessages {
|
||||||
|
@ -1,14 +1,28 @@
|
|||||||
package full
|
package full
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
pseudo "math/rand"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ipfs/go-cid"
|
||||||
|
"github.com/multiformats/go-multihash"
|
||||||
|
"github.com/raulk/clock"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-address"
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
"github.com/filecoin-project/go-state-types/crypto"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/chain/events/filter"
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var testCid = cid.MustParse("bafyreicmaj5hhoy5mgqvamfhgexxyergw7hdeshizghodwkjg6qmpoco7i")
|
||||||
|
|
||||||
func TestParseHeightRange(t *testing.T) {
|
func TestParseHeightRange(t *testing.T) {
|
||||||
epochPtr := func(i int) *abi.ChainEpoch {
|
epochPtr := func(i int) *abi.ChainEpoch {
|
||||||
e := abi.ChainEpoch(i)
|
e := abi.ChainEpoch(i)
|
||||||
@ -97,16 +111,655 @@ func TestParseHeightRange(t *testing.T) {
|
|||||||
for name, tc := range tcs {
|
for name, tc := range tcs {
|
||||||
tc2 := tc
|
tc2 := tc
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
|
req := require.New(t)
|
||||||
min, max, err := parseHeightRange(tc2.heaviest, tc2.from, tc2.to, tc2.maxRange)
|
min, max, err := parseHeightRange(tc2.heaviest, tc2.from, tc2.to, tc2.maxRange)
|
||||||
require.Equal(t, tc2.minOut, min)
|
req.Equal(tc2.minOut, min)
|
||||||
require.Equal(t, tc2.maxOut, max)
|
req.Equal(tc2.maxOut, max)
|
||||||
if tc2.errStr != "" {
|
if tc2.errStr != "" {
|
||||||
fmt.Println(err)
|
t.Log(err)
|
||||||
require.Error(t, err)
|
req.Error(err)
|
||||||
require.Contains(t, err.Error(), tc2.errStr)
|
req.Contains(err.Error(), tc2.errStr)
|
||||||
} else {
|
} else {
|
||||||
require.NoError(t, err)
|
req.NoError(err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetActorEvents(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
req := require.New(t)
|
||||||
|
|
||||||
|
seed := time.Now().UnixNano()
|
||||||
|
t.Logf("seed: %d", seed)
|
||||||
|
rng := pseudo.New(pseudo.NewSource(seed))
|
||||||
|
const maxFilterHeightRange = 100
|
||||||
|
|
||||||
|
minerAddr, err := address.NewIDAddress(uint64(rng.Int63()))
|
||||||
|
req.NoError(err)
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
filter *types.ActorEventFilter
|
||||||
|
currentHeight int64
|
||||||
|
installMinHeight int64
|
||||||
|
installMaxHeight int64
|
||||||
|
installTipSetKey cid.Cid
|
||||||
|
installAddresses []address.Address
|
||||||
|
installKeysWithCodec map[string][]types.ActorEventBlock
|
||||||
|
installExcludeReverted bool
|
||||||
|
expectErr string
|
||||||
|
}{
|
||||||
|
"nil filter": {
|
||||||
|
filter: nil,
|
||||||
|
installMinHeight: -1,
|
||||||
|
installMaxHeight: -1,
|
||||||
|
},
|
||||||
|
"empty filter": {
|
||||||
|
filter: &types.ActorEventFilter{},
|
||||||
|
installMinHeight: -1,
|
||||||
|
installMaxHeight: -1,
|
||||||
|
},
|
||||||
|
"basic height range filter": {
|
||||||
|
filter: &types.ActorEventFilter{
|
||||||
|
FromHeight: epochPtr(0),
|
||||||
|
ToHeight: epochPtr(maxFilterHeightRange),
|
||||||
|
},
|
||||||
|
installMinHeight: 0,
|
||||||
|
installMaxHeight: maxFilterHeightRange,
|
||||||
|
},
|
||||||
|
"from, no to height": {
|
||||||
|
filter: &types.ActorEventFilter{
|
||||||
|
FromHeight: epochPtr(0),
|
||||||
|
},
|
||||||
|
currentHeight: maxFilterHeightRange - 1,
|
||||||
|
installMinHeight: 0,
|
||||||
|
installMaxHeight: -1,
|
||||||
|
},
|
||||||
|
"to, no from height": {
|
||||||
|
filter: &types.ActorEventFilter{
|
||||||
|
ToHeight: epochPtr(maxFilterHeightRange - 1),
|
||||||
|
},
|
||||||
|
installMinHeight: -1,
|
||||||
|
installMaxHeight: maxFilterHeightRange - 1,
|
||||||
|
},
|
||||||
|
"from, no to height, too far": {
|
||||||
|
filter: &types.ActorEventFilter{
|
||||||
|
FromHeight: epochPtr(0),
|
||||||
|
},
|
||||||
|
currentHeight: maxFilterHeightRange + 1,
|
||||||
|
expectErr: "invalid epoch range: 'from' height is too far in the past",
|
||||||
|
},
|
||||||
|
"to, no from height, too far": {
|
||||||
|
filter: &types.ActorEventFilter{
|
||||||
|
ToHeight: epochPtr(maxFilterHeightRange + 1),
|
||||||
|
},
|
||||||
|
currentHeight: 0,
|
||||||
|
expectErr: "invalid epoch range: 'to' height is too far in the future",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range testCases {
|
||||||
|
tc := tc
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
efm := newMockEventFilterManager(t)
|
||||||
|
collectedEvents := makeCollectedEvents(t, rng, 0, 1, 10)
|
||||||
|
filter := newMockFilter(ctx, t, rng, collectedEvents)
|
||||||
|
|
||||||
|
if tc.expectErr == "" {
|
||||||
|
efm.expectInstall(abi.ChainEpoch(tc.installMinHeight), abi.ChainEpoch(tc.installMaxHeight), tc.installTipSetKey, tc.installAddresses, tc.installKeysWithCodec, tc.installExcludeReverted, filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
ts, err := types.NewTipSet([]*types.BlockHeader{newBlockHeader(minerAddr, tc.currentHeight)})
|
||||||
|
req.NoError(err)
|
||||||
|
chain := newMockChainAccessor(t, ts)
|
||||||
|
|
||||||
|
handler := NewActorEventHandler(chain, efm, 50*time.Millisecond, maxFilterHeightRange)
|
||||||
|
|
||||||
|
gotEvents, err := handler.GetActorEvents(ctx, tc.filter)
|
||||||
|
if tc.expectErr != "" {
|
||||||
|
req.Error(err)
|
||||||
|
req.Contains(err.Error(), tc.expectErr)
|
||||||
|
} else {
|
||||||
|
req.NoError(err)
|
||||||
|
expectedEvents := collectedToActorEvents(collectedEvents)
|
||||||
|
req.Equal(expectedEvents, gotEvents)
|
||||||
|
efm.assertRemoved(filter.ID())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubscribeActorEvents(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
seed := time.Now().UnixNano()
|
||||||
|
t.Logf("seed: %d", seed)
|
||||||
|
rng := pseudo.New(pseudo.NewSource(seed))
|
||||||
|
mockClock := clock.NewMock()
|
||||||
|
|
||||||
|
const maxFilterHeightRange = 100
|
||||||
|
const blockDelay = 30 * time.Second
|
||||||
|
const filterStartHeight = 0
|
||||||
|
const currentHeight = 10
|
||||||
|
const finishHeight = 20
|
||||||
|
const eventsPerEpoch = 2
|
||||||
|
|
||||||
|
minerAddr, err := address.NewIDAddress(uint64(rng.Int63()))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
receiveSpeed time.Duration // how fast will we receive all events _per epoch_
|
||||||
|
expectComplete bool // do we expect this to succeed?
|
||||||
|
endEpoch int // -1 for no end
|
||||||
|
}{
|
||||||
|
{"fast", 0, true, -1},
|
||||||
|
{"fast with end", 0, true, finishHeight},
|
||||||
|
{"half block speed", blockDelay / 2, true, -1},
|
||||||
|
{"half block speed with end", blockDelay / 2, true, finishHeight},
|
||||||
|
// testing exactly blockDelay is a border case and will be flaky
|
||||||
|
{"1.5 block speed", blockDelay * 3 / 2, false, -1},
|
||||||
|
{"twice block speed", blockDelay * 2, false, -1},
|
||||||
|
} {
|
||||||
|
|
||||||
|
tc := tc
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
req := require.New(t)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
mockClock.Set(time.Now())
|
||||||
|
mockFilterManager := newMockEventFilterManager(t)
|
||||||
|
allEvents := makeCollectedEvents(t, rng, filterStartHeight, eventsPerEpoch, finishHeight)
|
||||||
|
historicalEvents := allEvents[0 : (currentHeight-filterStartHeight)*eventsPerEpoch]
|
||||||
|
mockFilter := newMockFilter(ctx, t, rng, historicalEvents)
|
||||||
|
mockFilterManager.expectInstall(abi.ChainEpoch(0), abi.ChainEpoch(tc.endEpoch), cid.Undef, nil, nil, false, mockFilter)
|
||||||
|
|
||||||
|
ts, err := types.NewTipSet([]*types.BlockHeader{newBlockHeader(minerAddr, currentHeight)})
|
||||||
|
req.NoError(err)
|
||||||
|
mockChain := newMockChainAccessor(t, ts)
|
||||||
|
|
||||||
|
handler := NewActorEventHandlerWithClock(mockChain, mockFilterManager, blockDelay, maxFilterHeightRange, mockClock)
|
||||||
|
|
||||||
|
aef := &types.ActorEventFilter{FromHeight: epochPtr(0)}
|
||||||
|
if tc.endEpoch >= 0 {
|
||||||
|
aef.ToHeight = epochPtr(tc.endEpoch)
|
||||||
|
}
|
||||||
|
eventChan, err := handler.SubscribeActorEvents(ctx, aef)
|
||||||
|
req.NoError(err)
|
||||||
|
|
||||||
|
gotEvents := make([]*types.ActorEvent, 0)
|
||||||
|
|
||||||
|
// assume we can cleanly pick up all historical events in one go
|
||||||
|
for len(gotEvents) < len(historicalEvents) && ctx.Err() == nil {
|
||||||
|
select {
|
||||||
|
case e, ok := <-eventChan:
|
||||||
|
req.True(ok)
|
||||||
|
gotEvents = append(gotEvents, e)
|
||||||
|
case <-ctx.Done():
|
||||||
|
t.Fatalf("timed out waiting for event")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
req.Equal(collectedToActorEvents(historicalEvents), gotEvents)
|
||||||
|
|
||||||
|
mockClock.Add(blockDelay)
|
||||||
|
nextReceiveTime := mockClock.Now()
|
||||||
|
|
||||||
|
// Ticker to simulate both time and the chain advancing, including emitting events at
|
||||||
|
// the right time directly to the filter.
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for thisHeight := int64(currentHeight); ctx.Err() == nil; thisHeight++ {
|
||||||
|
ts, err := types.NewTipSet([]*types.BlockHeader{newBlockHeader(minerAddr, thisHeight)})
|
||||||
|
req.NoError(err)
|
||||||
|
mockChain.setHeaviestTipSet(ts)
|
||||||
|
|
||||||
|
var eventsThisEpoch []*filter.CollectedEvent
|
||||||
|
if thisHeight <= finishHeight {
|
||||||
|
eventsThisEpoch = allEvents[(thisHeight-filterStartHeight)*eventsPerEpoch : (thisHeight-filterStartHeight+1)*eventsPerEpoch]
|
||||||
|
}
|
||||||
|
for i := 0; i < eventsPerEpoch; i++ {
|
||||||
|
if len(eventsThisEpoch) > 0 {
|
||||||
|
mockFilter.sendEventToChannel(eventsThisEpoch[0])
|
||||||
|
eventsThisEpoch = eventsThisEpoch[1:]
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-time.After(2 * time.Millisecond): // allow everyone to catch a breath
|
||||||
|
mockClock.Add(blockDelay / eventsPerEpoch)
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if thisHeight == finishHeight+1 && tc.expectComplete && tc.endEpoch < 0 && ctx.Err() == nil {
|
||||||
|
// at finish+1, for the case where we expect clean completion and there is no ToEpoch
|
||||||
|
// set on the filter, if we send one more event at the next height so we end up with
|
||||||
|
// something uncollected in the buffer, causing a disconnect
|
||||||
|
evt := makeCollectedEvents(t, rng, finishHeight+1, 1, finishHeight+1)[0]
|
||||||
|
mockFilter.sendEventToChannel(evt)
|
||||||
|
} // else if endEpoch is set, we expect the chain advance to force closure
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Client collecting events off the channel
|
||||||
|
|
||||||
|
var prematureEnd bool
|
||||||
|
for thisHeight := int64(currentHeight); thisHeight <= finishHeight && !prematureEnd && ctx.Err() == nil; thisHeight++ {
|
||||||
|
// delay to simulate latency
|
||||||
|
select {
|
||||||
|
case <-mockClock.After(nextReceiveTime.Sub(mockClock.Now())):
|
||||||
|
case <-ctx.Done():
|
||||||
|
t.Fatalf("timed out simulating receive delay")
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect eventsPerEpoch more events
|
||||||
|
newEvents := make([]*types.ActorEvent, 0)
|
||||||
|
for len(newEvents) < eventsPerEpoch && !prematureEnd && ctx.Err() == nil {
|
||||||
|
select {
|
||||||
|
case e, ok := <-eventChan: // receive the events from the subscription
|
||||||
|
if ok {
|
||||||
|
newEvents = append(newEvents, e)
|
||||||
|
} else {
|
||||||
|
prematureEnd = true
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
t.Fatalf("timed out waiting for event")
|
||||||
|
}
|
||||||
|
nextReceiveTime = nextReceiveTime.Add(tc.receiveSpeed)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.expectComplete || !prematureEnd {
|
||||||
|
// sanity check that we got what we expected this epoch
|
||||||
|
req.Len(newEvents, eventsPerEpoch)
|
||||||
|
epochEvents := allEvents[(thisHeight)*eventsPerEpoch : (thisHeight+1)*eventsPerEpoch]
|
||||||
|
req.Equal(collectedToActorEvents(epochEvents), newEvents)
|
||||||
|
gotEvents = append(gotEvents, newEvents...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Equal(tc.expectComplete, !prematureEnd, "expected to complete")
|
||||||
|
if tc.expectComplete {
|
||||||
|
req.Len(gotEvents, len(allEvents))
|
||||||
|
req.Equal(collectedToActorEvents(allEvents), gotEvents)
|
||||||
|
} else {
|
||||||
|
req.NotEqual(len(gotEvents), len(allEvents))
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
mockFilter.waitAssertClearSubChannelCalled(500 * time.Millisecond)
|
||||||
|
mockFilterManager.waitAssertRemoved(mockFilter.ID(), 500*time.Millisecond)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubscribeActorEvents_OnlyHistorical(t *testing.T) {
|
||||||
|
// Similar to TestSubscribeActorEvents but we set an explicit end that caps out at the current height
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
seed := time.Now().UnixNano()
|
||||||
|
t.Logf("seed: %d", seed)
|
||||||
|
rng := pseudo.New(pseudo.NewSource(seed))
|
||||||
|
mockClock := clock.NewMock()
|
||||||
|
|
||||||
|
const maxFilterHeightRange = 100
|
||||||
|
const blockDelay = 30 * time.Second
|
||||||
|
const filterStartHeight = 0
|
||||||
|
const currentHeight = 10
|
||||||
|
const eventsPerEpoch = 2
|
||||||
|
|
||||||
|
minerAddr, err := address.NewIDAddress(uint64(rng.Int63()))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
blockTimeToComplete float64 // fraction of a block time that it takes to receive all events
|
||||||
|
expectComplete bool // do we expect this to succeed?
|
||||||
|
}{
|
||||||
|
{"fast", 0, true},
|
||||||
|
{"half block speed", 0.5, true},
|
||||||
|
{"1.5 block speed", 1.5, false},
|
||||||
|
{"twice block speed", 2, false},
|
||||||
|
} {
|
||||||
|
|
||||||
|
tc := tc
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
req := require.New(t)
|
||||||
|
|
||||||
|
mockClock.Set(time.Now())
|
||||||
|
mockFilterManager := newMockEventFilterManager(t)
|
||||||
|
allEvents := makeCollectedEvents(t, rng, filterStartHeight, eventsPerEpoch, currentHeight-1)
|
||||||
|
mockFilter := newMockFilter(ctx, t, rng, allEvents)
|
||||||
|
mockFilterManager.expectInstall(abi.ChainEpoch(0), abi.ChainEpoch(currentHeight), cid.Undef, nil, nil, false, mockFilter)
|
||||||
|
|
||||||
|
ts, err := types.NewTipSet([]*types.BlockHeader{newBlockHeader(minerAddr, currentHeight)})
|
||||||
|
req.NoError(err)
|
||||||
|
mockChain := newMockChainAccessor(t, ts)
|
||||||
|
|
||||||
|
handler := NewActorEventHandlerWithClock(mockChain, mockFilterManager, blockDelay, maxFilterHeightRange, mockClock)
|
||||||
|
|
||||||
|
aef := &types.ActorEventFilter{FromHeight: epochPtr(0), ToHeight: epochPtr(currentHeight)}
|
||||||
|
eventChan, err := handler.SubscribeActorEvents(ctx, aef)
|
||||||
|
req.NoError(err)
|
||||||
|
|
||||||
|
gotEvents := make([]*types.ActorEvent, 0)
|
||||||
|
|
||||||
|
// assume we can cleanly pick up all historical events in one go
|
||||||
|
receiveLoop:
|
||||||
|
for len(gotEvents) < len(allEvents) && ctx.Err() == nil {
|
||||||
|
select {
|
||||||
|
case e, ok := <-eventChan:
|
||||||
|
if tc.expectComplete || ok {
|
||||||
|
req.True(ok)
|
||||||
|
gotEvents = append(gotEvents, e)
|
||||||
|
mockClock.Add(time.Duration(float64(blockDelay) * tc.blockTimeToComplete / float64(len(allEvents))))
|
||||||
|
// no need to advance the chain, we're also testing that's not necessary
|
||||||
|
time.Sleep(2 * time.Millisecond) // catch a breath
|
||||||
|
} else {
|
||||||
|
break receiveLoop
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
t.Fatalf("timed out waiting for event, got %d/%d events", len(gotEvents), len(allEvents))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if tc.expectComplete {
|
||||||
|
req.Equal(collectedToActorEvents(allEvents), gotEvents)
|
||||||
|
} else {
|
||||||
|
req.NotEqual(len(gotEvents), len(allEvents))
|
||||||
|
}
|
||||||
|
// advance the chain and observe cleanup
|
||||||
|
ts, err = types.NewTipSet([]*types.BlockHeader{newBlockHeader(minerAddr, currentHeight+1)})
|
||||||
|
req.NoError(err)
|
||||||
|
mockChain.setHeaviestTipSet(ts)
|
||||||
|
mockClock.Add(blockDelay)
|
||||||
|
mockFilterManager.waitAssertRemoved(mockFilter.ID(), 1*time.Second)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ ChainAccessor = (*mockChainAccessor)(nil)
|
||||||
|
_ filter.EventFilter = (*mockFilter)(nil)
|
||||||
|
_ EventFilterManager = (*mockEventFilterManager)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
type mockChainAccessor struct {
|
||||||
|
t *testing.T
|
||||||
|
ts *types.TipSet
|
||||||
|
lk sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMockChainAccessor(t *testing.T, ts *types.TipSet) *mockChainAccessor {
|
||||||
|
return &mockChainAccessor{t: t, ts: ts}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockChainAccessor) setHeaviestTipSet(ts *types.TipSet) {
|
||||||
|
m.lk.Lock()
|
||||||
|
defer m.lk.Unlock()
|
||||||
|
m.ts = ts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockChainAccessor) GetHeaviestTipSet() *types.TipSet {
|
||||||
|
m.lk.Lock()
|
||||||
|
defer m.lk.Unlock()
|
||||||
|
return m.ts
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockFilter struct {
|
||||||
|
t *testing.T
|
||||||
|
ctx context.Context
|
||||||
|
id types.FilterID
|
||||||
|
lastTaken time.Time
|
||||||
|
ch chan<- interface{}
|
||||||
|
historicalEvents []*filter.CollectedEvent
|
||||||
|
subChannelCalls int
|
||||||
|
clearSubChannelCalls int
|
||||||
|
lk sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMockFilter(ctx context.Context, t *testing.T, rng *pseudo.Rand, historicalEvents []*filter.CollectedEvent) *mockFilter {
|
||||||
|
t.Helper()
|
||||||
|
byt := make([]byte, 32)
|
||||||
|
_, err := rng.Read(byt)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return &mockFilter{
|
||||||
|
t: t,
|
||||||
|
ctx: ctx,
|
||||||
|
id: types.FilterID(byt),
|
||||||
|
historicalEvents: historicalEvents,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockFilter) sendEventToChannel(e *filter.CollectedEvent) {
|
||||||
|
m.lk.Lock()
|
||||||
|
defer m.lk.Unlock()
|
||||||
|
if m.ch != nil {
|
||||||
|
select {
|
||||||
|
case m.ch <- e:
|
||||||
|
case <-m.ctx.Done():
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockFilter) waitAssertClearSubChannelCalled(timeout time.Duration) {
|
||||||
|
m.t.Helper()
|
||||||
|
for start := time.Now(); time.Since(start) < timeout; time.Sleep(10 * time.Millisecond) {
|
||||||
|
m.lk.Lock()
|
||||||
|
c := m.clearSubChannelCalls
|
||||||
|
m.lk.Unlock()
|
||||||
|
switch c {
|
||||||
|
case 0:
|
||||||
|
continue
|
||||||
|
case 1:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
m.t.Fatalf("ClearSubChannel called more than once")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.t.Fatalf("ClearSubChannel not called")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockFilter) ID() types.FilterID {
|
||||||
|
return m.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockFilter) LastTaken() time.Time {
|
||||||
|
return m.lastTaken
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockFilter) SetSubChannel(ch chan<- interface{}) {
|
||||||
|
m.t.Helper()
|
||||||
|
m.lk.Lock()
|
||||||
|
defer m.lk.Unlock()
|
||||||
|
m.subChannelCalls++
|
||||||
|
m.ch = ch
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockFilter) ClearSubChannel() {
|
||||||
|
m.t.Helper()
|
||||||
|
m.lk.Lock()
|
||||||
|
defer m.lk.Unlock()
|
||||||
|
m.clearSubChannelCalls++
|
||||||
|
m.ch = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockFilter) TakeCollectedEvents(ctx context.Context) []*filter.CollectedEvent {
|
||||||
|
e := m.historicalEvents
|
||||||
|
m.historicalEvents = nil
|
||||||
|
m.lastTaken = time.Now()
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockFilter) CollectEvents(ctx context.Context, tse *filter.TipSetEvents, reorg bool, ar filter.AddressResolver) error {
|
||||||
|
m.t.Fatalf("unexpected call to CollectEvents")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type filterManagerExpectation struct {
|
||||||
|
minHeight, maxHeight abi.ChainEpoch
|
||||||
|
tipsetCid cid.Cid
|
||||||
|
addresses []address.Address
|
||||||
|
keysWithCodec map[string][]types.ActorEventBlock
|
||||||
|
excludeReverted bool
|
||||||
|
returnFilter filter.EventFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockEventFilterManager struct {
|
||||||
|
t *testing.T
|
||||||
|
expectations []filterManagerExpectation
|
||||||
|
removed []types.FilterID
|
||||||
|
lk sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMockEventFilterManager(t *testing.T) *mockEventFilterManager {
|
||||||
|
return &mockEventFilterManager{t: t}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockEventFilterManager) expectInstall(
|
||||||
|
minHeight, maxHeight abi.ChainEpoch,
|
||||||
|
tipsetCid cid.Cid,
|
||||||
|
addresses []address.Address,
|
||||||
|
keysWithCodec map[string][]types.ActorEventBlock,
|
||||||
|
excludeReverted bool,
|
||||||
|
returnFilter filter.EventFilter) {
|
||||||
|
|
||||||
|
m.t.Helper()
|
||||||
|
m.expectations = append(m.expectations, filterManagerExpectation{
|
||||||
|
minHeight: minHeight,
|
||||||
|
maxHeight: maxHeight,
|
||||||
|
tipsetCid: tipsetCid,
|
||||||
|
addresses: addresses,
|
||||||
|
keysWithCodec: keysWithCodec,
|
||||||
|
excludeReverted: excludeReverted,
|
||||||
|
returnFilter: returnFilter,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockEventFilterManager) assertRemoved(id types.FilterID) {
|
||||||
|
m.t.Helper()
|
||||||
|
m.lk.Lock()
|
||||||
|
defer m.lk.Unlock()
|
||||||
|
require.Contains(m.t, m.removed, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockEventFilterManager) waitAssertRemoved(id types.FilterID, timeout time.Duration) {
|
||||||
|
m.t.Helper()
|
||||||
|
for start := time.Now(); time.Since(start) < timeout; time.Sleep(10 * time.Millisecond) {
|
||||||
|
m.lk.Lock()
|
||||||
|
if len(m.removed) == 0 {
|
||||||
|
m.lk.Unlock()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
defer m.lk.Unlock()
|
||||||
|
require.Contains(m.t, m.removed, id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.t.Fatalf("filter %x not removed", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockEventFilterManager) Install(
|
||||||
|
ctx context.Context,
|
||||||
|
minHeight, maxHeight abi.ChainEpoch,
|
||||||
|
tipsetCid cid.Cid,
|
||||||
|
addresses []address.Address,
|
||||||
|
keysWithCodec map[string][]types.ActorEventBlock,
|
||||||
|
excludeReverted bool,
|
||||||
|
) (filter.EventFilter, error) {
|
||||||
|
|
||||||
|
require.True(m.t, len(m.expectations) > 0, "unexpected call to Install")
|
||||||
|
exp := m.expectations[0]
|
||||||
|
m.expectations = m.expectations[1:]
|
||||||
|
// check the expectation matches the call then return the attached filter
|
||||||
|
require.Equal(m.t, exp.minHeight, minHeight)
|
||||||
|
require.Equal(m.t, exp.maxHeight, maxHeight)
|
||||||
|
require.Equal(m.t, exp.tipsetCid, tipsetCid)
|
||||||
|
require.Equal(m.t, exp.addresses, addresses)
|
||||||
|
require.Equal(m.t, exp.keysWithCodec, keysWithCodec)
|
||||||
|
require.Equal(m.t, exp.excludeReverted, excludeReverted)
|
||||||
|
return exp.returnFilter, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockEventFilterManager) Remove(ctx context.Context, id types.FilterID) error {
|
||||||
|
m.lk.Lock()
|
||||||
|
defer m.lk.Unlock()
|
||||||
|
m.removed = append(m.removed, id)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBlockHeader(minerAddr address.Address, height int64) *types.BlockHeader {
|
||||||
|
return &types.BlockHeader{
|
||||||
|
Miner: minerAddr,
|
||||||
|
Ticket: &types.Ticket{
|
||||||
|
VRFProof: []byte("vrf proof0000000vrf proof0000000"),
|
||||||
|
},
|
||||||
|
ElectionProof: &types.ElectionProof{
|
||||||
|
VRFProof: []byte("vrf proof0000000vrf proof0000000"),
|
||||||
|
},
|
||||||
|
Parents: []cid.Cid{testCid, testCid},
|
||||||
|
ParentMessageReceipts: testCid,
|
||||||
|
BLSAggregate: &crypto.Signature{Type: crypto.SigTypeBLS, Data: []byte("sign me up")},
|
||||||
|
ParentWeight: types.NewInt(123125126212),
|
||||||
|
Messages: testCid,
|
||||||
|
Height: abi.ChainEpoch(height),
|
||||||
|
ParentStateRoot: testCid,
|
||||||
|
BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS, Data: []byte("sign me up")},
|
||||||
|
ParentBaseFee: types.NewInt(3432432843291),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func epochPtr(i int) *abi.ChainEpoch {
|
||||||
|
e := abi.ChainEpoch(i)
|
||||||
|
return &e
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectedToActorEvents(collected []*filter.CollectedEvent) []*types.ActorEvent {
|
||||||
|
var out []*types.ActorEvent
|
||||||
|
for _, c := range collected {
|
||||||
|
out = append(out, &types.ActorEvent{
|
||||||
|
Entries: c.Entries,
|
||||||
|
Emitter: c.EmitterAddr,
|
||||||
|
Reverted: c.Reverted,
|
||||||
|
Height: c.Height,
|
||||||
|
TipSetKey: c.TipSetKey,
|
||||||
|
MsgCid: c.MsgCid,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCollectedEvents(t *testing.T, rng *pseudo.Rand, eventStartHeight, eventsPerHeight, eventEndHeight int64) []*filter.CollectedEvent {
|
||||||
|
var out []*filter.CollectedEvent
|
||||||
|
for h := eventStartHeight; h <= eventEndHeight; h++ {
|
||||||
|
for i := int64(0); i < eventsPerHeight; i++ {
|
||||||
|
out = append(out, makeCollectedEvent(t, rng, types.NewTipSetKey(mkCid(t, fmt.Sprintf("h=%d", h))), abi.ChainEpoch(h)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCollectedEvent(t *testing.T, rng *pseudo.Rand, tsKey types.TipSetKey, height abi.ChainEpoch) *filter.CollectedEvent {
|
||||||
|
addr, err := address.NewIDAddress(uint64(rng.Int63()))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return &filter.CollectedEvent{
|
||||||
|
Entries: []types.EventEntry{
|
||||||
|
{Flags: 0x01, Key: "k1", Codec: cid.Raw, Value: []byte("v1")},
|
||||||
|
{Flags: 0x01, Key: "k2", Codec: cid.Raw, Value: []byte("v2")},
|
||||||
|
},
|
||||||
|
EmitterAddr: addr,
|
||||||
|
EventIdx: 0,
|
||||||
|
Reverted: false,
|
||||||
|
Height: height,
|
||||||
|
TipSetKey: tsKey,
|
||||||
|
MsgIdx: 0,
|
||||||
|
MsgCid: testCid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mkCid(t *testing.T, s string) cid.Cid {
|
||||||
|
h, err := multihash.Sum([]byte(s), multihash.SHA2_256, -1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return cid.NewCidV1(cid.Raw, h)
|
||||||
|
}
|
||||||
|
@ -6,14 +6,14 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
|
"github.com/raulk/clock"
|
||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-address"
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/api"
|
"github.com/filecoin-project/lotus/api"
|
||||||
"github.com/filecoin-project/lotus/build"
|
|
||||||
"github.com/filecoin-project/lotus/chain/events/filter"
|
"github.com/filecoin-project/lotus/chain/events/filter"
|
||||||
"github.com/filecoin-project/lotus/chain/store"
|
|
||||||
"github.com/filecoin-project/lotus/chain/types"
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -27,21 +27,70 @@ var (
|
|||||||
_ ActorEventAPI = *new(api.Gateway)
|
_ ActorEventAPI = *new(api.Gateway)
|
||||||
)
|
)
|
||||||
|
|
||||||
type ActorEventHandler struct {
|
type ChainAccessor interface {
|
||||||
EventFilterManager *filter.EventFilterManager
|
GetHeaviestTipSet() *types.TipSet
|
||||||
MaxFilterHeightRange abi.ChainEpoch
|
|
||||||
Chain *store.ChainStore
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ ActorEventAPI = (*ActorEventHandler)(nil)
|
type EventFilterManager interface {
|
||||||
|
Install(
|
||||||
|
ctx context.Context,
|
||||||
|
minHeight, maxHeight abi.ChainEpoch,
|
||||||
|
tipsetCid cid.Cid,
|
||||||
|
addresses []address.Address,
|
||||||
|
keysWithCodec map[string][]types.ActorEventBlock,
|
||||||
|
excludeReverted bool,
|
||||||
|
) (filter.EventFilter, error)
|
||||||
|
Remove(ctx context.Context, id types.FilterID) error
|
||||||
|
}
|
||||||
|
|
||||||
type ActorEventsAPI struct {
|
type ActorEventsAPI struct {
|
||||||
fx.In
|
fx.In
|
||||||
ActorEventAPI
|
ActorEventAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ActorEventHandler struct {
|
||||||
|
chain ChainAccessor
|
||||||
|
eventFilterManager EventFilterManager
|
||||||
|
blockDelay time.Duration
|
||||||
|
maxFilterHeightRange abi.ChainEpoch
|
||||||
|
clock clock.Clock
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ActorEventAPI = (*ActorEventHandler)(nil)
|
||||||
|
|
||||||
|
func NewActorEventHandler(
|
||||||
|
chain ChainAccessor,
|
||||||
|
eventFilterManager EventFilterManager,
|
||||||
|
blockDelay time.Duration,
|
||||||
|
maxFilterHeightRange abi.ChainEpoch,
|
||||||
|
) *ActorEventHandler {
|
||||||
|
return &ActorEventHandler{
|
||||||
|
chain: chain,
|
||||||
|
eventFilterManager: eventFilterManager,
|
||||||
|
blockDelay: blockDelay,
|
||||||
|
maxFilterHeightRange: maxFilterHeightRange,
|
||||||
|
clock: clock.New(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewActorEventHandlerWithClock(
|
||||||
|
chain ChainAccessor,
|
||||||
|
eventFilterManager EventFilterManager,
|
||||||
|
blockDelay time.Duration,
|
||||||
|
maxFilterHeightRange abi.ChainEpoch,
|
||||||
|
clock clock.Clock,
|
||||||
|
) *ActorEventHandler {
|
||||||
|
return &ActorEventHandler{
|
||||||
|
chain: chain,
|
||||||
|
eventFilterManager: eventFilterManager,
|
||||||
|
blockDelay: blockDelay,
|
||||||
|
maxFilterHeightRange: maxFilterHeightRange,
|
||||||
|
clock: clock,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (a *ActorEventHandler) GetActorEvents(ctx context.Context, evtFilter *types.ActorEventFilter) ([]*types.ActorEvent, error) {
|
func (a *ActorEventHandler) GetActorEvents(ctx context.Context, evtFilter *types.ActorEventFilter) ([]*types.ActorEvent, error) {
|
||||||
if a.EventFilterManager == nil {
|
if a.eventFilterManager == nil {
|
||||||
return nil, api.ErrNotSupported
|
return nil, api.ErrNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,13 +108,13 @@ func (a *ActorEventHandler) GetActorEvents(ctx context.Context, evtFilter *types
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get tipset cid: %w", err)
|
return nil, fmt.Errorf("failed to get tipset cid: %w", err)
|
||||||
}
|
}
|
||||||
f, err := a.EventFilterManager.Install(ctx, params.MinHeight, params.MaxHeight, tipSetCid, evtFilter.Addresses, evtFilter.Fields, false)
|
f, err := a.eventFilterManager.Install(ctx, params.MinHeight, params.MaxHeight, tipSetCid, evtFilter.Addresses, evtFilter.Fields, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
evs, _, _ := getCollected(ctx, f)
|
evs := getCollected(ctx, f)
|
||||||
if err := a.EventFilterManager.Remove(ctx, f.ID()); err != nil {
|
if err := a.eventFilterManager.Remove(ctx, f.ID()); err != nil {
|
||||||
log.Warnf("failed to remove filter: %s", err)
|
log.Warnf("failed to remove filter: %s", err)
|
||||||
}
|
}
|
||||||
return evs, nil
|
return evs, nil
|
||||||
@ -90,18 +139,14 @@ func (a *ActorEventHandler) parseFilter(f types.ActorEventFilter) (*filterParams
|
|||||||
return nil, fmt.Errorf("cannot specify both TipSetKey and FromHeight/ToHeight")
|
return nil, fmt.Errorf("cannot specify both TipSetKey and FromHeight/ToHeight")
|
||||||
}
|
}
|
||||||
|
|
||||||
tsk := types.EmptyTSK
|
|
||||||
if f.TipSetKey != nil {
|
|
||||||
tsk = *f.TipSetKey
|
|
||||||
}
|
|
||||||
return &filterParams{
|
return &filterParams{
|
||||||
MinHeight: 0,
|
MinHeight: 0,
|
||||||
MaxHeight: 0,
|
MaxHeight: 0,
|
||||||
TipSetKey: tsk,
|
TipSetKey: *f.TipSetKey,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
min, max, err := parseHeightRange(a.Chain.GetHeaviestTipSet().Height(), f.FromHeight, f.ToHeight, a.MaxFilterHeightRange)
|
min, max, err := parseHeightRange(a.chain.GetHeaviestTipSet().Height(), f.FromHeight, f.ToHeight, a.maxFilterHeightRange)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -156,9 +201,10 @@ func parseHeightRange(heaviest abi.ChainEpoch, fromHeight, toHeight *abi.ChainEp
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *ActorEventHandler) SubscribeActorEvents(ctx context.Context, evtFilter *types.ActorEventFilter) (<-chan *types.ActorEvent, error) {
|
func (a *ActorEventHandler) SubscribeActorEvents(ctx context.Context, evtFilter *types.ActorEventFilter) (<-chan *types.ActorEvent, error) {
|
||||||
if a.EventFilterManager == nil {
|
if a.eventFilterManager == nil {
|
||||||
return nil, api.ErrNotSupported
|
return nil, api.ErrNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
if evtFilter == nil {
|
if evtFilter == nil {
|
||||||
evtFilter = &types.ActorEventFilter{}
|
evtFilter = &types.ActorEventFilter{}
|
||||||
}
|
}
|
||||||
@ -171,22 +217,18 @@ func (a *ActorEventHandler) SubscribeActorEvents(ctx context.Context, evtFilter
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get tipset cid: %w", err)
|
return nil, fmt.Errorf("failed to get tipset cid: %w", err)
|
||||||
}
|
}
|
||||||
fm, err := a.EventFilterManager.Install(ctx, params.MinHeight, params.MaxHeight, tipSetCid, evtFilter.Addresses, evtFilter.Fields, false)
|
fm, err := a.eventFilterManager.Install(ctx, params.MinHeight, params.MaxHeight, tipSetCid, evtFilter.Addresses, evtFilter.Fields, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// The goal for the code below is to be able to send events on the `out` channel as fast as
|
// The goal for the code below is to send events on the `out` channel as fast as possible and not
|
||||||
// possible and not let it get too far behind the rate at which the events are generated.
|
// let it get too far behind the rate at which the events are generated.
|
||||||
// For historical events we see the rate at which they were generated by looking the height range;
|
// For historical events, we aim to send all events within a single block's time (30s on mainnet).
|
||||||
// we then make sure that the client can receive them at least twice as fast as they were
|
// This ensures that the client can catch up quickly enough to start receiving new events.
|
||||||
// generated so they catch up quick enough to receive new events.
|
// For ongoing events, we also aim to send all events within a single block's time, so we never
|
||||||
// For ongoing events we use an exponential moving average of the events per height to make sure
|
// want to be buffering events (approximately) more than one epoch behind the current head.
|
||||||
// that the client doesn't fall behind.
|
// It's approximate because we only update our notion of "current epoch" once per ~blocktime.
|
||||||
// In both cases we allow a little bit of slack but need to avoid letting the client bloat the
|
|
||||||
// buffer too much.
|
|
||||||
// There is no special handling for reverts, so they will just look like a lot more events per
|
|
||||||
// epoch and the user has to receive them anyway.
|
|
||||||
|
|
||||||
out := make(chan *types.ActorEvent)
|
out := make(chan *types.ActorEvent)
|
||||||
|
|
||||||
@ -195,54 +237,41 @@ func (a *ActorEventHandler) SubscribeActorEvents(ctx context.Context, evtFilter
|
|||||||
// tell the caller we're done
|
// tell the caller we're done
|
||||||
close(out)
|
close(out)
|
||||||
fm.ClearSubChannel()
|
fm.ClearSubChannel()
|
||||||
if err := a.EventFilterManager.Remove(ctx, fm.ID()); err != nil {
|
if err := a.eventFilterManager.Remove(ctx, fm.ID()); err != nil {
|
||||||
log.Warnf("failed to remove filter: %s", err)
|
log.Warnf("failed to remove filter: %s", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// When we start sending real-time events, we want to make sure that we don't fall behind more
|
||||||
|
// than one epoch's worth of events (approximately). Capture this value now, before we send
|
||||||
|
// historical events to allow for a little bit of slack in the historical event sending.
|
||||||
|
minBacklogHeight := a.chain.GetHeaviestTipSet().Height() - 1
|
||||||
|
|
||||||
// Handle any historical events that our filter may have picked up -----------------------------
|
// Handle any historical events that our filter may have picked up -----------------------------
|
||||||
|
|
||||||
evs, minEpoch, maxEpoch := getCollected(ctx, fm)
|
evs := getCollected(ctx, fm)
|
||||||
if len(evs) > 0 {
|
if len(evs) > 0 {
|
||||||
// must be able to send events at least twice as fast as they were generated
|
// ensure we get all events out on the channel within one block's time (30s on mainnet)
|
||||||
epochRange := maxEpoch - minEpoch
|
timer := a.clock.Timer(a.blockDelay)
|
||||||
if epochRange <= 0 {
|
|
||||||
epochRange = 1
|
|
||||||
}
|
|
||||||
eventsPerEpoch := float64(len(evs)) / float64(epochRange)
|
|
||||||
eventsPerSecond := 2 * eventsPerEpoch / float64(build.BlockDelaySecs)
|
|
||||||
// a minimum rate of 1 event per second if we don't have many events
|
|
||||||
if eventsPerSecond < 1 {
|
|
||||||
eventsPerSecond = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// send events from evs to the out channel and ensure we don't do it slower than eventsPerMs
|
|
||||||
ticker := time.NewTicker(time.Second)
|
|
||||||
defer ticker.Stop()
|
|
||||||
|
|
||||||
const maxSlowTicks = 3 // slightly forgiving, allow 3 slow ticks (seconds) before giving up
|
|
||||||
slowTicks := 0
|
|
||||||
sentEvents := 0.0
|
|
||||||
|
|
||||||
for _, ev := range evs {
|
for _, ev := range evs {
|
||||||
select {
|
select {
|
||||||
case out <- ev:
|
case out <- ev:
|
||||||
sentEvents++
|
case <-timer.C:
|
||||||
case <-ticker.C:
|
|
||||||
if sentEvents < eventsPerSecond {
|
|
||||||
slowTicks++
|
|
||||||
if slowTicks >= maxSlowTicks {
|
|
||||||
log.Errorf("closing event subscription due to slow event sending rate")
|
log.Errorf("closing event subscription due to slow event sending rate")
|
||||||
|
timer.Stop()
|
||||||
return
|
return
|
||||||
}
|
|
||||||
} else {
|
|
||||||
slowTicks = 0
|
|
||||||
}
|
|
||||||
sentEvents = 0
|
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
timer.Stop()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
timer.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
// for the case where we have a MaxHeight set, we don't get a signal from the filter when we
|
||||||
|
// reach that height, so we need to check it ourselves, do it now but also in the loop
|
||||||
|
if params.MaxHeight > 0 && a.chain.GetHeaviestTipSet().Height() > params.MaxHeight {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle ongoing events from the filter -------------------------------------------------------
|
// Handle ongoing events from the filter -------------------------------------------------------
|
||||||
@ -251,10 +280,7 @@ func (a *ActorEventHandler) SubscribeActorEvents(ctx context.Context, evtFilter
|
|||||||
fm.SetSubChannel(in)
|
fm.SetSubChannel(in)
|
||||||
|
|
||||||
var buffer []*types.ActorEvent
|
var buffer []*types.ActorEvent
|
||||||
const α = 0.2 // decay factor for the events per height EMA
|
nextBacklogHeightUpdate := a.clock.Now().Add(a.blockDelay)
|
||||||
var eventsPerHeightEma float64 = 256 // exponential moving average of events per height, initially guess at 256
|
|
||||||
var lastHeight abi.ChainEpoch // last seen event height
|
|
||||||
var eventsAtCurrentHeight int // number of events at the current height
|
|
||||||
|
|
||||||
collectEvent := func(ev interface{}) bool {
|
collectEvent := func(ev interface{}) bool {
|
||||||
ce, ok := ev.(*filter.CollectedEvent)
|
ce, ok := ev.(*filter.CollectedEvent)
|
||||||
@ -263,15 +289,12 @@ func (a *ActorEventHandler) SubscribeActorEvents(ctx context.Context, evtFilter
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if ce.Height > lastHeight {
|
if ce.Height < minBacklogHeight {
|
||||||
// update the EMA of events per height when the height increases
|
// since we mostly care about buffer size, we only trigger a too-slow close when the buffer
|
||||||
if lastHeight != 0 {
|
// increases, i.e. we collect a new event
|
||||||
eventsPerHeightEma = α*float64(eventsAtCurrentHeight) + (1-α)*eventsPerHeightEma
|
log.Errorf("closing event subscription due to slow event sending rate")
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
lastHeight = ce.Height
|
|
||||||
eventsAtCurrentHeight = 0
|
|
||||||
}
|
|
||||||
eventsAtCurrentHeight++
|
|
||||||
|
|
||||||
buffer = append(buffer, &types.ActorEvent{
|
buffer = append(buffer, &types.ActorEvent{
|
||||||
Entries: ce.Entries,
|
Entries: ce.Entries,
|
||||||
@ -284,23 +307,11 @@ func (a *ActorEventHandler) SubscribeActorEvents(ctx context.Context, evtFilter
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// for the case where we have a MaxHeight set, we don't get a signal from the filter when we
|
ticker := a.clock.Ticker(a.blockDelay)
|
||||||
// reach that height, so we need to check it ourselves, do it now but also in the loop
|
|
||||||
if params.MaxHeight > 0 && a.Chain.GetHeaviestTipSet().Height() > params.MaxHeight {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ticker := time.NewTicker(time.Duration(build.BlockDelaySecs) * time.Second)
|
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
for ctx.Err() == nil {
|
for ctx.Err() == nil {
|
||||||
if len(buffer) > 0 {
|
if len(buffer) > 0 {
|
||||||
// check if we need to disconnect the client because they've fallen behind, always allow at
|
|
||||||
// least 8 events in the buffer to provide a little bit of slack
|
|
||||||
if len(buffer) > 8 && float64(len(buffer)) > eventsPerHeightEma/2 {
|
|
||||||
log.Errorf("closing event subscription due to slow event sending rate")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case ev, ok := <-in: // incoming event
|
case ev, ok := <-in: // incoming event
|
||||||
if !ok || !collectEvent(ev) {
|
if !ok || !collectEvent(ev) {
|
||||||
@ -309,6 +320,12 @@ func (a *ActorEventHandler) SubscribeActorEvents(ctx context.Context, evtFilter
|
|||||||
case out <- buffer[0]: // successful send
|
case out <- buffer[0]: // successful send
|
||||||
buffer[0] = nil
|
buffer[0] = nil
|
||||||
buffer = buffer[1:]
|
buffer = buffer[1:]
|
||||||
|
case <-ticker.C:
|
||||||
|
// check that our backlog isn't too big by looking at the oldest event
|
||||||
|
if buffer[0].Height < minBacklogHeight {
|
||||||
|
log.Errorf("closing event subscription due to slow event sending rate")
|
||||||
|
return
|
||||||
|
}
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -321,30 +338,30 @@ func (a *ActorEventHandler) SubscribeActorEvents(ctx context.Context, evtFilter
|
|||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
if params.MaxHeight > 0 && a.Chain.GetHeaviestTipSet().Height() > params.MaxHeight {
|
currentHeight := a.chain.GetHeaviestTipSet().Height()
|
||||||
|
if params.MaxHeight > 0 && currentHeight > params.MaxHeight {
|
||||||
|
// we've reached the filter's MaxHeight, we're done so we can close the channel
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if a.clock.Now().After(nextBacklogHeightUpdate) {
|
||||||
|
minBacklogHeight = a.chain.GetHeaviestTipSet().Height() - 1
|
||||||
|
nextBacklogHeightUpdate = a.clock.Now().Add(a.blockDelay)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCollected(ctx context.Context, f *filter.EventFilter) ([]*types.ActorEvent, abi.ChainEpoch, abi.ChainEpoch) {
|
func getCollected(ctx context.Context, f filter.EventFilter) []*types.ActorEvent {
|
||||||
ces := f.TakeCollectedEvents(ctx)
|
ces := f.TakeCollectedEvents(ctx)
|
||||||
|
|
||||||
var out []*types.ActorEvent
|
var out []*types.ActorEvent
|
||||||
var min, max abi.ChainEpoch
|
|
||||||
|
|
||||||
for _, e := range ces {
|
for _, e := range ces {
|
||||||
if min == 0 || e.Height < min {
|
|
||||||
min = e.Height
|
|
||||||
}
|
|
||||||
if e.Height > max {
|
|
||||||
max = e.Height
|
|
||||||
}
|
|
||||||
out = append(out, &types.ActorEvent{
|
out = append(out, &types.ActorEvent{
|
||||||
Entries: e.Entries,
|
Entries: e.Entries,
|
||||||
Emitter: e.EmitterAddr,
|
Emitter: e.EmitterAddr,
|
||||||
@ -355,5 +372,5 @@ func getCollected(ctx context.Context, f *filter.EventFilter) ([]*types.ActorEve
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return out, min, max
|
return out
|
||||||
}
|
}
|
||||||
|
@ -1322,7 +1322,7 @@ func parseBlockRange(heaviest abi.ChainEpoch, fromBlock, toBlock *string, maxRan
|
|||||||
return minHeight, maxHeight, nil
|
return minHeight, maxHeight, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EthEventHandler) installEthFilterSpec(ctx context.Context, filterSpec *ethtypes.EthFilterSpec) (*filter.EventFilter, error) {
|
func (e *EthEventHandler) installEthFilterSpec(ctx context.Context, filterSpec *ethtypes.EthFilterSpec) (filter.EventFilter, error) {
|
||||||
var (
|
var (
|
||||||
minHeight abi.ChainEpoch
|
minHeight abi.ChainEpoch
|
||||||
maxHeight abi.ChainEpoch
|
maxHeight abi.ChainEpoch
|
||||||
@ -1465,7 +1465,7 @@ func (e *EthEventHandler) EthUninstallFilter(ctx context.Context, id ethtypes.Et
|
|||||||
|
|
||||||
func (e *EthEventHandler) uninstallFilter(ctx context.Context, f filter.Filter) error {
|
func (e *EthEventHandler) uninstallFilter(ctx context.Context, f filter.Filter) error {
|
||||||
switch f.(type) {
|
switch f.(type) {
|
||||||
case *filter.EventFilter:
|
case filter.EventFilter:
|
||||||
err := e.EventFilterManager.Remove(ctx, f.ID())
|
err := e.EventFilterManager.Remove(ctx, f.ID())
|
||||||
if err != nil && !errors.Is(err, filter.ErrFilterNotFound) {
|
if err != nil && !errors.Is(err, filter.ErrFilterNotFound) {
|
||||||
return err
|
return err
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/filecoin-project/go-address"
|
"github.com/filecoin-project/go-address"
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/build"
|
||||||
"github.com/filecoin-project/lotus/chain/events"
|
"github.com/filecoin-project/lotus/chain/events"
|
||||||
"github.com/filecoin-project/lotus/chain/events/filter"
|
"github.com/filecoin-project/lotus/chain/events/filter"
|
||||||
"github.com/filecoin-project/lotus/chain/messagepool"
|
"github.com/filecoin-project/lotus/chain/messagepool"
|
||||||
@ -163,18 +164,16 @@ func EventFilterManager(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.Loc
|
|||||||
|
|
||||||
func ActorEventHandler(enable bool, fevmCfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo, fx.Lifecycle, *filter.EventFilterManager, *store.ChainStore, *stmgr.StateManager, EventHelperAPI, *messagepool.MessagePool, full.StateAPI, full.ChainAPI) (*full.ActorEventHandler, error) {
|
func ActorEventHandler(enable bool, fevmCfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo, fx.Lifecycle, *filter.EventFilterManager, *store.ChainStore, *stmgr.StateManager, EventHelperAPI, *messagepool.MessagePool, full.StateAPI, full.ChainAPI) (*full.ActorEventHandler, error) {
|
||||||
return func(mctx helpers.MetricsCtx, r repo.LockedRepo, lc fx.Lifecycle, fm *filter.EventFilterManager, cs *store.ChainStore, sm *stmgr.StateManager, evapi EventHelperAPI, mp *messagepool.MessagePool, stateapi full.StateAPI, chainapi full.ChainAPI) (*full.ActorEventHandler, error) {
|
return func(mctx helpers.MetricsCtx, r repo.LockedRepo, lc fx.Lifecycle, fm *filter.EventFilterManager, cs *store.ChainStore, sm *stmgr.StateManager, evapi EventHelperAPI, mp *messagepool.MessagePool, stateapi full.StateAPI, chainapi full.ChainAPI) (*full.ActorEventHandler, error) {
|
||||||
ee := &full.ActorEventHandler{
|
|
||||||
MaxFilterHeightRange: abi.ChainEpoch(fevmCfg.Events.MaxFilterHeightRange),
|
|
||||||
Chain: cs,
|
|
||||||
}
|
|
||||||
|
|
||||||
if !enable || fevmCfg.Events.DisableRealTimeFilterAPI {
|
if !enable || fevmCfg.Events.DisableRealTimeFilterAPI {
|
||||||
// all Actor events functionality is disabled
|
fm = nil
|
||||||
return ee, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ee.EventFilterManager = fm
|
return full.NewActorEventHandler(
|
||||||
|
cs,
|
||||||
return ee, nil
|
fm,
|
||||||
|
time.Duration(build.BlockDelaySecs)*time.Second,
|
||||||
|
abi.ChainEpoch(fevmCfg.Events.MaxFilterHeightRange),
|
||||||
|
), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user