nim-codex/tests/integration/codexconfig.nim
Marcin Czenko 92a0eda79a
Validator historical state restoration (#922)
* adds a new overload of queryPastEvents allowing to query past events based on timestamp in the past

* adds state restoration to validator

* refactors a bit to get the tests back to work

* replaces deprecated generic methods from Market with methods for specific event types

* Refactors binary search

* adds market tests for querying past SlotFilled events and binary search

* Takes into account that <<earliest>> block available is not necessarily the genesis block

* Adds more logging and makes testing earliest block boundary more reliable

* adds validation tests for historical state restoration

* adds mockprovider to simplify and improve testing of the edge conditions

* adds slot reservation to the new tests after rebasing

* adds validation groups and group index in logs of validator

* adds integration test with two validators

* adds comment on how to enable logging in integration test executable itself

* testIntegration: makes list is running nodes injected and available in the body of the test

* validation: adds integration test for historical state

* adds more logging to validator

* integration test: validator only looks 30 days back for historical state

* adds logging of the slotState when removing slots during validation

* review and refactor validator integration tests

* adds validation to the set of integration tests

* Fixes mistyped name of the mock provider module in testMarket

* Fixes a typo in the name of the validation suite in integration tests

* Makes validation unit test a bit easier to follow

* better use of logScopes to reduce duplication

* improves timing and clarifies the test conditions

* uses http as default RPC provider for nodes running in integration tests as a workaround for dropped subscriptions

* simplifies the validation integration tests by waiting for failed request instead of tracking slots

* adds config option allowing selectively to set different provider url

* Brings back the default settings for RPC provider in integration tests

* use http RPC provider for clients in validation integration tests

* fine-tune the tests

* Makes validator integration test more robust - adds extra tracking

* brings tracking of marketplace event back to validator integration test

* refactors integration tests

* deletes tmp file

* adds <<return>> after forcing integration test to fail preliminarily

* re-enables all integration tests and matrix

* stops debug output in CI

* allows to choose a different RPC provider for a given integration test suite

* fixes signature of <<getBlock>> method in mockProvider

* adds missing import which seem to be braking integration tests on windows

* makes sure that clients, SPs, and validators use the same provider url

* makes validator integration tests using http at 127.0.0.1:8545

* testvalidator: stop resubscribing as we are now using http polling as rpc provider

* applying review comments

* groups queryPastStorage overrides together (review comment)

* groups the historical validation tests into a sub suite

* removes the temporary extensions in marketplacesuite and multinodesuite allowing to specify provider url

* simplifies validation integration tests

* Removes debug logs when waiting for request to fail

* Renaming waitForRequestFailed => waitForRequestToFail

* renames blockNumberForBlocksAgo to pastBlockTag and makes it private

* removes redundant debugging logs

* refines logging in validation

* removes dev logging from mockmarket

* improves exception handling in provider helper procs and prepares for extraction to a separate module

* Uses chronos instead of std/times for Duration

* extracts provider and binary search helpers to a separate module

* removes redundant log entry params from validator

* unifies the notation to consistently use method call syntax

* reuses ProviderError from nim-ethers in the provider extension

* clarifies the comment in multinodesuite

* uses == operator to check the predefined tags and raises exception when `BlockTag.pending` is requested.

* when waiting for request to fail, we break on any request state that is not Started

* removes tests that were moved to testProvider from testMarket

* extracts tests that use MockProvider to a separate async suite

* improves performance of the historical state restoration

* removing redundant log messages in validator (groupIndex and groups)

* adds testProvider to testContracts group

* removes unused import in testMarket
2024-12-14 05:07:55 +00:00

341 lines
9.8 KiB
Nim

import std/options
import std/os
import std/sequtils
import std/strutils
import std/sugar
import std/tables
from pkg/chronicles import LogLevel
import pkg/codex/conf
import pkg/codex/units
import pkg/confutils
import pkg/confutils/defs
import libp2p except setup
import pkg/questionable
import ./clioption
export clioption
export confutils
type
CodexConfigs* = object
configs*: seq[CodexConfig]
CodexConfig* = object
cliOptions: Table[StartUpCmd, Table[string, CliOption]]
cliPersistenceOptions: Table[PersistenceCmd, Table[string, CliOption]]
debugEnabled*: bool
CodexConfigError* = object of CatchableError
proc cliArgs*(config: CodexConfig): seq[string] {.gcsafe, raises: [CodexConfigError].}
proc raiseCodexConfigError(msg: string) {.raises: [CodexConfigError].} =
raise newException(CodexConfigError, msg)
template convertError(body) =
try:
body
except CatchableError as e:
raiseCodexConfigError e.msg
proc init*(_: type CodexConfigs, nodes = 1): CodexConfigs {.raises: [].} =
CodexConfigs(configs: newSeq[CodexConfig](nodes))
func nodes*(self: CodexConfigs): int =
self.configs.len
proc checkBounds(self: CodexConfigs, idx: int) {.raises: [CodexConfigError].} =
if idx notin 0..<self.configs.len:
raiseCodexConfigError "index must be in bounds of the number of nodes"
proc buildConfig(
config: CodexConfig,
msg: string): CodexConf {.raises: [CodexConfigError].} =
proc postFix(msg: string): string =
if msg.len > 0:
": " & msg
else: ""
try:
return CodexConf.load(cmdLine = config.cliArgs, quitOnFailure = false)
except ConfigurationError as e:
raiseCodexConfigError msg & e.msg.postFix
except Exception as e:
## TODO: remove once proper exception handling added to nim-confutils
raiseCodexConfigError msg & e.msg.postFix
proc addCliOption*(
config: var CodexConfig,
group = PersistenceCmd.noCmd,
cliOption: CliOption) {.raises: [CodexConfigError].} =
var options = config.cliPersistenceOptions.getOrDefault(group)
options[cliOption.key] = cliOption # overwrite if already exists
config.cliPersistenceOptions[group] = options
discard config.buildConfig("Invalid cli arg " & $cliOption)
proc addCliOption*(
config: var CodexConfig,
group = PersistenceCmd.noCmd,
key: string, value = "") {.raises: [CodexConfigError].} =
config.addCliOption(group, CliOption(key: key, value: value))
proc addCliOption*(
config: var CodexConfig,
group = StartUpCmd.noCmd,
cliOption: CliOption) {.raises: [CodexConfigError].} =
var options = config.cliOptions.getOrDefault(group)
options[cliOption.key] = cliOption # overwrite if already exists
config.cliOptions[group] = options
discard config.buildConfig("Invalid cli arg " & $cliOption)
proc addCliOption*(
config: var CodexConfig,
group = StartUpCmd.noCmd,
key: string, value = "") {.raises: [CodexConfigError].} =
config.addCliOption(group, CliOption(key: key, value: value))
proc addCliOption*(
config: var CodexConfig,
cliOption: CliOption) {.raises: [CodexConfigError].} =
config.addCliOption(StartUpCmd.noCmd, cliOption)
proc addCliOption*(
config: var CodexConfig,
key: string, value = "") {.raises: [CodexConfigError].} =
config.addCliOption(StartUpCmd.noCmd, CliOption(key: key, value: value))
proc cliArgs*(
config: CodexConfig): seq[string] {.gcsafe, raises: [CodexConfigError].} =
## converts CodexConfig cli options and command groups in a sequence of args
## and filters out cli options by node index if provided in the CliOption
var args: seq[string] = @[]
convertError:
for cmd in StartUpCmd:
if config.cliOptions.hasKey(cmd):
if cmd != StartUpCmd.noCmd:
args.add $cmd
var opts = config.cliOptions[cmd].values.toSeq
args = args.concat( opts.map(o => $o) )
for cmd in PersistenceCmd:
if config.cliPersistenceOptions.hasKey(cmd):
if cmd != PersistenceCmd.noCmd:
args.add $cmd
var opts = config.cliPersistenceOptions[cmd].values.toSeq
args = args.concat( opts.map(o => $o) )
return args
proc logFile*(config: CodexConfig): ?string {.raises: [CodexConfigError].} =
let built = config.buildConfig("Invalid codex config cli params")
built.logFile
proc logLevel*(config: CodexConfig): LogLevel {.raises: [CodexConfigError].} =
convertError:
let built = config.buildConfig("Invalid codex config cli params")
return parseEnum[LogLevel](built.logLevel.toUpperAscii)
proc debug*(
self: CodexConfigs,
idx: int,
enabled = true): CodexConfigs {.raises: [CodexConfigError].} =
## output log in stdout for a specific node in the group
self.checkBounds idx
var startConfig = self
startConfig.configs[idx].debugEnabled = enabled
return startConfig
proc debug*(self: CodexConfigs, enabled = true): CodexConfigs {.raises: [].} =
## output log in stdout for all nodes in group
var startConfig = self
for config in startConfig.configs.mitems:
config.debugEnabled = enabled
return startConfig
proc withLogFile*(
self: CodexConfigs,
idx: int): CodexConfigs {.raises: [CodexConfigError].} =
self.checkBounds idx
var startConfig = self
startConfig.configs[idx].addCliOption("--log-file", "<updated_in_test>")
return startConfig
proc withLogFile*(
self: CodexConfigs): CodexConfigs {.raises: [CodexConfigError].} =
## typically called from test, sets config such that a log file should be
## created
var startConfig = self
for config in startConfig.configs.mitems:
config.addCliOption("--log-file", "<updated_in_test>")
return startConfig
proc withLogFile*(
self: var CodexConfig,
logFile: string) {.raises: [CodexConfigError].} = #: CodexConfigs =
## typically called internally from the test suite, sets a log file path to
## be created during the test run, for a specified node in the group
# var config = self
self.addCliOption("--log-file", logFile)
# return startConfig
proc withLogLevel*(
self: CodexConfig,
level: LogLevel | string): CodexConfig {.raises: [CodexConfigError].} =
var config = self
config.addCliOption("--log-level", $level)
return config
proc withLogLevel*(
self: CodexConfigs,
idx: int,
level: LogLevel | string): CodexConfigs {.raises: [CodexConfigError].} =
self.checkBounds idx
var startConfig = self
startConfig.configs[idx].addCliOption("--log-level", $level)
return startConfig
proc withLogLevel*(
self: CodexConfigs,
level: LogLevel | string): CodexConfigs {.raises: [CodexConfigError].} =
var startConfig = self
for config in startConfig.configs.mitems:
config.addCliOption("--log-level", $level)
return startConfig
proc withSimulateProofFailures*(
self: CodexConfigs,
idx: int,
failEveryNProofs: int
): CodexConfigs {.raises: [CodexConfigError].} =
self.checkBounds idx
var startConfig = self
startConfig.configs[idx].addCliOption(
StartUpCmd.persistence, "--simulate-proof-failures", $failEveryNProofs)
return startConfig
proc withSimulateProofFailures*(
self: CodexConfigs,
failEveryNProofs: int): CodexConfigs {.raises: [CodexConfigError].} =
var startConfig = self
for config in startConfig.configs.mitems:
config.addCliOption(
StartUpCmd.persistence, "--simulate-proof-failures", $failEveryNProofs)
return startConfig
proc withValidationGroups*(
self: CodexConfigs,
groups: ValidationGroups): CodexConfigs {.raises: [CodexConfigError].} =
var startConfig = self
for config in startConfig.configs.mitems:
config.addCliOption(
StartUpCmd.persistence, "--validator-groups", $(groups))
return startConfig
proc withValidationGroupIndex*(
self: CodexConfigs,
idx: int,
groupIndex: uint16): CodexConfigs {.raises: [CodexConfigError].} =
self.checkBounds idx
var startConfig = self
startConfig.configs[idx].addCliOption(
StartUpCmd.persistence, "--validator-group-index", $groupIndex)
return startConfig
proc withEthProvider*(
self: CodexConfigs,
idx: int,
ethProvider: string
): CodexConfigs {.raises: [CodexConfigError].} =
self.checkBounds idx
var startConfig = self
startConfig.configs[idx].addCliOption(StartUpCmd.persistence,
"--eth-provider", ethProvider)
return startConfig
proc withEthProvider*(
self: CodexConfigs,
ethProvider: string): CodexConfigs {.raises: [CodexConfigError].} =
var startConfig = self
for config in startConfig.configs.mitems:
config.addCliOption(StartUpCmd.persistence,
"--eth-provider", ethProvider)
return startConfig
proc logLevelWithTopics(
config: CodexConfig,
topics: varargs[string]): string {.raises: [CodexConfigError].} =
convertError:
var logLevel = LogLevel.INFO
let built = config.buildConfig("Invalid codex config cli params")
logLevel = parseEnum[LogLevel](built.logLevel.toUpperAscii)
let level = $logLevel & ";TRACE: " & topics.join(",")
return level
proc withLogTopics*(
self: CodexConfigs,
idx: int,
topics: varargs[string]): CodexConfigs {.raises: [CodexConfigError].} =
self.checkBounds idx
convertError:
let config = self.configs[idx]
let level = config.logLevelWithTopics(topics)
var startConfig = self
return startConfig.withLogLevel(idx, level)
proc withLogTopics*(
self: CodexConfigs,
topics: varargs[string]
): CodexConfigs {.raises: [CodexConfigError].} =
var startConfig = self
for config in startConfig.configs.mitems:
let level = config.logLevelWithTopics(topics)
config = config.withLogLevel(level)
return startConfig
proc withStorageQuota*(
self: CodexConfigs,
idx: int,
quota: NBytes): CodexConfigs {.raises: [CodexConfigError].} =
self.checkBounds idx
var startConfig = self
startConfig.configs[idx].addCliOption("--storage-quota", $quota)
return startConfig
proc withStorageQuota*(
self: CodexConfigs,
quota: NBytes): CodexConfigs {.raises: [CodexConfigError].} =
var startConfig = self
for config in startConfig.configs.mitems:
config.addCliOption("--storage-quota", $quota)
return startConfig