From e5ccf1915d2596cb2aed581900cec260e5fb5baa Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Fri, 8 Mar 2024 18:43:39 +1100 Subject: [PATCH] feat(config): move Fevm.Events->Events, implement soft deprecation Closes: https://github.com/filecoin-project/lotus/issues/11679 * Introduce a `moved:"To.New.Config"` tag which prints a stderr warning when you use one of these, but will move any set value to the new location if the new location isn't already set itself. * Look for `X is DEPRECATED` to hold certain fields back from documentation. * Use `toml:"omitempty"` to prevent the default config output from having these deprecated values. --- CHANGELOG.md | 9 +- documentation/en/default-lotus-config.toml | 103 ++++++++------- itests/kit/node_opts.go | 2 +- node/builder_chain.go | 6 +- node/config/cfgdocgen/gen.go | 5 + node/config/def.go | 16 +-- node/config/doc_gen.go | 28 ++-- node/config/load.go | 142 +++++++++++++++++++-- node/config/load_test.go | 75 +++++++++++ node/config/types.go | 45 +++++-- node/modules/actorevent.go | 30 ++--- 11 files changed, 343 insertions(+), 118 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0603065db..5195be38a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -136,7 +136,7 @@ Additionally, Filecoin is not Ethereum no matter how much we try to provide API/ [FIP-0049](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0049.md) introduced _Actor Events_ that can be emitted by user programmed actors. [FIP-0083](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0083.md) introduces new events emitted by the builtin Verified Registry, Miner and Market Actors. These new events for builtin actors are being activated with network version 22 to coincide with _Direct Data Onboarding_ as defined in [FIP-0076](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0076.md) which introduces additional flexibility for data onboarding. Sector, Deal and DataCap lifecycles can be tracked with these events, providing visibility and options for programmatic responses to changes in state. -Actor events are available on message receipts, but can now be retrieved from a node using the new `GetActorEvents` and `SubscribeActorEvents` methods. These methods allow for querying and subscribing to actor events, respectively. They depend on the Lotus node both collecting events (with `Fevm.Events.RealTimeFilterAPI` and `Fevm.Events.HistoricFilterAPI`) and being enabled with the new configuration option `Events.EnableActorEventsAPI`. Note that a Lotus node can only respond to requests for historic events that it retains in its event store. +Actor events are available on message receipts, but can now be retrieved from a node using the new `GetActorEvents` and `SubscribeActorEvents` methods. These methods allow for querying and subscribing to actor events, respectively. They depend on the Lotus node both collecting events (with `Events.RealTimeFilterAPI` and `Events.HistoricFilterAPI`) and being enabled with the new configuration option `Events.EnableActorEventsAPI`. Note that a Lotus node can only respond to requests for historic events that it retains in its event store. Both `GetActorEvents` and `SubscribeActorEvents` take a filter parameter which can optionally filter events on: @@ -147,7 +147,14 @@ Both `GetActorEvents` and `SubscribeActorEvents` take a filter parameter which c `GetActorEvents` provides a one-time query for actor events, while `SubscribeActorEvents` provides a long-lived connection (via websockets) to the Lotus node, allowing for real-time updates on actor events. The subscription can be cancelled by the client at any time. +### Events Configuration Changes + +All configuration options previously under `Fevm.Events` are now in the top-level `Events` section along with the new `Events.EnableActorEventsAPI` option mentioned above. If you have non-default options in `[Events]` under `[Fevm]` in your configuration file, please move them to the top-level `[Events]`. + +While `Fevm.Events.*` options are deprecated and replaced by `Events.*`, any existing custom values will be respected if their new form isn't set, but a warning will be printed to standard error upon startup. Support for these deprecated options will be removed in a future Lotus release, so please migrate your configuration promptly. + ### GetAllClaims and GetAllAlocations + Additionally the methods `GetAllAllocations` and `GetAllClaims` has been added to the Lotus API. These methods lists all the available allocations and claims available in the actor state. ### Lotus CLI diff --git a/documentation/en/default-lotus-config.toml b/documentation/en/default-lotus-config.toml index 15216514b..3f3a6b171 100644 --- a/documentation/en/default-lotus-config.toml +++ b/documentation/en/default-lotus-config.toml @@ -337,68 +337,67 @@ # env var: LOTUS_FEVM_ETHTXHASHMAPPINGLIFETIMEDAYS #EthTxHashMappingLifetimeDays = 0 - [Fevm.Events] - # DisableRealTimeFilterAPI will disable the RealTimeFilterAPI that can create and query filters for actor events as they are emitted. - # The API is enabled when EnableEthRPC or Events.EnableActorEventsAPI is true, but can be disabled selectively with this flag. - # - # type: bool - # env var: LOTUS_FEVM_EVENTS_DISABLEREALTIMEFILTERAPI - #DisableRealTimeFilterAPI = false - - # DisableHistoricFilterAPI will disable the HistoricFilterAPI that can create and query filters for actor events - # that occurred in the past. HistoricFilterAPI maintains a queryable index of events. - # The API is enabled when EnableEthRPC or Events.EnableActorEventsAPI is true, but can be disabled selectively with this flag. - # - # type: bool - # env var: LOTUS_FEVM_EVENTS_DISABLEHISTORICFILTERAPI - #DisableHistoricFilterAPI = false - - # FilterTTL specifies the time to live for actor event filters. Filters that haven't been accessed longer than - # this time become eligible for automatic deletion. - # - # type: Duration - # env var: LOTUS_FEVM_EVENTS_FILTERTTL - #FilterTTL = "24h0m0s" - - # MaxFilters specifies the maximum number of filters that may exist at any one time. - # - # type: int - # env var: LOTUS_FEVM_EVENTS_MAXFILTERS - #MaxFilters = 100 - - # MaxFilterResults specifies the maximum number of results that can be accumulated by an actor event filter. - # - # type: int - # env var: LOTUS_FEVM_EVENTS_MAXFILTERRESULTS - #MaxFilterResults = 10000 - - # MaxFilterHeightRange specifies the maximum range of heights that can be used in a filter (to avoid querying - # the entire chain) - # - # type: uint64 - # env var: LOTUS_FEVM_EVENTS_MAXFILTERHEIGHTRANGE - #MaxFilterHeightRange = 2880 - - # DatabasePath is the full path to a sqlite database that will be used to index actor events to - # support the historic filter APIs. If the database does not exist it will be created. The directory containing - # the database must already exist and be writeable. If a relative path is provided here, sqlite treats it as - # relative to the CWD (current working directory). - # - # type: string - # env var: LOTUS_FEVM_EVENTS_DATABASEPATH - #DatabasePath = "" - [Events] + # DisableRealTimeFilterAPI will disable the RealTimeFilterAPI that can create and query filters for actor events as they are emitted. + # The API is enabled when Fevm.EnableEthRPC or EnableActorEventsAPI is true, but can be disabled selectively with this flag. + # + # type: bool + # env var: LOTUS_EVENTS_DISABLEREALTIMEFILTERAPI + #DisableRealTimeFilterAPI = false + + # DisableHistoricFilterAPI will disable the HistoricFilterAPI that can create and query filters for actor events + # that occurred in the past. HistoricFilterAPI maintains a queryable index of events. + # The API is enabled when Fevm.EnableEthRPC or EnableActorEventsAPI is true, but can be disabled selectively with this flag. + # + # type: bool + # env var: LOTUS_EVENTS_DISABLEHISTORICFILTERAPI + #DisableHistoricFilterAPI = false + # EnableActorEventsAPI enables the Actor events API that enables clients to consume events # emitted by (smart contracts + built-in Actors). # This will also enable the RealTimeFilterAPI and HistoricFilterAPI by default, but they can be - # disabled by setting their respective Disable* options in Fevm.Events. + # disabled by setting their respective Disable* options. # # type: bool # env var: LOTUS_EVENTS_ENABLEACTOREVENTSAPI #EnableActorEventsAPI = false + # FilterTTL specifies the time to live for actor event filters. Filters that haven't been accessed longer than + # this time become eligible for automatic deletion. + # + # type: Duration + # env var: LOTUS_EVENTS_FILTERTTL + #FilterTTL = "24h0m0s" + + # MaxFilters specifies the maximum number of filters that may exist at any one time. + # + # type: int + # env var: LOTUS_EVENTS_MAXFILTERS + #MaxFilters = 100 + + # MaxFilterResults specifies the maximum number of results that can be accumulated by an actor event filter. + # + # type: int + # env var: LOTUS_EVENTS_MAXFILTERRESULTS + #MaxFilterResults = 10000 + + # MaxFilterHeightRange specifies the maximum range of heights that can be used in a filter (to avoid querying + # the entire chain) + # + # type: uint64 + # env var: LOTUS_EVENTS_MAXFILTERHEIGHTRANGE + #MaxFilterHeightRange = 2880 + + # DatabasePath is the full path to a sqlite database that will be used to index actor events to + # support the historic filter APIs. If the database does not exist it will be created. The directory containing + # the database must already exist and be writeable. If a relative path is provided here, sqlite treats it as + # relative to the CWD (current working directory). + # + # type: string + # env var: LOTUS_EVENTS_DATABASEPATH + #DatabasePath = "" + [Index] # EXPERIMENTAL FEATURE. USE WITH CAUTION diff --git a/itests/kit/node_opts.go b/itests/kit/node_opts.go index 09e789951..1f4f9f6a4 100644 --- a/itests/kit/node_opts.go +++ b/itests/kit/node_opts.go @@ -65,7 +65,7 @@ var DefaultNodeOpts = nodeOpts{ // test defaults cfg.Fevm.EnableEthRPC = true - cfg.Fevm.Events.MaxFilterHeightRange = math.MaxInt64 + cfg.Events.MaxFilterHeightRange = math.MaxInt64 cfg.Events.EnableActorEventsAPI = true return nil }, diff --git a/node/builder_chain.go b/node/builder_chain.go index 44d067f93..0e465ffe1 100644 --- a/node/builder_chain.go +++ b/node/builder_chain.go @@ -266,13 +266,13 @@ func ConfigFullNode(c interface{}) Option { // Actor event filtering support Override(new(events.EventHelperAPI), From(new(modules.EventHelperAPI))), - Override(new(*filter.EventFilterManager), modules.EventFilterManager(cfg.Fevm)), + Override(new(*filter.EventFilterManager), modules.EventFilterManager(cfg.Events)), // in lite-mode Eth api is provided by gateway ApplyIf(isFullNode, If(cfg.Fevm.EnableEthRPC, Override(new(full.EthModuleAPI), modules.EthModuleAPI(cfg.Fevm)), - Override(new(full.EthEventAPI), modules.EthEventHandler(cfg.Fevm)), + Override(new(full.EthEventAPI), modules.EthEventHandler(cfg.Events, cfg.Fevm.EnableEthRPC)), ), If(!cfg.Fevm.EnableEthRPC, Override(new(full.EthModuleAPI), &full.EthModuleDummy{}), @@ -282,7 +282,7 @@ func ConfigFullNode(c interface{}) Option { ApplyIf(isFullNode, If(cfg.Events.EnableActorEventsAPI, - Override(new(full.ActorEventAPI), modules.ActorEventHandler(cfg.Events.EnableActorEventsAPI, cfg.Fevm)), + Override(new(full.ActorEventAPI), modules.ActorEventHandler(cfg.Events)), ), If(!cfg.Events.EnableActorEventsAPI, Override(new(full.ActorEventAPI), &full.ActorEventDummy{}), diff --git a/node/config/cfgdocgen/gen.go b/node/config/cfgdocgen/gen.go index b13b7d799..6c7371a40 100644 --- a/node/config/cfgdocgen/gen.go +++ b/node/config/cfgdocgen/gen.go @@ -74,6 +74,11 @@ func run() error { name := f[0] typ := f[1] + if len(comment) > 0 && strings.HasPrefix(comment[0], fmt.Sprintf("%s is DEPRECATED", name)) { + // don't document deprecated fields + continue + } + out[currentType] = append(out[currentType], field{ Name: name, Type: typ, diff --git a/node/config/def.go b/node/config/def.go index 7a69293dd..7e06eba3a 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -110,17 +110,15 @@ func DefaultFullNode() *FullNode { Fevm: FevmConfig{ EnableEthRPC: false, EthTxHashMappingLifetimeDays: 0, - Events: Events{ - DisableRealTimeFilterAPI: false, - DisableHistoricFilterAPI: false, - FilterTTL: Duration(time.Hour * 24), - MaxFilters: 100, - MaxFilterResults: 10000, - MaxFilterHeightRange: 2880, // conservative limit of one day - }, }, Events: EventsConfig{ - EnableActorEventsAPI: false, + DisableRealTimeFilterAPI: false, + DisableHistoricFilterAPI: false, + EnableActorEventsAPI: false, + FilterTTL: Duration(time.Hour * 24), + MaxFilters: 100, + MaxFilterResults: 10000, + MaxFilterHeightRange: 2880, // conservative limit of one day }, } } diff --git a/node/config/doc_gen.go b/node/config/doc_gen.go index d56b0660e..929f15766 100644 --- a/node/config/doc_gen.go +++ b/node/config/doc_gen.go @@ -357,13 +357,13 @@ see https://lotus.filecoin.io/storage-providers/advanced-configurations/market/# Comment: ``, }, }, - "Events": { + "EventsConfig": { { Name: "DisableRealTimeFilterAPI", Type: "bool", Comment: `DisableRealTimeFilterAPI will disable the RealTimeFilterAPI that can create and query filters for actor events as they are emitted. -The API is enabled when EnableEthRPC or Events.EnableActorEventsAPI is true, but can be disabled selectively with this flag.`, +The API is enabled when Fevm.EnableEthRPC or EnableActorEventsAPI is true, but can be disabled selectively with this flag.`, }, { Name: "DisableHistoricFilterAPI", @@ -371,7 +371,16 @@ The API is enabled when EnableEthRPC or Events.EnableActorEventsAPI is true, but Comment: `DisableHistoricFilterAPI will disable the HistoricFilterAPI that can create and query filters for actor events that occurred in the past. HistoricFilterAPI maintains a queryable index of events. -The API is enabled when EnableEthRPC or Events.EnableActorEventsAPI is true, but can be disabled selectively with this flag.`, +The API is enabled when Fevm.EnableEthRPC or EnableActorEventsAPI is true, but can be disabled selectively with this flag.`, + }, + { + Name: "EnableActorEventsAPI", + Type: "bool", + + Comment: `EnableActorEventsAPI enables the Actor events API that enables clients to consume events +emitted by (smart contracts + built-in Actors). +This will also enable the RealTimeFilterAPI and HistoricFilterAPI by default, but they can be +disabled by setting their respective Disable* options.`, }, { Name: "FilterTTL", @@ -409,17 +418,6 @@ the database must already exist and be writeable. If a relative path is provided relative to the CWD (current working directory).`, }, }, - "EventsConfig": { - { - Name: "EnableActorEventsAPI", - Type: "bool", - - Comment: `EnableActorEventsAPI enables the Actor events API that enables clients to consume events -emitted by (smart contracts + built-in Actors). -This will also enable the RealTimeFilterAPI and HistoricFilterAPI by default, but they can be -disabled by setting their respective Disable* options in Fevm.Events.`, - }, - }, "FaultReporterConfig": { { Name: "EnableConsensusFaultReporter", @@ -474,7 +472,7 @@ Set to 0 to keep all mappings`, }, { Name: "Events", - Type: "Events", + Type: "DeprecatedEvents", Comment: ``, }, diff --git a/node/config/load.go b/node/config/load.go index fd015d533..354d3b3cb 100644 --- a/node/config/load.go +++ b/node/config/load.go @@ -18,12 +18,9 @@ import ( // FromFile loads config from a specified file overriding defaults specified in // the def parameter. If file does not exist or is empty defaults are assumed. func FromFile(path string, opts ...LoadCfgOpt) (interface{}, error) { - var loadOpts cfgLoadOpts - var err error - for _, opt := range opts { - if err = opt(&loadOpts); err != nil { - return nil, xerrors.Errorf("failed to apply load cfg option: %w", err) - } + loadOpts, err := applyOpts(opts...) + if err != nil { + return nil, err } var def interface{} if loadOpts.defaultCfg != nil { @@ -56,16 +53,43 @@ func FromFile(path string, opts ...LoadCfgOpt) (interface{}, error) { return nil, xerrors.Errorf("config failed validation: %w", err) } } - return FromReader(buf, def) + return FromReader(buf, def, opts...) } // FromReader loads config from a reader instance. -func FromReader(reader io.Reader, def interface{}) (interface{}, error) { - cfg := def - _, err := toml.NewDecoder(reader).Decode(cfg) +func FromReader(reader io.Reader, def interface{}, opts ...LoadCfgOpt) (interface{}, error) { + loadOpts, err := applyOpts(opts...) if err != nil { return nil, err } + cfg := def + md, err := toml.NewDecoder(reader).Decode(cfg) + if err != nil { + return nil, err + } + + // find any fields with a tag: `moved:"New.Config.Location"` and move any set values there over to + // the new location if they are not already set there. + movedFields := findMovedFields(nil, cfg) + var warningOut io.Writer = os.Stderr + if loadOpts.warningWriter != nil { + warningOut = loadOpts.warningWriter + } + for _, d := range movedFields { + if md.IsDefined(d.Field...) { + fmt.Fprintf( + warningOut, + "WARNING: Use of deprecated configuration option '%s' will be removed in a future release, use '%s' instead\n", + strings.Join(d.Field, "."), + strings.Join(d.NewField, ".")) + if !md.IsDefined(d.NewField...) { + // new value isn't set but old is, we should move what the user set there + if err := moveFieldValue(cfg, d.Field, d.NewField); err != nil { + return nil, fmt.Errorf("failed to move field value: %w", err) + } + } + } + } err = envconfig.Process("LOTUS", cfg) if err != nil { @@ -75,14 +99,105 @@ func FromReader(reader io.Reader, def interface{}) (interface{}, error) { return cfg, nil } +// move a value from the location in the valPtr struct specified by oldPath, to the location +// specified by newPath; where the path is an array of nested field names. +func moveFieldValue(valPtr interface{}, oldPath []string, newPath []string) error { + oldValue, err := getFieldValue(valPtr, oldPath) + if err != nil { + return err + } + val := reflect.ValueOf(valPtr).Elem() + for { + field := val.FieldByName(newPath[0]) + if !field.IsValid() { + return fmt.Errorf("unexpected error fetching field value") + } + if len(newPath) == 1 { + if field.Kind() != oldValue.Kind() { + return fmt.Errorf("unexpected error, old kind != new kind") + } + // set field on val to be the new one, and we're done + field.Set(oldValue) + return nil + } + if field.Kind() != reflect.Struct { + return fmt.Errorf("unexpected error fetching field value, is not a struct") + } + newPath = newPath[1:] + val = field + } +} + +// recursively iterate into `path` to find the terminal value +func getFieldValue(val interface{}, path []string) (reflect.Value, error) { + if reflect.ValueOf(val).Kind() == reflect.Ptr { + val = reflect.ValueOf(val).Elem().Interface() + } + field := reflect.ValueOf(val).FieldByName(path[0]) + if !field.IsValid() { + return reflect.Value{}, fmt.Errorf("unexpected error fetching field value") + } + if len(path) > 1 { + if field.Kind() != reflect.Struct { + return reflect.Value{}, fmt.Errorf("unexpected error fetching field value, is not a struct") + } + return getFieldValue(field.Interface(), path[1:]) + } + return field, nil +} + +type movedField struct { + Field []string + NewField []string +} + +// inspect the fields recursively within a struct and find any with "moved" tags +func findMovedFields(path []string, val interface{}) []movedField { + dep := make([]movedField, 0) + if reflect.ValueOf(val).Kind() == reflect.Ptr { + val = reflect.ValueOf(val).Elem().Interface() + } + t := reflect.TypeOf(val) + if t.Kind() != reflect.Struct { + return nil + } + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + // could also do a "deprecated" in here + if idx := field.Tag.Get("moved"); idx != "" && idx != "-" { + dep = append(dep, movedField{ + Field: append(path, field.Name), + NewField: strings.Split(idx, "."), + }) + } + if field.Type.Kind() == reflect.Struct && reflect.ValueOf(val).FieldByName(field.Name).IsValid() { + deps := findMovedFields(append(path, field.Name), reflect.ValueOf(val).FieldByName(field.Name).Interface()) + dep = append(dep, deps...) + } + } + return dep +} + type cfgLoadOpts struct { defaultCfg func() (interface{}, error) canFallbackOnDefault func() error validate func(string) error + warningWriter io.Writer } type LoadCfgOpt func(opts *cfgLoadOpts) error +func applyOpts(opts ...LoadCfgOpt) (cfgLoadOpts, error) { + var loadOpts cfgLoadOpts + var err error + for _, opt := range opts { + if err = opt(&loadOpts); err != nil { + return loadOpts, fmt.Errorf("failed to apply load cfg option: %w", err) + } + } + return loadOpts, nil +} + func SetDefault(f func() (interface{}, error)) LoadCfgOpt { return func(opts *cfgLoadOpts) error { opts.defaultCfg = f @@ -104,6 +219,13 @@ func SetValidate(f func(string) error) LoadCfgOpt { } } +func SetWarningWriter(w io.Writer) LoadCfgOpt { + return func(opts *cfgLoadOpts) error { + opts.warningWriter = w + return nil + } +} + func NoDefaultForSplitstoreTransition() error { return xerrors.Errorf("FullNode config not found and fallback to default disallowed while we transition to splitstore discard default. Use `lotus config default` to set this repo up with a default config. Be sure to set `EnableSplitstore` to `false` if you are running a full archive node") } diff --git a/node/config/load_test.go b/node/config/load_test.go index e17660c19..2edef259b 100644 --- a/node/config/load_test.go +++ b/node/config/load_test.go @@ -8,6 +8,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func fullNodeDefault() (interface{}, error) { return DefaultFullNode(), nil } @@ -138,3 +139,77 @@ func TestFailToFallbackToDefault(t *testing.T) { _, err = FromFile(nonExistantFileName, SetDefault(fullNodeDefault), SetCanFallbackOnDefault(NoDefaultForSplitstoreTransition)) assert.Error(t, err) } + +func TestPrintDeprecated(t *testing.T) { + type ChildCfg struct { + Field string `moved:"Bang"` + NewField string + } + type Old struct { + Thing1 int `moved:"New.Thing1"` + Thing2 int `moved:"New.Thing2"` + } + type New struct { + Thing1 int + Thing2 int + } + type ParentCfg struct { + Child ChildCfg + Old Old + New New + Foo int + Baz string `moved:"Child.NewField"` + Boom int `moved:"Foo"` + Bang string + } + + t.Run("warning output", func(t *testing.T) { + cfg := ` + Baz = "baz" + Foo = 100 + [Child] + Field = "bip" + NewField = "bop" + ` + + warningWriter := bytes.NewBuffer(nil) + + v, err := FromReader(bytes.NewReader([]byte(cfg)), &ParentCfg{Boom: 200, Bang: "300"}, SetWarningWriter(warningWriter)) + + require.NoError(t, err) + require.Equal(t, &ParentCfg{ + Child: ChildCfg{ + Field: "bip", + NewField: "bop", + }, + Baz: "baz", + Foo: 100, + Boom: 200, + Bang: "bip", + }, v) + require.Regexp(t, `\WChild\.Field\W.+use 'Bang' instead`, warningWriter.String()) + require.Regexp(t, `\WBaz\W.+use 'Child\.NewField' instead`, warningWriter.String()) + require.NotContains(t, warningWriter.String(), "don't use this at all") + require.NotContains(t, warningWriter.String(), "Boom") + }) + + defaultNew := New{Thing1: 42, Thing2: 800} + testCases := []struct { + name string + cfg string + expected New + }{ + {"simple", ``, defaultNew}, + {"set new", "[New]\nThing1 = 101\nThing2 = 102\n", New{Thing1: 101, Thing2: 102}}, + // should move old to new fields if new isn't set + {"set old", "[Old]\nThing1 = 101\nThing2 = 102\n", New{Thing1: 101, Thing2: 102}}, + } + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + v, err := FromReader(bytes.NewReader([]byte(tc.cfg)), &ParentCfg{New: defaultNew}) + require.NoError(t, err) + require.Equal(t, tc.expected, v.(*ParentCfg).New) + }) + } +} diff --git a/node/config/types.go b/node/config/types.go index 7aa43e34f..ecb946f5d 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -791,19 +791,48 @@ type FevmConfig struct { // Set to 0 to keep all mappings EthTxHashMappingLifetimeDays int - Events Events + Events DeprecatedEvents `toml:"Events,omitempty"` } -type Events struct { +type DeprecatedEvents struct { + // DisableRealTimeFilterAPI is DEPRECATED and will be removed in a future release. Use Events.DisableRealTimeFilterAPI instead. + DisableRealTimeFilterAPI bool `moved:"Events.DisableRealTimeFilterAPI" toml:"DisableRealTimeFilterAPI,omitempty"` + + // DisableHistoricFilterAPI is DEPRECATED and will be removed in a future release. Use Events.DisableHistoricFilterAPI instead. + DisableHistoricFilterAPI bool `moved:"Events.DisableHistoricFilterAPI" toml:"DisableHistoricFilterAPI,omitempty"` + + // FilterTTL is DEPRECATED and will be removed in a future release. Use Events.FilterTTL instead. + FilterTTL Duration `moved:"Events.FilterTTL" toml:"FilterTTL,omitzero"` + + // MaxFilters is DEPRECATED and will be removed in a future release. Use Events.MaxFilters instead. + MaxFilters int `moved:"Events.MaxFilters" toml:"MaxFilters,omitzero"` + + // MaxFilterResults is DEPRECATED and will be removed in a future release. Use Events.MaxFilterResults instead. + MaxFilterResults int `moved:"Events.MaxFilterResults" toml:"MaxFilterResults,omitzero"` + + // MaxFilterHeightRange is DEPRECATED and will be removed in a future release. Use Events.MaxFilterHeightRange instead. + MaxFilterHeightRange uint64 `moved:"Events.MaxFilterHeightRange" toml:"MaxFilterHeightRange,omitzero"` + + // DatabasePath is DEPRECATED and will be removed in a future release. Use Events.DatabasePath instead. + DatabasePath string `moved:"Events.DatabasePath" toml:"DatabasePath,omitempty"` +} + +type EventsConfig struct { // DisableRealTimeFilterAPI will disable the RealTimeFilterAPI that can create and query filters for actor events as they are emitted. - // The API is enabled when EnableEthRPC or Events.EnableActorEventsAPI is true, but can be disabled selectively with this flag. + // The API is enabled when Fevm.EnableEthRPC or EnableActorEventsAPI is true, but can be disabled selectively with this flag. DisableRealTimeFilterAPI bool // DisableHistoricFilterAPI will disable the HistoricFilterAPI that can create and query filters for actor events // that occurred in the past. HistoricFilterAPI maintains a queryable index of events. - // The API is enabled when EnableEthRPC or Events.EnableActorEventsAPI is true, but can be disabled selectively with this flag. + // The API is enabled when Fevm.EnableEthRPC or EnableActorEventsAPI is true, but can be disabled selectively with this flag. DisableHistoricFilterAPI bool + // EnableActorEventsAPI enables the Actor events API that enables clients to consume events + // emitted by (smart contracts + built-in Actors). + // This will also enable the RealTimeFilterAPI and HistoricFilterAPI by default, but they can be + // disabled by setting their respective Disable* options. + EnableActorEventsAPI bool + // FilterTTL specifies the time to live for actor event filters. Filters that haven't been accessed longer than // this time become eligible for automatic deletion. FilterTTL Duration @@ -830,14 +859,6 @@ type Events struct { // Set upper bound on index size } -type EventsConfig struct { - // EnableActorEventsAPI enables the Actor events API that enables clients to consume events - // emitted by (smart contracts + built-in Actors). - // This will also enable the RealTimeFilterAPI and HistoricFilterAPI by default, but they can be - // disabled by setting their respective Disable* options in Fevm.Events. - EnableActorEventsAPI bool -} - type IndexConfig struct { // EXPERIMENTAL FEATURE. USE WITH CAUTION // EnableMsgIndex enables indexing of messages on chain. diff --git a/node/modules/actorevent.go b/node/modules/actorevent.go index 135a34e5b..529edb5c7 100644 --- a/node/modules/actorevent.go +++ b/node/modules/actorevent.go @@ -32,17 +32,17 @@ type EventHelperAPI struct { var _ events.EventHelperAPI = &EventHelperAPI{} -func EthEventHandler(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo, fx.Lifecycle, *filter.EventFilterManager, *store.ChainStore, *stmgr.StateManager, EventHelperAPI, *messagepool.MessagePool, full.StateAPI, full.ChainAPI) (*full.EthEventHandler, error) { +func EthEventHandler(cfg config.EventsConfig, enableEthRPC bool) func(helpers.MetricsCtx, repo.LockedRepo, fx.Lifecycle, *filter.EventFilterManager, *store.ChainStore, *stmgr.StateManager, EventHelperAPI, *messagepool.MessagePool, full.StateAPI, full.ChainAPI) (*full.EthEventHandler, 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.EthEventHandler, error) { ctx := helpers.LifecycleCtx(mctx, lc) ee := &full.EthEventHandler{ Chain: cs, - MaxFilterHeightRange: abi.ChainEpoch(cfg.Events.MaxFilterHeightRange), + MaxFilterHeightRange: abi.ChainEpoch(cfg.MaxFilterHeightRange), SubscribtionCtx: ctx, } - if !cfg.EnableEthRPC || cfg.Events.DisableRealTimeFilterAPI { + if !enableEthRPC || cfg.DisableRealTimeFilterAPI { // all event functionality is disabled // the historic filter API relies on the real time one return ee, nil @@ -53,21 +53,21 @@ func EthEventHandler(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.Locked StateAPI: stateapi, ChainAPI: chainapi, } - ee.FilterStore = filter.NewMemFilterStore(cfg.Events.MaxFilters) + ee.FilterStore = filter.NewMemFilterStore(cfg.MaxFilters) // Start garbage collection for filters lc.Append(fx.Hook{ OnStart: func(context.Context) error { - go ee.GC(ctx, time.Duration(cfg.Events.FilterTTL)) + go ee.GC(ctx, time.Duration(cfg.FilterTTL)) return nil }, }) ee.TipSetFilterManager = &filter.TipSetFilterManager{ - MaxFilterResults: cfg.Events.MaxFilterResults, + MaxFilterResults: cfg.MaxFilterResults, } ee.MemPoolFilterManager = &filter.MemPoolFilterManager{ - MaxFilterResults: cfg.Events.MaxFilterResults, + MaxFilterResults: cfg.MaxFilterResults, } ee.EventFilterManager = fm @@ -94,22 +94,22 @@ func EthEventHandler(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.Locked } } -func EventFilterManager(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo, fx.Lifecycle, *store.ChainStore, *stmgr.StateManager, EventHelperAPI, full.ChainAPI) (*filter.EventFilterManager, error) { +func EventFilterManager(cfg config.EventsConfig) func(helpers.MetricsCtx, repo.LockedRepo, fx.Lifecycle, *store.ChainStore, *stmgr.StateManager, EventHelperAPI, full.ChainAPI) (*filter.EventFilterManager, error) { return func(mctx helpers.MetricsCtx, r repo.LockedRepo, lc fx.Lifecycle, cs *store.ChainStore, sm *stmgr.StateManager, evapi EventHelperAPI, chainapi full.ChainAPI) (*filter.EventFilterManager, error) { ctx := helpers.LifecycleCtx(mctx, lc) // Enable indexing of actor events var eventIndex *filter.EventIndex - if !cfg.Events.DisableHistoricFilterAPI { + if !cfg.DisableHistoricFilterAPI { var dbPath string - if cfg.Events.DatabasePath == "" { + if cfg.DatabasePath == "" { sqlitePath, err := r.SqlitePath() if err != nil { return nil, err } dbPath = filepath.Join(sqlitePath, "events.db") } else { - dbPath = cfg.Events.DatabasePath + dbPath = cfg.DatabasePath } var err error @@ -144,7 +144,7 @@ func EventFilterManager(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.Loc return *actor.Address, true }, - MaxFilterResults: cfg.Events.MaxFilterResults, + MaxFilterResults: cfg.MaxFilterResults, } lc.Append(fx.Hook{ @@ -162,10 +162,10 @@ 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(cfg config.EventsConfig) 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) { - if !enable || fevmCfg.Events.DisableRealTimeFilterAPI { + if !cfg.EnableActorEventsAPI || cfg.DisableRealTimeFilterAPI { fm = nil } @@ -173,7 +173,7 @@ func ActorEventHandler(enable bool, fevmCfg config.FevmConfig) func(helpers.Metr cs, fm, time.Duration(build.BlockDelaySecs)*time.Second, - abi.ChainEpoch(fevmCfg.Events.MaxFilterHeightRange), + abi.ChainEpoch(cfg.MaxFilterHeightRange), ), nil } }