Built-in actor events first draft
This commit is contained in:
parent
9aef2ec8b5
commit
f007a012af
@ -906,6 +906,20 @@ type FullNode interface {
|
||||
|
||||
RaftState(ctx context.Context) (*RaftStateData, error) //perm:read
|
||||
RaftLeader(ctx context.Context) (peer.ID, error) //perm:read
|
||||
|
||||
// Actor events
|
||||
|
||||
// GetActorEvents returns all FVM and built-in Actor events that match the given filter.
|
||||
// This is a request/response API.
|
||||
GetActorEvents(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error) //perm:read
|
||||
|
||||
// SubscribeActorEvents returns a long-lived stream of all FVM and built-in Actor events that match the given filter.
|
||||
// Events that match the given filter are written to the stream in real-time as they are emitted from the FVM.
|
||||
// The response stream is closed when the client disconnects or if there is an error while writing an event to the stream.
|
||||
// This API also allows clients to read all historical events matching the given filter before
|
||||
// any real-time events are written to the response stream.
|
||||
// NOTE: THIS API IS ONLY SUPPORTED OVER WEBSOCKETS FOR NOW
|
||||
SubscribeActorEvents(ctx context.Context, filter *types.SubActorEventFilter) (<-chan *types.ActorEvent, error) //perm:read
|
||||
}
|
||||
|
||||
// reverse interface to the client, called after EthSubscribe
|
||||
|
@ -129,4 +129,7 @@ type Gateway interface {
|
||||
Web3ClientVersion(ctx context.Context) (string, error)
|
||||
EthTraceBlock(ctx context.Context, blkNum string) ([]*ethtypes.EthTraceBlock, error)
|
||||
EthTraceReplayBlockTransactions(ctx context.Context, blkNum string, traceTypes []string) ([]*ethtypes.EthTraceReplayBlockTransaction, error)
|
||||
|
||||
GetActorEvents(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error)
|
||||
SubscribeActorEvents(ctx context.Context, filter *types.SubActorEventFilter) (<-chan *types.ActorEvent, error)
|
||||
}
|
||||
|
@ -414,6 +414,42 @@ func init() {
|
||||
VerifiedAllocationKey: nil,
|
||||
Notify: nil,
|
||||
})
|
||||
|
||||
addExample(&types.ActorEventBlock{
|
||||
Codec: 0x51,
|
||||
Value: []byte("data"),
|
||||
})
|
||||
|
||||
addExample(&types.ActorEventFilter{
|
||||
Addresses: []address.Address{addr},
|
||||
Fields: map[string][]types.ActorEventBlock{
|
||||
"abc": {
|
||||
{
|
||||
Codec: 0x51,
|
||||
Value: []byte("data"),
|
||||
},
|
||||
},
|
||||
},
|
||||
MinEpoch: 2301220,
|
||||
MaxEpoch: 2301220,
|
||||
})
|
||||
|
||||
addExample(&types.SubActorEventFilter{
|
||||
Filter: types.ActorEventFilter{
|
||||
Addresses: []address.Address{addr},
|
||||
Fields: map[string][]types.ActorEventBlock{
|
||||
"abc": {
|
||||
{
|
||||
Codec: 0x51,
|
||||
Value: []byte("data"),
|
||||
},
|
||||
},
|
||||
},
|
||||
MinEpoch: 2301220,
|
||||
MaxEpoch: 2301220,
|
||||
},
|
||||
Prefill: true,
|
||||
})
|
||||
}
|
||||
|
||||
func GetAPIType(name, pkg string) (i interface{}, t reflect.Type, permStruct []reflect.Type) {
|
||||
|
@ -1626,6 +1626,21 @@ func (mr *MockFullNodeMockRecorder) GasEstimateMessageGas(arg0, arg1, arg2, arg3
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GasEstimateMessageGas", reflect.TypeOf((*MockFullNode)(nil).GasEstimateMessageGas), arg0, arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
// GetActorEvents mocks base method.
|
||||
func (m *MockFullNode) GetActorEvents(arg0 context.Context, arg1 *types.ActorEventFilter) ([]*types.ActorEvent, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetActorEvents", arg0, arg1)
|
||||
ret0, _ := ret[0].([]*types.ActorEvent)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetActorEvents indicates an expected call of GetActorEvents.
|
||||
func (mr *MockFullNodeMockRecorder) GetActorEvents(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActorEvents", reflect.TypeOf((*MockFullNode)(nil).GetActorEvents), arg0, arg1)
|
||||
}
|
||||
|
||||
// ID mocks base method.
|
||||
func (m *MockFullNode) ID(arg0 context.Context) (peer.ID, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@ -3968,6 +3983,21 @@ func (mr *MockFullNodeMockRecorder) StateWaitMsg(arg0, arg1, arg2, arg3, arg4 in
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateWaitMsg", reflect.TypeOf((*MockFullNode)(nil).StateWaitMsg), arg0, arg1, arg2, arg3, arg4)
|
||||
}
|
||||
|
||||
// SubscribeActorEvents mocks base method.
|
||||
func (m *MockFullNode) SubscribeActorEvents(arg0 context.Context, arg1 *types.SubActorEventFilter) (<-chan *types.ActorEvent, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SubscribeActorEvents", arg0, arg1)
|
||||
ret0, _ := ret[0].(<-chan *types.ActorEvent)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// SubscribeActorEvents indicates an expected call of SubscribeActorEvents.
|
||||
func (mr *MockFullNodeMockRecorder) SubscribeActorEvents(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubscribeActorEvents", reflect.TypeOf((*MockFullNode)(nil).SubscribeActorEvents), arg0, arg1)
|
||||
}
|
||||
|
||||
// SyncCheckBad mocks base method.
|
||||
func (m *MockFullNode) SyncCheckBad(arg0 context.Context, arg1 cid.Cid) (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -335,6 +335,8 @@ type FullNodeMethods struct {
|
||||
|
||||
GasEstimateMessageGas func(p0 context.Context, p1 *types.Message, p2 *MessageSendSpec, p3 types.TipSetKey) (*types.Message, error) `perm:"read"`
|
||||
|
||||
GetActorEvents func(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) `perm:"read"`
|
||||
|
||||
MarketAddBalance func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt) (cid.Cid, error) `perm:"sign"`
|
||||
|
||||
MarketGetReserved func(p0 context.Context, p1 address.Address) (types.BigInt, error) `perm:"sign"`
|
||||
@ -589,6 +591,8 @@ type FullNodeMethods struct {
|
||||
|
||||
StateWaitMsg func(p0 context.Context, p1 cid.Cid, p2 uint64, p3 abi.ChainEpoch, p4 bool) (*MsgLookup, error) `perm:"read"`
|
||||
|
||||
SubscribeActorEvents func(p0 context.Context, p1 *types.SubActorEventFilter) (<-chan *types.ActorEvent, error) `perm:"read"`
|
||||
|
||||
SyncCheckBad func(p0 context.Context, p1 cid.Cid) (string, error) `perm:"read"`
|
||||
|
||||
SyncCheckpoint func(p0 context.Context, p1 types.TipSetKey) error `perm:"admin"`
|
||||
@ -755,6 +759,8 @@ type GatewayMethods struct {
|
||||
|
||||
GasEstimateMessageGas func(p0 context.Context, p1 *types.Message, p2 *MessageSendSpec, p3 types.TipSetKey) (*types.Message, error) ``
|
||||
|
||||
GetActorEvents func(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) ``
|
||||
|
||||
MinerGetBaseInfo func(p0 context.Context, p1 address.Address, p2 abi.ChainEpoch, p3 types.TipSetKey) (*MiningBaseInfo, error) ``
|
||||
|
||||
MpoolGetNonce func(p0 context.Context, p1 address.Address) (uint64, error) ``
|
||||
@ -829,6 +835,8 @@ type GatewayMethods struct {
|
||||
|
||||
StateWaitMsg func(p0 context.Context, p1 cid.Cid, p2 uint64, p3 abi.ChainEpoch, p4 bool) (*MsgLookup, error) ``
|
||||
|
||||
SubscribeActorEvents func(p0 context.Context, p1 *types.SubActorEventFilter) (<-chan *types.ActorEvent, error) ``
|
||||
|
||||
Version func(p0 context.Context) (APIVersion, error) ``
|
||||
|
||||
WalletBalance func(p0 context.Context, p1 address.Address) (types.BigInt, error) ``
|
||||
@ -2584,6 +2592,17 @@ func (s *FullNodeStub) GasEstimateMessageGas(p0 context.Context, p1 *types.Messa
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *FullNodeStruct) GetActorEvents(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) {
|
||||
if s.Internal.GetActorEvents == nil {
|
||||
return *new([]*types.ActorEvent), ErrNotSupported
|
||||
}
|
||||
return s.Internal.GetActorEvents(p0, p1)
|
||||
}
|
||||
|
||||
func (s *FullNodeStub) GetActorEvents(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) {
|
||||
return *new([]*types.ActorEvent), ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *FullNodeStruct) MarketAddBalance(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt) (cid.Cid, error) {
|
||||
if s.Internal.MarketAddBalance == nil {
|
||||
return *new(cid.Cid), ErrNotSupported
|
||||
@ -3981,6 +4000,17 @@ func (s *FullNodeStub) StateWaitMsg(p0 context.Context, p1 cid.Cid, p2 uint64, p
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *FullNodeStruct) SubscribeActorEvents(p0 context.Context, p1 *types.SubActorEventFilter) (<-chan *types.ActorEvent, error) {
|
||||
if s.Internal.SubscribeActorEvents == nil {
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
return s.Internal.SubscribeActorEvents(p0, p1)
|
||||
}
|
||||
|
||||
func (s *FullNodeStub) SubscribeActorEvents(p0 context.Context, p1 *types.SubActorEventFilter) (<-chan *types.ActorEvent, error) {
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *FullNodeStruct) SyncCheckBad(p0 context.Context, p1 cid.Cid) (string, error) {
|
||||
if s.Internal.SyncCheckBad == nil {
|
||||
return "", ErrNotSupported
|
||||
@ -4828,6 +4858,17 @@ func (s *GatewayStub) GasEstimateMessageGas(p0 context.Context, p1 *types.Messag
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *GatewayStruct) GetActorEvents(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) {
|
||||
if s.Internal.GetActorEvents == nil {
|
||||
return *new([]*types.ActorEvent), ErrNotSupported
|
||||
}
|
||||
return s.Internal.GetActorEvents(p0, p1)
|
||||
}
|
||||
|
||||
func (s *GatewayStub) GetActorEvents(p0 context.Context, p1 *types.ActorEventFilter) ([]*types.ActorEvent, error) {
|
||||
return *new([]*types.ActorEvent), ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *GatewayStruct) MinerGetBaseInfo(p0 context.Context, p1 address.Address, p2 abi.ChainEpoch, p3 types.TipSetKey) (*MiningBaseInfo, error) {
|
||||
if s.Internal.MinerGetBaseInfo == nil {
|
||||
return nil, ErrNotSupported
|
||||
@ -5235,6 +5276,17 @@ func (s *GatewayStub) StateWaitMsg(p0 context.Context, p1 cid.Cid, p2 uint64, p3
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *GatewayStruct) SubscribeActorEvents(p0 context.Context, p1 *types.SubActorEventFilter) (<-chan *types.ActorEvent, error) {
|
||||
if s.Internal.SubscribeActorEvents == nil {
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
return s.Internal.SubscribeActorEvents(p0, p1)
|
||||
}
|
||||
|
||||
func (s *GatewayStub) SubscribeActorEvents(p0 context.Context, p1 *types.SubActorEventFilter) (<-chan *types.ActorEvent, error) {
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *GatewayStruct) Version(p0 context.Context) (APIVersion, error) {
|
||||
if s.Internal.Version == nil {
|
||||
return *new(APIVersion), ErrNotSupported
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -33,7 +33,8 @@ type EventFilter struct {
|
||||
maxHeight abi.ChainEpoch // maximum epoch to apply filter or -1 if no maximum
|
||||
tipsetCid cid.Cid
|
||||
addresses []address.Address // list of f4 actor addresses that are extpected to emit the event
|
||||
keys map[string][][]byte // map of key names to a list of alternate values that may match
|
||||
|
||||
keysWithCodec map[string][]types.ActorEventBlock // map of key names to a list of alternate values that may match
|
||||
maxResults int // maximum number of results to collect, 0 is unlimited
|
||||
|
||||
mu sync.Mutex
|
||||
@ -194,7 +195,7 @@ func (f *EventFilter) matchAddress(o address.Address) bool {
|
||||
}
|
||||
|
||||
func (f *EventFilter) matchKeys(ees []types.EventEntry) bool {
|
||||
if len(f.keys) == 0 {
|
||||
if len(f.keysWithCodec) == 0 {
|
||||
return true
|
||||
}
|
||||
// TODO: optimize this naive algorithm
|
||||
@ -216,19 +217,19 @@ func (f *EventFilter) matchKeys(ees []types.EventEntry) bool {
|
||||
continue
|
||||
}
|
||||
|
||||
wantlist, ok := f.keys[keyname]
|
||||
wantlist, ok := f.keysWithCodec[keyname]
|
||||
if !ok || len(wantlist) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, w := range wantlist {
|
||||
if bytes.Equal(w, ee.Value) {
|
||||
if bytes.Equal(w.Value, ee.Value) && w.Codec == ee.Codec {
|
||||
matched[keyname] = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(matched) == len(f.keys) {
|
||||
if len(matched) == len(f.keysWithCodec) {
|
||||
// all keys have been matched
|
||||
return true
|
||||
}
|
||||
@ -362,7 +363,8 @@ func (m *EventFilterManager) Revert(ctx context.Context, from, to *types.TipSet)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *EventFilterManager) Install(ctx context.Context, minHeight, maxHeight abi.ChainEpoch, tipsetCid cid.Cid, addresses []address.Address, keys map[string][][]byte) (*EventFilter, error) {
|
||||
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) {
|
||||
m.mu.Lock()
|
||||
currentHeight := m.currentHeight
|
||||
m.mu.Unlock()
|
||||
@ -382,13 +384,13 @@ func (m *EventFilterManager) Install(ctx context.Context, minHeight, maxHeight a
|
||||
maxHeight: maxHeight,
|
||||
tipsetCid: tipsetCid,
|
||||
addresses: addresses,
|
||||
keys: keys,
|
||||
keysWithCodec: keysWithCodec,
|
||||
maxResults: m.MaxFilterResults,
|
||||
}
|
||||
|
||||
if m.EventIndex != nil && minHeight != -1 && minHeight < currentHeight {
|
||||
// Filter needs historic events
|
||||
if err := m.EventIndex.PrefillFilter(ctx, f, true); err != nil {
|
||||
if err := m.EventIndex.PrefillFilter(ctx, f, excludeReverted); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,19 @@ import (
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
func keysToKeysWithCodec(keys map[string][][]byte) map[string][]types.ActorEventBlock {
|
||||
keysWithCodec := make(map[string][]types.ActorEventBlock)
|
||||
for k, v := range keys {
|
||||
for _, vv := range v {
|
||||
keysWithCodec[k] = append(keysWithCodec[k], types.ActorEventBlock{
|
||||
Codec: cid.Raw,
|
||||
Value: vv,
|
||||
})
|
||||
}
|
||||
}
|
||||
return keysWithCodec
|
||||
}
|
||||
|
||||
func TestEventFilterCollectEvents(t *testing.T) {
|
||||
rng := pseudo.New(pseudo.NewSource(299792458))
|
||||
a1 := randomF4Addr(t, rng)
|
||||
@ -139,11 +152,11 @@ func TestEventFilterCollectEvents(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("approval"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: oneCollectedEvent,
|
||||
@ -153,13 +166,13 @@ func TestEventFilterCollectEvents(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("cancel"),
|
||||
[]byte("propose"),
|
||||
[]byte("approval"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: oneCollectedEvent,
|
||||
@ -169,12 +182,12 @@ func TestEventFilterCollectEvents(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("cancel"),
|
||||
[]byte("propose"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: noCollectedEvents,
|
||||
@ -184,11 +197,11 @@ func TestEventFilterCollectEvents(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"method": {
|
||||
[]byte("approval"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: noCollectedEvents,
|
||||
@ -198,14 +211,14 @@ func TestEventFilterCollectEvents(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("approval"),
|
||||
},
|
||||
"signer": {
|
||||
[]byte("addr1"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: oneCollectedEvent,
|
||||
@ -215,14 +228,14 @@ func TestEventFilterCollectEvents(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("approval"),
|
||||
},
|
||||
"approver": {
|
||||
[]byte("addr1"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: noCollectedEvents,
|
||||
@ -232,14 +245,14 @@ func TestEventFilterCollectEvents(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("approval"),
|
||||
},
|
||||
"signer": {
|
||||
[]byte("addr2"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: noCollectedEvents,
|
||||
@ -249,11 +262,11 @@ func TestEventFilterCollectEvents(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"amount": {
|
||||
[]byte("2988181"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: noCollectedEvents,
|
||||
|
@ -514,9 +514,9 @@ func (ei *EventIndex) PrefillFilter(ctx context.Context, f *EventFilter, exclude
|
||||
clauses = append(clauses, "("+strings.Join(subclauses, " OR ")+")")
|
||||
}
|
||||
|
||||
if len(f.keys) > 0 {
|
||||
if len(f.keysWithCodec) > 0 {
|
||||
join := 0
|
||||
for key, vals := range f.keys {
|
||||
for key, vals := range f.keysWithCodec {
|
||||
if len(vals) > 0 {
|
||||
join++
|
||||
joinAlias := fmt.Sprintf("ee%d", join)
|
||||
@ -525,8 +525,8 @@ func (ei *EventIndex) PrefillFilter(ctx context.Context, f *EventFilter, exclude
|
||||
values = append(values, key)
|
||||
subclauses := []string{}
|
||||
for _, val := range vals {
|
||||
subclauses = append(subclauses, fmt.Sprintf("%s.value=?", joinAlias))
|
||||
values = append(values, val)
|
||||
subclauses = append(subclauses, fmt.Sprintf("(%s.value=? AND %[1]s.codec=?)", joinAlias))
|
||||
values = append(values, val.Value, val.Codec)
|
||||
}
|
||||
clauses = append(clauses, "("+strings.Join(subclauses, " OR ")+")")
|
||||
}
|
||||
|
@ -148,11 +148,11 @@ func TestEventIndexPrefillFilter(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("approval"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: oneCollectedEvent,
|
||||
@ -162,13 +162,13 @@ func TestEventIndexPrefillFilter(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("cancel"),
|
||||
[]byte("propose"),
|
||||
[]byte("approval"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: oneCollectedEvent,
|
||||
@ -178,12 +178,12 @@ func TestEventIndexPrefillFilter(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("cancel"),
|
||||
[]byte("propose"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: noCollectedEvents,
|
||||
@ -193,11 +193,11 @@ func TestEventIndexPrefillFilter(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"method": {
|
||||
[]byte("approval"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: noCollectedEvents,
|
||||
@ -207,14 +207,14 @@ func TestEventIndexPrefillFilter(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("approval"),
|
||||
},
|
||||
"signer": {
|
||||
[]byte("addr1"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: oneCollectedEvent,
|
||||
@ -224,14 +224,14 @@ func TestEventIndexPrefillFilter(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("approval"),
|
||||
},
|
||||
"approver": {
|
||||
[]byte("addr1"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: noCollectedEvents,
|
||||
@ -241,14 +241,14 @@ func TestEventIndexPrefillFilter(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("approval"),
|
||||
},
|
||||
"signer": {
|
||||
[]byte("addr2"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: noCollectedEvents,
|
||||
@ -258,11 +258,11 @@ func TestEventIndexPrefillFilter(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"amount": {
|
||||
[]byte("2988181"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: noCollectedEvents,
|
||||
@ -495,11 +495,11 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("approval"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: twoCollectedEvent,
|
||||
@ -509,13 +509,13 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("cancel"),
|
||||
[]byte("propose"),
|
||||
[]byte("approval"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: twoCollectedEvent,
|
||||
@ -525,12 +525,12 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("cancel"),
|
||||
[]byte("propose"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: noCollectedEvents,
|
||||
@ -540,11 +540,11 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"method": {
|
||||
[]byte("approval"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: noCollectedEvents,
|
||||
@ -554,14 +554,14 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("approval"),
|
||||
},
|
||||
"signer": {
|
||||
[]byte("addr1"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: oneCollectedEvent,
|
||||
@ -571,14 +571,14 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("approval"),
|
||||
},
|
||||
"signer": {
|
||||
[]byte("addr2"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: revertedEvents14000,
|
||||
want: oneCollectedRevertedEvent,
|
||||
@ -588,14 +588,14 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("approval"),
|
||||
},
|
||||
"approver": {
|
||||
[]byte("addr1"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: noCollectedEvents,
|
||||
@ -605,14 +605,14 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("approval"),
|
||||
},
|
||||
"signer": {
|
||||
[]byte("addr3"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: noCollectedEvents,
|
||||
@ -622,11 +622,11 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"amount": {
|
||||
[]byte("2988181"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: noCollectedEvents,
|
||||
@ -636,11 +636,11 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"amount": {
|
||||
[]byte("2988182"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: noCollectedEvents,
|
||||
@ -735,11 +735,11 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("approval"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: oneCollectedEvent,
|
||||
@ -749,13 +749,13 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("cancel"),
|
||||
[]byte("propose"),
|
||||
[]byte("approval"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: oneCollectedEvent,
|
||||
@ -765,12 +765,12 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("cancel"),
|
||||
[]byte("propose"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: noCollectedEvents,
|
||||
@ -780,11 +780,11 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"method": {
|
||||
[]byte("approval"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: noCollectedEvents,
|
||||
@ -794,14 +794,14 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("approval"),
|
||||
},
|
||||
"signer": {
|
||||
[]byte("addr1"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: oneCollectedEvent,
|
||||
@ -811,14 +811,14 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("approval"),
|
||||
},
|
||||
"approver": {
|
||||
[]byte("addr1"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: noCollectedEvents,
|
||||
@ -828,14 +828,14 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("approval"),
|
||||
},
|
||||
"signer": {
|
||||
[]byte("addr2"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: noCollectedEvents,
|
||||
@ -845,14 +845,14 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"type": {
|
||||
[]byte("approval"),
|
||||
},
|
||||
"signer": {
|
||||
[]byte("addr3"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: noCollectedEvents,
|
||||
@ -862,11 +862,11 @@ func TestEventIndexPrefillFilterExcludeReverted(t *testing.T) {
|
||||
filter: &EventFilter{
|
||||
minHeight: -1,
|
||||
maxHeight: -1,
|
||||
keys: map[string][][]byte{
|
||||
keysWithCodec: keysToKeysWithCodec(map[string][][]byte{
|
||||
"amount": {
|
||||
[]byte("2988181"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
te: events14000,
|
||||
want: noCollectedEvents,
|
||||
|
47
chain/types/actor_event.go
Normal file
47
chain/types/actor_event.go
Normal file
@ -0,0 +1,47 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/ipfs/go-cid"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
)
|
||||
|
||||
type ActorEventBlock struct {
|
||||
// what value codec does client want to match on ?
|
||||
Codec uint64 `json:"codec"`
|
||||
// data associated with the "event key"
|
||||
Value []byte `json:"value"`
|
||||
}
|
||||
|
||||
type SubActorEventFilter struct {
|
||||
Filter ActorEventFilter `json:"filter"`
|
||||
Prefill bool `json:"prefill"`
|
||||
}
|
||||
|
||||
type ActorEventFilter struct {
|
||||
// Matches events from one of these actors, or any actor if empty.
|
||||
// TODO: Should we also allow Eth addresses here?
|
||||
// For now, this MUST be a Filecoin address.
|
||||
Addresses []address.Address `json:"address"`
|
||||
|
||||
// Matches events with the specified key/values, or all events if empty.
|
||||
// If the `Blocks` slice is empty, matches on the key only.
|
||||
Fields map[string][]ActorEventBlock `json:"fields"`
|
||||
|
||||
// Epoch based filtering ?
|
||||
// Start epoch for the filter; -1 means no minimum
|
||||
MinEpoch abi.ChainEpoch `json:"minEpoch,omitempty"`
|
||||
|
||||
// End epoch for the filter; -1 means no maximum
|
||||
MaxEpoch abi.ChainEpoch `json:"maxEpoch,omitempty"`
|
||||
}
|
||||
|
||||
type ActorEvent struct {
|
||||
Entries []EventEntry
|
||||
EmitterAddr address.Address // f4 address of emitter
|
||||
Reverted bool
|
||||
Height abi.ChainEpoch
|
||||
TipSetKey cid.Cid // tipset that contained the message
|
||||
MsgCid cid.Cid // cid of message that produced event
|
||||
}
|
@ -115,6 +115,8 @@
|
||||
* [GasEstimateGasLimit](#GasEstimateGasLimit)
|
||||
* [GasEstimateGasPremium](#GasEstimateGasPremium)
|
||||
* [GasEstimateMessageGas](#GasEstimateMessageGas)
|
||||
* [Get](#Get)
|
||||
* [GetActorEvents](#GetActorEvents)
|
||||
* [I](#I)
|
||||
* [ID](#ID)
|
||||
* [Log](#Log)
|
||||
@ -282,6 +284,8 @@
|
||||
* [StateVerifiedRegistryRootKey](#StateVerifiedRegistryRootKey)
|
||||
* [StateVerifierStatus](#StateVerifierStatus)
|
||||
* [StateWaitMsg](#StateWaitMsg)
|
||||
* [Subscribe](#Subscribe)
|
||||
* [SubscribeActorEvents](#SubscribeActorEvents)
|
||||
* [Sync](#Sync)
|
||||
* [SyncCheckBad](#SyncCheckBad)
|
||||
* [SyncCheckpoint](#SyncCheckpoint)
|
||||
@ -3382,6 +3386,62 @@ Response:
|
||||
}
|
||||
```
|
||||
|
||||
## Get
|
||||
|
||||
|
||||
### GetActorEvents
|
||||
GetActorEvents returns all FVM and built-in Actor events that match the given filter.
|
||||
This is a request/response API.
|
||||
|
||||
|
||||
Perms: read
|
||||
|
||||
Inputs:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"address": [
|
||||
"f01234"
|
||||
],
|
||||
"fields": {
|
||||
"abc": [
|
||||
{
|
||||
"codec": 81,
|
||||
"value": "ZGF0YQ=="
|
||||
}
|
||||
]
|
||||
},
|
||||
"minEpoch": 2301220,
|
||||
"maxEpoch": 2301220
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"Entries": [
|
||||
{
|
||||
"Flags": 7,
|
||||
"Key": "string value",
|
||||
"Codec": 42,
|
||||
"Value": "Ynl0ZSBhcnJheQ=="
|
||||
}
|
||||
],
|
||||
"EmitterAddr": "f01234",
|
||||
"Reverted": true,
|
||||
"Height": 10101,
|
||||
"TipSetKey": {
|
||||
"/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4"
|
||||
},
|
||||
"MsgCid": {
|
||||
"/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## I
|
||||
|
||||
|
||||
@ -8758,6 +8818,67 @@ Response:
|
||||
}
|
||||
```
|
||||
|
||||
## Subscribe
|
||||
|
||||
|
||||
### SubscribeActorEvents
|
||||
SubscribeActorEvents returns a long-lived stream of all FVM and built-in Actor events that match the given filter.
|
||||
Events that match the given filter are written to the stream in real-time as they are emitted from the FVM.
|
||||
The response stream is closed when the client disconnects or if there is an error while writing an event to the stream.
|
||||
This API also allows clients to read all historical events matching the given filter before
|
||||
any real-time events are written to the response stream.
|
||||
NOTE: THIS API IS ONLY SUPPORTED OVER WEBSOCKETS FOR NOW
|
||||
|
||||
|
||||
Perms: read
|
||||
|
||||
Inputs:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"filter": {
|
||||
"address": [
|
||||
"f01234"
|
||||
],
|
||||
"fields": {
|
||||
"abc": [
|
||||
{
|
||||
"codec": 81,
|
||||
"value": "ZGF0YQ=="
|
||||
}
|
||||
]
|
||||
},
|
||||
"minEpoch": 2301220,
|
||||
"maxEpoch": 2301220
|
||||
},
|
||||
"prefill": true
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"Entries": [
|
||||
{
|
||||
"Flags": 7,
|
||||
"Key": "string value",
|
||||
"Codec": 42,
|
||||
"Value": "Ynl0ZSBhcnJheQ=="
|
||||
}
|
||||
],
|
||||
"EmitterAddr": "f01234",
|
||||
"Reverted": true,
|
||||
"Height": 10101,
|
||||
"TipSetKey": {
|
||||
"/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4"
|
||||
},
|
||||
"MsgCid": {
|
||||
"/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Sync
|
||||
The Sync method group contains methods for interacting with and
|
||||
observing the lotus sync service.
|
||||
|
@ -330,6 +330,10 @@
|
||||
# env var: LOTUS_FEVM_ENABLEETHRPC
|
||||
#EnableEthRPC = false
|
||||
|
||||
# type: bool
|
||||
# env var: LOTUS_FEVM_ENABLEACTOREVENTSAPI
|
||||
#EnableActorEventsAPI = false
|
||||
|
||||
# EthTxHashMappingLifetimeDays the transaction hash lookup database will delete mappings that have been stored for more than x days
|
||||
# Set to 0 to keep all mappings
|
||||
#
|
||||
|
@ -146,6 +146,9 @@ type TargetAPI interface {
|
||||
Web3ClientVersion(ctx context.Context) (string, error)
|
||||
EthTraceBlock(ctx context.Context, blkNum string) ([]*ethtypes.EthTraceBlock, error)
|
||||
EthTraceReplayBlockTransactions(ctx context.Context, blkNum string, traceTypes []string) ([]*ethtypes.EthTraceReplayBlockTransaction, error)
|
||||
|
||||
GetActorEvents(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error)
|
||||
SubscribeActorEvents(ctx context.Context, filter *types.SubActorEventFilter) (<-chan *types.ActorEvent, error)
|
||||
}
|
||||
|
||||
var _ TargetAPI = *new(api.FullNode) // gateway depends on latest
|
||||
|
@ -437,6 +437,20 @@ func (gw *Node) StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64
|
||||
return gw.target.StateWaitMsg(ctx, msg, confidence, limit, allowReplaced)
|
||||
}
|
||||
|
||||
func (gw *Node) GetActorEvents(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error) {
|
||||
if err := gw.limit(ctx, stateRateLimitTokens); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gw.target.GetActorEvents(ctx, filter)
|
||||
}
|
||||
|
||||
func (gw *Node) SubscribeActorEvents(ctx context.Context, filter *types.SubActorEventFilter) (<-chan *types.ActorEvent, error) {
|
||||
if err := gw.limit(ctx, stateRateLimitTokens); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gw.target.SubscribeActorEvents(ctx, filter)
|
||||
}
|
||||
|
||||
func (gw *Node) StateReadState(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*api.ActorState, error) {
|
||||
if err := gw.limit(ctx, stateRateLimitTokens); err != nil {
|
||||
return nil, err
|
||||
|
94
itests/actor_events_filter_test.go
Normal file
94
itests/actor_events_filter_test.go
Normal file
@ -0,0 +1,94 @@
|
||||
package itests
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/itests/kit"
|
||||
)
|
||||
|
||||
func TestGetActorEvents(t *testing.T) {
|
||||
t.Skip("skipping for now")
|
||||
//require := require.New(t)
|
||||
kit.QuietAllLogsExcept("events", "messagepool")
|
||||
|
||||
blockTime := 100 * time.Millisecond
|
||||
|
||||
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
|
||||
ens.InterconnectAll().BeginMining(blockTime)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
|
||||
// Set up the test fixture with a standard list of invocations
|
||||
contract1, contract2, invocations := prepareEventMatrixInvocations(ctx, t, client)
|
||||
fmt.Printf("contract1:%s; contract2:%s\n", contract1, contract2)
|
||||
|
||||
cf1, err := contract1.ToFilecoinAddress()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
cf2, err := contract2.ToFilecoinAddress()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("contract1 f4 is:%s; contract2 f4 is:%s\n", cf1.String(), cf2.String())
|
||||
|
||||
testCases := getCombinationFilterTestCases(contract1, contract2, "0x0")
|
||||
|
||||
messages := invokeAndWaitUntilAllOnChain(t, client, invocations)
|
||||
|
||||
// f410fiy2dwcbbvc5c6xwwrhlwgi2dby4rzgamxllpgva
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc // appease the lint despot
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
||||
res, err := client.EthGetLogs(ctx, tc.spec)
|
||||
require.NoError(t, err)
|
||||
|
||||
/*ch, _ := client.SubscribeActorEvents(ctx, &types.SubActorEventFilter{
|
||||
Prefill: true,
|
||||
ActorEventFilter: types.ActorEventFilter{
|
||||
MinEpoch: 0,
|
||||
MaxEpoch: 1000,
|
||||
},
|
||||
})
|
||||
|
||||
for i := range ch {
|
||||
fmt.Println("Hello Chan", i.Entries[0].Key, i.Entries[0].Codec, i.EmitterAddr.String())
|
||||
}*/
|
||||
|
||||
res2, _ := client.GetActorEvents(ctx, &types.ActorEventFilter{
|
||||
MinEpoch: 0,
|
||||
MaxEpoch: -1,
|
||||
Addresses: []address.Address{cf2},
|
||||
//EthAddresses: []ethtypes.EthAddress{
|
||||
// contract1,
|
||||
//},
|
||||
})
|
||||
for _, res := range res2 {
|
||||
res := res
|
||||
fmt.Println("Emitter Address is", res.EmitterAddr.String())
|
||||
for _, entry := range res.Entries {
|
||||
fmt.Println("Hello", entry.Key, entry.Codec, string(entry.Value))
|
||||
}
|
||||
|
||||
}
|
||||
fmt.Println("Hello", res2[0].Entries[0].Key, res2[0].Entries[0].Codec, res2[0].EmitterAddr.String())
|
||||
|
||||
elogs, err := parseEthLogsFromFilterResult(res)
|
||||
require.NoError(t, err)
|
||||
AssertEthLogs(t, elogs, tc.expected, messages)
|
||||
})
|
||||
}
|
||||
}
|
@ -63,6 +63,7 @@ var DefaultNodeOpts = nodeOpts{
|
||||
// test defaults
|
||||
|
||||
cfg.Fevm.EnableEthRPC = true
|
||||
cfg.Fevm.EnableActorEventsAPI = true
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"github.com/filecoin-project/lotus/chain/consensus"
|
||||
"github.com/filecoin-project/lotus/chain/consensus/filcns"
|
||||
"github.com/filecoin-project/lotus/chain/events"
|
||||
"github.com/filecoin-project/lotus/chain/events/filter"
|
||||
"github.com/filecoin-project/lotus/chain/exchange"
|
||||
"github.com/filecoin-project/lotus/chain/gen/slashfilter"
|
||||
"github.com/filecoin-project/lotus/chain/index"
|
||||
@ -155,6 +156,7 @@ var ChainNode = Options(
|
||||
Override(new(stmgr.StateManagerAPI), rpcstmgr.NewRPCStateManager),
|
||||
Override(new(full.EthModuleAPI), From(new(api.Gateway))),
|
||||
Override(new(full.EthEventAPI), From(new(api.Gateway))),
|
||||
Override(new(full.ActorEventAPI), From(new(api.Gateway))),
|
||||
),
|
||||
|
||||
// Full node API / service startup
|
||||
@ -265,6 +267,8 @@ func ConfigFullNode(c interface{}) Option {
|
||||
// Actor event filtering support
|
||||
Override(new(events.EventAPI), From(new(modules.EventAPI))),
|
||||
|
||||
Override(new(*filter.EventFilterManager), modules.EventFilterManager(cfg.Fevm)),
|
||||
|
||||
// in lite-mode Eth api is provided by gateway
|
||||
ApplyIf(isFullNode,
|
||||
If(cfg.Fevm.EnableEthRPC,
|
||||
@ -277,6 +281,15 @@ func ConfigFullNode(c interface{}) Option {
|
||||
),
|
||||
),
|
||||
|
||||
ApplyIf(isFullNode,
|
||||
If(cfg.Fevm.EnableActorEventsAPI,
|
||||
Override(new(full.ActorEventAPI), modules.ActorEventAPI(cfg.Fevm)),
|
||||
),
|
||||
If(!cfg.Fevm.EnableActorEventsAPI,
|
||||
Override(new(full.ActorEventAPI), &full.ActorEventDummy{}),
|
||||
),
|
||||
),
|
||||
|
||||
// enable message index for full node when configured by the user, otherwise use dummy.
|
||||
If(cfg.Index.EnableMsgIndex, Override(new(index.MsgIndex), modules.MsgIndex)),
|
||||
If(!cfg.Index.EnableMsgIndex, Override(new(index.MsgIndex), modules.DummyMsgIndex)),
|
||||
|
@ -109,6 +109,7 @@ func DefaultFullNode() *FullNode {
|
||||
Cluster: *DefaultUserRaftConfig(),
|
||||
Fevm: FevmConfig{
|
||||
EnableEthRPC: false,
|
||||
EnableActorEventsAPI: false,
|
||||
EthTxHashMappingLifetimeDays: 0,
|
||||
Events: Events{
|
||||
DisableRealTimeFilterAPI: false,
|
||||
|
@ -455,6 +455,12 @@ rewards. This address should have adequate funds to cover gas fees.`,
|
||||
Comment: `EnableEthRPC enables eth_ rpc, and enables storing a mapping of eth transaction hashes to filecoin message Cids.
|
||||
This will also enable the RealTimeFilterAPI and HistoricFilterAPI by default, but they can be disabled by config options above.`,
|
||||
},
|
||||
{
|
||||
Name: "EnableActorEventsAPI",
|
||||
Type: "bool",
|
||||
|
||||
Comment: ``,
|
||||
},
|
||||
{
|
||||
Name: "EthTxHashMappingLifetimeDays",
|
||||
Type: "int",
|
||||
|
@ -786,6 +786,8 @@ type FevmConfig struct {
|
||||
// This will also enable the RealTimeFilterAPI and HistoricFilterAPI by default, but they can be disabled by config options above.
|
||||
EnableEthRPC bool
|
||||
|
||||
EnableActorEventsAPI bool
|
||||
|
||||
// EthTxHashMappingLifetimeDays the transaction hash lookup database will delete mappings that have been stored for more than x days
|
||||
// Set to 0 to keep all mappings
|
||||
EthTxHashMappingLifetimeDays int
|
||||
|
@ -36,6 +36,7 @@ type FullNodeAPI struct {
|
||||
full.SyncAPI
|
||||
full.RaftAPI
|
||||
full.EthAPI
|
||||
full.ActorEventsAPI
|
||||
|
||||
DS dtypes.MetadataDS
|
||||
NetworkName dtypes.NetworkName
|
||||
|
171
node/impl/full/actor_event.go
Normal file
171
node/impl/full/actor_event.go
Normal file
@ -0,0 +1,171 @@
|
||||
package full
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"go.uber.org/fx"
|
||||
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/events/filter"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
type ActorEventAPI interface {
|
||||
GetActorEvents(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error)
|
||||
SubscribeActorEvents(ctx context.Context, filter *types.SubActorEventFilter) (<-chan *types.ActorEvent, error)
|
||||
}
|
||||
|
||||
var (
|
||||
_ ActorEventAPI = *new(api.FullNode)
|
||||
_ ActorEventAPI = *new(api.Gateway)
|
||||
)
|
||||
|
||||
type ActorEvent struct {
|
||||
EventFilterManager *filter.EventFilterManager
|
||||
MaxFilterHeightRange abi.ChainEpoch
|
||||
}
|
||||
|
||||
var _ ActorEventAPI = (*ActorEvent)(nil)
|
||||
|
||||
type ActorEventsAPI struct {
|
||||
fx.In
|
||||
ActorEventAPI
|
||||
}
|
||||
|
||||
func (a *ActorEvent) GetActorEvents(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error) {
|
||||
if a.EventFilterManager == nil {
|
||||
return nil, api.ErrNotSupported
|
||||
}
|
||||
|
||||
// Create a temporary filter
|
||||
f, err := a.EventFilterManager.Install(ctx, filter.MinEpoch, filter.MaxEpoch, cid.Undef, filter.Addresses, filter.Fields, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
evs, err := getCollected(ctx, f)
|
||||
_ = a.EventFilterManager.Remove(ctx, f.ID())
|
||||
return evs, err
|
||||
}
|
||||
|
||||
func (a *ActorEvent) SubscribeActorEvents(ctx context.Context, f *types.SubActorEventFilter) (<-chan *types.ActorEvent, error) {
|
||||
if a.EventFilterManager == nil {
|
||||
return nil, api.ErrNotSupported
|
||||
}
|
||||
fm, err := a.EventFilterManager.Install(ctx, f.Filter.MinEpoch, f.Filter.MaxEpoch, cid.Undef, f.Filter.Addresses, f.Filter.Fields, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := make(chan *types.ActorEvent, 25)
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
// Tell the caller we're done
|
||||
close(out)
|
||||
|
||||
// Unsubscribe.
|
||||
fm.ClearSubChannel()
|
||||
_ = a.EventFilterManager.Remove(ctx, fm.ID())
|
||||
}()
|
||||
|
||||
if f.Prefill {
|
||||
evs, err := getCollected(ctx, fm)
|
||||
if err != nil {
|
||||
log.Errorf("failed to get collected events: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, ev := range evs {
|
||||
ev := ev
|
||||
select {
|
||||
case out <- ev:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
log.Errorf("closing event subscription due to slow reader")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
in := make(chan interface{}, 256)
|
||||
fm.SetSubChannel(in)
|
||||
|
||||
for {
|
||||
select {
|
||||
case val, ok := <-in:
|
||||
if !ok {
|
||||
// Shutting down.
|
||||
return
|
||||
}
|
||||
|
||||
ce, ok := val.(*filter.CollectedEvent)
|
||||
if !ok {
|
||||
log.Errorf("got unexpected value from event filter: %T", val)
|
||||
return
|
||||
}
|
||||
c, err := ce.TipSetKey.Cid()
|
||||
if err != nil {
|
||||
log.Errorf("failed to get tipset cid: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
ev := &types.ActorEvent{
|
||||
Entries: ce.Entries,
|
||||
EmitterAddr: ce.EmitterAddr,
|
||||
Reverted: ce.Reverted,
|
||||
Height: ce.Height,
|
||||
TipSetKey: c,
|
||||
MsgCid: ce.MsgCid,
|
||||
}
|
||||
|
||||
select {
|
||||
case out <- ev:
|
||||
default:
|
||||
log.Errorf("closing event subscription due to slow reader")
|
||||
return
|
||||
}
|
||||
if len(out) > 5 {
|
||||
log.Warnf("event subscription is slow, has %d buffered entries", len(out))
|
||||
}
|
||||
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func getCollected(ctx context.Context, f *filter.EventFilter) ([]*types.ActorEvent, error) {
|
||||
ces := f.TakeCollectedEvents(ctx)
|
||||
|
||||
var out []*types.ActorEvent
|
||||
|
||||
for _, e := range ces {
|
||||
e := e
|
||||
c, err := e.TipSetKey.Cid()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get tipset cid: %w", err)
|
||||
}
|
||||
|
||||
ev := &types.ActorEvent{
|
||||
Entries: e.Entries,
|
||||
EmitterAddr: e.EmitterAddr,
|
||||
Reverted: e.Reverted,
|
||||
Height: e.Height,
|
||||
TipSetKey: c,
|
||||
MsgCid: e.MsgCid,
|
||||
}
|
||||
|
||||
out = append(out, ev)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
@ -11,6 +11,7 @@ import (
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/types/ethtypes"
|
||||
)
|
||||
|
||||
@ -188,3 +189,17 @@ func (e *EthModuleDummy) EthTraceReplayBlockTransactions(ctx context.Context, bl
|
||||
|
||||
var _ EthModuleAPI = &EthModuleDummy{}
|
||||
var _ EthEventAPI = &EthModuleDummy{}
|
||||
|
||||
var ErrActorEventModuleDisabled = errors.New("module disabled, enable with Fevm.EnableActorEventsAPI")
|
||||
|
||||
type ActorEventDummy struct{}
|
||||
|
||||
func (a *ActorEventDummy) GetActorEvents(ctx context.Context, filter *types.ActorEventFilter) ([]*types.ActorEvent, error) {
|
||||
return nil, ErrActorEventModuleDisabled
|
||||
}
|
||||
|
||||
func (a *ActorEventDummy) SubscribeActorEvents(ctx context.Context, filter *types.SubActorEventFilter) (<-chan *types.ActorEvent, error) {
|
||||
return nil, ErrActorEventModuleDisabled
|
||||
}
|
||||
|
||||
var _ ActorEventAPI = &ActorEventDummy{}
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/multiformats/go-multicodec"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"go.uber.org/fx"
|
||||
"golang.org/x/xerrors"
|
||||
@ -1353,7 +1354,20 @@ func (e *EthEvent) installEthFilterSpec(ctx context.Context, filterSpec *ethtype
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return e.EventFilterManager.Install(ctx, minHeight, maxHeight, tipsetCid, addresses, keys)
|
||||
return e.EventFilterManager.Install(ctx, minHeight, maxHeight, tipsetCid, addresses, keysToKeysWithCodec(keys), true)
|
||||
}
|
||||
|
||||
func keysToKeysWithCodec(keys map[string][][]byte) map[string][]types.ActorEventBlock {
|
||||
keysWithCodec := make(map[string][]types.ActorEventBlock)
|
||||
for k, v := range keys {
|
||||
for _, vv := range v {
|
||||
keysWithCodec[k] = append(keysWithCodec[k], types.ActorEventBlock{
|
||||
Codec: uint64(multicodec.Raw),
|
||||
Value: vv,
|
||||
})
|
||||
}
|
||||
}
|
||||
return keysWithCodec
|
||||
}
|
||||
|
||||
func (e *EthEvent) EthNewFilter(ctx context.Context, filterSpec *ethtypes.EthFilterSpec) (ethtypes.EthFilterID, error) {
|
||||
@ -1527,7 +1541,7 @@ func (e *EthEvent) EthSubscribe(ctx context.Context, p jsonrpc.RawParams) (ethty
|
||||
}
|
||||
}
|
||||
|
||||
f, err := e.EventFilterManager.Install(ctx, -1, -1, cid.Undef, addresses, keys)
|
||||
f, err := e.EventFilterManager.Install(ctx, -1, -1, cid.Undef, addresses, keysToKeysWithCodec(keys), true)
|
||||
if err != nil {
|
||||
// clean up any previous filters added and stop the sub
|
||||
_, _ = e.EthUnsubscribe(ctx, sub.id)
|
||||
|
@ -33,8 +33,8 @@ type EventAPI struct {
|
||||
|
||||
var _ events.EventAPI = &EventAPI{}
|
||||
|
||||
func EthEventAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo, fx.Lifecycle, *store.ChainStore, *stmgr.StateManager, EventAPI, *messagepool.MessagePool, full.StateAPI, full.ChainAPI) (*full.EthEvent, error) {
|
||||
return func(mctx helpers.MetricsCtx, r repo.LockedRepo, lc fx.Lifecycle, cs *store.ChainStore, sm *stmgr.StateManager, evapi EventAPI, mp *messagepool.MessagePool, stateapi full.StateAPI, chainapi full.ChainAPI) (*full.EthEvent, error) {
|
||||
func EthEventAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo, fx.Lifecycle, *filter.EventFilterManager, *store.ChainStore, *stmgr.StateManager, EventAPI, *messagepool.MessagePool, full.StateAPI, full.ChainAPI) (*full.EthEvent, error) {
|
||||
return func(mctx helpers.MetricsCtx, r repo.LockedRepo, lc fx.Lifecycle, fm *filter.EventFilterManager, cs *store.ChainStore, sm *stmgr.StateManager, evapi EventAPI, mp *messagepool.MessagePool, stateapi full.StateAPI, chainapi full.ChainAPI) (*full.EthEvent, error) {
|
||||
ctx := helpers.LifecycleCtx(mctx, lc)
|
||||
|
||||
ee := &full.EthEvent{
|
||||
@ -64,6 +64,41 @@ func EthEventAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo
|
||||
},
|
||||
})
|
||||
|
||||
ee.TipSetFilterManager = &filter.TipSetFilterManager{
|
||||
MaxFilterResults: cfg.Events.MaxFilterResults,
|
||||
}
|
||||
ee.MemPoolFilterManager = &filter.MemPoolFilterManager{
|
||||
MaxFilterResults: cfg.Events.MaxFilterResults,
|
||||
}
|
||||
ee.EventFilterManager = fm
|
||||
|
||||
lc.Append(fx.Hook{
|
||||
OnStart: func(context.Context) error {
|
||||
ev, err := events.NewEvents(ctx, &evapi)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// ignore returned tipsets
|
||||
_ = ev.Observe(ee.TipSetFilterManager)
|
||||
|
||||
ch, err := mp.Updates(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go ee.MemPoolFilterManager.WaitForMpoolUpdates(ctx, ch)
|
||||
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
return ee, nil
|
||||
}
|
||||
}
|
||||
|
||||
func EventFilterManager(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo, fx.Lifecycle, *store.ChainStore, *stmgr.StateManager, EventAPI, full.ChainAPI) (*filter.EventFilterManager, error) {
|
||||
return func(mctx helpers.MetricsCtx, r repo.LockedRepo, lc fx.Lifecycle, cs *store.ChainStore, sm *stmgr.StateManager, evapi EventAPI, chainapi full.ChainAPI) (*filter.EventFilterManager, error) {
|
||||
ctx := helpers.LifecycleCtx(mctx, lc)
|
||||
|
||||
// Enable indexing of actor events
|
||||
var eventIndex *filter.EventIndex
|
||||
if !cfg.Events.DisableHistoricFilterAPI {
|
||||
@ -91,7 +126,7 @@ func EthEventAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo
|
||||
})
|
||||
}
|
||||
|
||||
ee.EventFilterManager = &filter.EventFilterManager{
|
||||
fm := &filter.EventFilterManager{
|
||||
ChainStore: cs,
|
||||
EventIndex: eventIndex, // will be nil unless EnableHistoricFilterAPI is true
|
||||
AddressResolver: func(ctx context.Context, emitter abi.ActorID, ts *types.TipSet) (address.Address, bool) {
|
||||
@ -111,6 +146,7 @@ func EthEventAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo
|
||||
return address.Undef, false
|
||||
}
|
||||
// we have an f4 address, make sure it's assigned by the EAM
|
||||
// What happens when we introduce events for built-in Actor events here ?
|
||||
if namespace, _, err := varint.FromUvarint(actor.Address.Payload()); err != nil || namespace != builtintypes.EthereumAddressManagerActorID {
|
||||
return address.Undef, false
|
||||
}
|
||||
@ -119,12 +155,6 @@ func EthEventAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo
|
||||
|
||||
MaxFilterResults: cfg.Events.MaxFilterResults,
|
||||
}
|
||||
ee.TipSetFilterManager = &filter.TipSetFilterManager{
|
||||
MaxFilterResults: cfg.Events.MaxFilterResults,
|
||||
}
|
||||
ee.MemPoolFilterManager = &filter.MemPoolFilterManager{
|
||||
MaxFilterResults: cfg.Events.MaxFilterResults,
|
||||
}
|
||||
|
||||
lc.Append(fx.Hook{
|
||||
OnStart: func(context.Context) error {
|
||||
@ -132,20 +162,28 @@ func EthEventAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// ignore returned tipsets
|
||||
_ = ev.Observe(ee.EventFilterManager)
|
||||
_ = ev.Observe(ee.TipSetFilterManager)
|
||||
|
||||
ch, err := mp.Updates(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go ee.MemPoolFilterManager.WaitForMpoolUpdates(ctx, ch)
|
||||
|
||||
_ = ev.Observe(fm)
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
return fm, nil
|
||||
}
|
||||
}
|
||||
|
||||
func ActorEventAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo, fx.Lifecycle, *filter.EventFilterManager, *store.ChainStore, *stmgr.StateManager, EventAPI, *messagepool.MessagePool, full.StateAPI, full.ChainAPI) (*full.ActorEvent, error) {
|
||||
return func(mctx helpers.MetricsCtx, r repo.LockedRepo, lc fx.Lifecycle, fm *filter.EventFilterManager, cs *store.ChainStore, sm *stmgr.StateManager, evapi EventAPI, mp *messagepool.MessagePool, stateapi full.StateAPI, chainapi full.ChainAPI) (*full.ActorEvent, error) {
|
||||
ee := &full.ActorEvent{
|
||||
MaxFilterHeightRange: abi.ChainEpoch(cfg.Events.MaxFilterHeightRange),
|
||||
}
|
||||
|
||||
if !cfg.EnableActorEventsAPI {
|
||||
// all Actor events functionality is disabled
|
||||
return ee, nil
|
||||
}
|
||||
|
||||
ee.EventFilterManager = fm
|
||||
|
||||
return ee, nil
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user