Built-in actor events first draft
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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{}
|
||||
|
||||
+16
-2
@@ -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)
|
||||
|
||||
+57
-19
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user