Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package linea.coordinator.config

import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import com.github.michaelbull.result.get
import com.github.michaelbull.result.getOrElse
import com.sksamuel.hoplite.ConfigLoaderBuilder
import com.sksamuel.hoplite.addPathSource
import net.consensys.linea.traces.TracesCountersV1
import net.consensys.linea.traces.TracesCountersV2
import net.consensys.zkevm.coordinator.app.config.CoordinatorConfig
import net.consensys.zkevm.coordinator.app.config.CoordinatorConfigTomlDto
import net.consensys.zkevm.coordinator.app.config.GasPriceCapTimeOfDayMultipliersConfig
import net.consensys.zkevm.coordinator.app.config.SmartContractErrorCodesConfig
import net.consensys.zkevm.coordinator.app.config.TracesLimitsV1ConfigFile
import net.consensys.zkevm.coordinator.app.config.TracesLimitsV2ConfigFile
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
import java.nio.file.Path

inline fun <reified T : Any> loadConfigsOrError(
configFiles: List<Path>
): Result<T, String> {
val confBuilder: ConfigLoaderBuilder = ConfigLoaderBuilder.Companion.empty().addDefaults()
for (configFile in configFiles.reversed()) {
// files must be added in reverse order for overriding
confBuilder.addPathSource(configFile, false)
}

return confBuilder.build().loadConfig<T>(emptyList()).let { config ->
if (config.isInvalid()) {
Err(config.getInvalidUnsafe().description())
} else {
Ok(config.getUnsafe())
}
}
}

fun logErrorIfPresent(
configName: String,
configFiles: List<Path>,
configLoadingResult: Result<Any?, String>,
logger: Logger
) {
if (configLoadingResult is Err) {
logger.error("Failed to load $configName from files=$configFiles with error=${configLoadingResult.error}")
}
}

inline fun <reified T : Any> loadConfigsAndLogErrors(
configFiles: List<Path>,
configName: String,
logger: Logger = LogManager.getLogger("linea.coordinator.config")
): Result<T, String> {
return loadConfigsOrError<T>(configFiles)
.also { logErrorIfPresent(configName, configFiles, it, logger) }
}

fun loadConfigsOrError(
coordinatorConfigFiles: List<Path>,
tracesLimitsFileV1: Path?,
tracesLimitsFileV2: Path?,
gasPriceCapTimeOfDayMultipliersFile: Path,
smartContractErrorsFile: Path,
logger: Logger = LogManager.getLogger("linea.coordinator.config")
): Result<CoordinatorConfigTomlDto, String> {
val coordinatorBaseConfigs =
loadConfigsAndLogErrors<CoordinatorConfigTomlDto>(coordinatorConfigFiles, "coordinator", logger)
val tracesLimitsV1Configs = tracesLimitsFileV1
?.let { loadConfigsAndLogErrors<TracesLimitsV1ConfigFile>(listOf(it), "traces limit v1", logger) }
val tracesLimitsV2Configs = tracesLimitsFileV2
?.let { loadConfigsAndLogErrors<TracesLimitsV2ConfigFile>(listOf(it), "traces limits v2", logger) }
val gasPriceCapTimeOfDayMultipliersConfig =
loadConfigsAndLogErrors<GasPriceCapTimeOfDayMultipliersConfig>(
listOf(gasPriceCapTimeOfDayMultipliersFile),
"l1 submission gas prices caps",
logger
)
val smartContractErrorsConfig = loadConfigsAndLogErrors<SmartContractErrorCodesConfig>(
listOf(smartContractErrorsFile),
"smart contract errors",
logger
)
val configError = listOf(
coordinatorBaseConfigs,
tracesLimitsV1Configs,
tracesLimitsV1Configs,
gasPriceCapTimeOfDayMultipliersConfig,
smartContractErrorsConfig
)
.find { it is Err }

if (configError != null) {
@Suppress("UNCHECKED_CAST")
return configError as Result<CoordinatorConfigTomlDto, String>
}

val baseConfig = coordinatorBaseConfigs.get()!!
val finalConfig = baseConfig.copy(
conflation = baseConfig.conflation.copy(
_tracesLimitsV1 = tracesLimitsV1Configs?.get()?.tracesLimits?.let { TracesCountersV1(it) },
_tracesLimitsV2 = tracesLimitsV2Configs?.get()?.tracesLimits?.let { TracesCountersV2(it) },
_smartContractErrors = smartContractErrorsConfig.get()!!.smartContractErrors
),
l1DynamicGasPriceCapService = baseConfig.l1DynamicGasPriceCapService.copy(
gasPriceCapCalculation = baseConfig.l1DynamicGasPriceCapService.gasPriceCapCalculation.copy(
timeOfDayMultipliers = gasPriceCapTimeOfDayMultipliersConfig.get()?.gasPriceCapTimeOfDayMultipliers
)
)
)
return Ok(finalConfig)
}

fun loadConfigs(
coordinatorConfigFiles: List<Path>,
tracesLimitsFileV1: Path?,
tracesLimitsFileV2: Path?,
gasPriceCapTimeOfDayMultipliersFile: Path,
smartContractErrorsFile: Path,
logger: Logger = LogManager.getLogger("linea.coordinator.config")
): CoordinatorConfig {
loadConfigsOrError(
coordinatorConfigFiles,
tracesLimitsFileV1,
tracesLimitsFileV2,
gasPriceCapTimeOfDayMultipliersFile,
smartContractErrorsFile,
logger
).let {
return it
.getOrElse {
throw RuntimeException("Invalid configurations: $it")
}.reified()
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,6 @@
package net.consensys.zkevm.coordinator.app

import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import com.github.michaelbull.result.get
import com.github.michaelbull.result.getError
import com.github.michaelbull.result.onFailure
import com.sksamuel.hoplite.ConfigLoaderBuilder
import com.sksamuel.hoplite.addFileSource
import net.consensys.linea.traces.TracesCountersV1
import net.consensys.linea.traces.TracesCountersV2
import net.consensys.zkevm.coordinator.app.config.CoordinatorConfig
import net.consensys.zkevm.coordinator.app.config.CoordinatorConfigTomlDto
import net.consensys.zkevm.coordinator.app.config.GasPriceCapTimeOfDayMultipliersConfig
import net.consensys.zkevm.coordinator.app.config.SmartContractErrorCodesConfig
import net.consensys.zkevm.coordinator.app.config.TracesLimitsV1ConfigFile
import net.consensys.zkevm.coordinator.app.config.TracesLimitsV2ConfigFile
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
import picocli.CommandLine
Expand Down Expand Up @@ -115,13 +100,14 @@ internal constructor(private val errorWriter: PrintWriter, private val startActi
}
}

val configs = validateConfigs(
tracesLimitsFile,
tracesLimitsV2File,
smartContractErrorsFile,
gasPriceCapTimeOfDayMultipliersFile,
configFiles
) ?: return 1
val configs = linea.coordinator.config.loadConfigs(
coordinatorConfigFiles = configFiles.map { it.toPath() },
tracesLimitsFileV1 = tracesLimitsFile?.toPath(),
tracesLimitsFileV2 = tracesLimitsV2File?.toPath(),
smartContractErrorsFile = smartContractErrorsFile.toPath(),
gasPriceCapTimeOfDayMultipliersFile = gasPriceCapTimeOfDayMultipliersFile.toPath(),
logger = logger
)

if (checkConfigsOnly) {
logger.info("All configs are valid. Final configs: {}", configs)
Expand All @@ -144,7 +130,7 @@ internal constructor(private val errorWriter: PrintWriter, private val startActi
}

fun reportUserError(ex: Throwable) {
logger.fatal(ex.message, ex)
logger.fatal(ex.message)
errorWriter.println(ex.message)
printUsage(errorWriter)
}
Expand All @@ -155,97 +141,6 @@ internal constructor(private val errorWriter: PrintWriter, private val startActi
outputWriter.println(COMMAND_NAME + " --help")
}

private fun validateConfigs(
tracesLimitsFile: File?,
tracesLimitsV2File: File?,
smartContractErrorsFile: File,
gasPriceCapTimeOfDayMultipliersFile: File,
coordinatorConfigFiles: List<File>
): CoordinatorConfig? {
var hasConfigError = false
val tracesLimitsV1Configs = if (tracesLimitsFile == null) {
null
} else {
loadConfigsOrError<TracesLimitsV1ConfigFile>(listOf(tracesLimitsFile))
}

val tracesLimitsV2Configs = if (tracesLimitsV2File == null) {
null
} else {
loadConfigsOrError<TracesLimitsV2ConfigFile>(listOf(tracesLimitsV2File))
}

val smartContractErrorCodes =
loadConfigsOrError<SmartContractErrorCodesConfig>(listOf(smartContractErrorsFile))

val gasPriceCapTimeOfDayMultipliers =
loadConfigsOrError<GasPriceCapTimeOfDayMultipliersConfig>(listOf(gasPriceCapTimeOfDayMultipliersFile))

val configs = loadConfigsOrError<CoordinatorConfigTomlDto>(coordinatorConfigFiles)

if (tracesLimitsV1Configs is Err) {
hasConfigError = true
logger.error("Reading {} failed: {}", tracesLimitsFile, tracesLimitsV1Configs.getError())
} else if (tracesLimitsV1Configs is Ok) {
runCatching {
TracesCountersV1(tracesLimitsV1Configs.get()!!.tracesLimits)
}.getOrElse {
hasConfigError = true
logger.error("Traces limits file {} is incomplete. {}", tracesLimitsFile, it.message)
}
}

if (tracesLimitsV2Configs is Err) {
hasConfigError = true
logger.error("Reading {} failed: {}", tracesLimitsV2File, tracesLimitsV2Configs.getError())
} else if (tracesLimitsV2Configs is Ok) {
runCatching {
TracesCountersV2(tracesLimitsV2Configs.get()!!.tracesLimits)
}.getOrElse {
hasConfigError = true
logger.error("Traces limits file {} is incomplete. {}", tracesLimitsV2File, it.message)
}
}

if (smartContractErrorCodes is Err) {
hasConfigError = true
logger.error("Reading {} failed: {}", smartContractErrorsFile, smartContractErrorCodes.getError())
}

if (gasPriceCapTimeOfDayMultipliers is Err) {
hasConfigError = true
logger.error(
"Reading {} failed: {}",
gasPriceCapTimeOfDayMultipliersFile,
gasPriceCapTimeOfDayMultipliers.getError()
)
}

if (configs is Err) {
hasConfigError = true
logger.error("Reading {} failed: {}", configFiles, configs.getError())
}

return if (hasConfigError) {
null
} else {
configs.get()?.let { config: CoordinatorConfigTomlDto ->
config.copy(
conflation = config.conflation.copy(
_tracesLimitsV1 = tracesLimitsV1Configs?.get()?.tracesLimits?.let { TracesCountersV1(it) },
_tracesLimitsV2 = tracesLimitsV2Configs?.get()?.tracesLimits?.let { TracesCountersV2(it) },
_smartContractErrors = smartContractErrorCodes.get()?.smartContractErrors
),
l1DynamicGasPriceCapService = config.l1DynamicGasPriceCapService.copy(
gasPriceCapCalculation = config.l1DynamicGasPriceCapService.gasPriceCapCalculation.copy(
timeOfDayMultipliers = gasPriceCapTimeOfDayMultipliers.get()?.gasPriceCapTimeOfDayMultipliers
)
)
).reified()
}
}
}

/**
* Not using a static field for this log instance because some code in this class executes prior
* to the logging configuration being applied so it's not always safe to use the logger.
Expand All @@ -267,29 +162,5 @@ internal constructor(private val errorWriter: PrintWriter, private val startActi
val errorWriter = PrintWriter(System.err, true, Charset.defaultCharset())
return CoordinatorAppCli(errorWriter, startAction)
}

inline fun <reified T : Any> loadConfigs(configFiles: List<File>, errorWriter: PrintWriter): T? {
return loadConfigsOrError<T>(configFiles).onFailure { error ->
errorWriter.println(error)
}.get()
}

inline fun <reified T : Any> loadConfigsOrError(
configFiles: List<File>
): Result<T, String> {
val confBuilder: ConfigLoaderBuilder = ConfigLoaderBuilder.Companion.empty().addDefaults()
for (configFile in configFiles.reversed()) {
// files must be added in reverse order for overriding
confBuilder.addFileSource(configFile, false)
}

return confBuilder.build().loadConfig<T>(emptyList()).let { config ->
if (config.isInvalid()) {
Err(config.getInvalidUnsafe().description())
} else {
Ok(config.getUnsafe())
}
}
}
}
}
Loading