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.
This commit is contained in:
parent
82a45185dd
commit
d35ebe5bc0
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
},
|
||||
|
@ -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{}),
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -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: ``,
|
||||
},
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user