Gas pricing refactoring + transaction sample calculator for legacy gas price estimation (#27)

* Transaction cost calculator implementation and a comparative test.

* Refactored gas pricing config to make it cleaner

* Added DTO objects for easier instantination

* Better type for VariableFeesCalculator configs
This commit is contained in:
Roman Vaseev
2024-09-20 11:30:19 +02:00
committed by GitHub
parent 32008054bb
commit 643067ee97
24 changed files with 846 additions and 213 deletions

View File

@@ -10,6 +10,13 @@ max_line_length=120
insert_final_newline = true
trim_trailing_whitespace = true
[*.{kt,kts}]
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
ij_kotlin_name_count_to_use_star_import = 2147483647
ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
ij_kotlin_packages_to_use_import_on_demand = unset
[*.go]
indent_style = tab
indent_size = tab

View File

@@ -157,14 +157,14 @@ jobs:
mkdir -p docker_logs
docker ps -a >> docker_logs/docker_ps.txt
docker logs prover --since 1h &>> docker_logs/prover.txt
docker logs prover-v3 --since 1h &>> docker_logs/prover-v3.txt
docker logs prover-v3 --since 1h &>> docker_logs/prover-v3.txt;
docker logs coordinator --since 1h &>> docker_logs/coordinator.txt
docker logs shomei --since 1h &>> docker_logs/shomei.txt
docker logs zkbesu-shomei --since 1h &>> docker_logs/zkbesu-shomei.txt
docker logs shomei-frontend --since 1h &>> docker_logs/shomei-frontend.txt
docker logs postman --since 1h &>> docker_logs/postman.txt
docker logs traces-node --since 1h &>> docker_logs/traces-node.txt
docker logs traces-node-v2 --since 1h &>> docker_logs/traces-node-v2.txt
docker logs traces-node-v2 --since 1h &>> docker_logs/traces-node-v2.txt;
docker logs sequencer --since 1h &>> docker_logs/sequencer.txt
- name: Archive debug logs
uses: actions/upload-artifact@v4

View File

@@ -206,39 +206,47 @@ disabled=false
polling-interval="PT10S"
max-messages-to-anchor=100
[dynamic-gas-price-service]
disabled=false
price-update-interval="PT12S"
fee-history-block-count=50
fee-history-reward-percentile=15
base-fee-coefficient=0.1
priority-fee-coefficient=1.0
base-fee-blob-coefficient=0.1
expected-blob-gas = 131072.0 # 2^17
[l2-network-gas-pricing]
disabled = false
price-update-interval = "PT12S"
fee-history-block-count = 50
fee-history-reward-percentile = 15
blob-submission-expected-execution-gas = 213000.0 # Lower to 120k as we improve efficiency
gas-price-upper-bound=10000000000 # 10 GWEI
gas-price-lower-bound=90000000 # 0.09 GWEI
gas-price-fixed-cost=3000000
geth-gas-price-update-recipients=[
# Defaults to expected-blob-gas
#bytes-per-data-submission=131072.0 # 2^17
l1-blob-gas = 131072 # 2^17
[l2-network-gas-pricing.request-retry]
max-retries = 3
timeout = "PT6S"
backoff-delay = "PT1S"
failures-warning-threshold = 2
[l2-network-gas-pricing.variable-cost-pricing]
gas-price-fixed-cost = 3000000
legacy-fees-multiplier = 1.2
margin = 4.0
variable-cost-upper-bound = 10000000001 # ~10 GWEI
variable-cost-lower-bound = 90000001 # ~0.09 GWEI
[l2-network-gas-pricing.extra-data-pricing-propagation]
extra-data-update-recipient = "http://sequencer:8545/"
[l2-network-gas-pricing.legacy]
type="SampleTransaction"
gas-price-upper-bound = 10000000000 # 10 GWEI
gas-price-lower-bound = 90000000 # 0.09 GWEI
[l2-network-gas-pricing.json-rpc-pricing-propagation]
geth-gas-price-update-recipients = [
"http://traces-node:8545/",
"http://l2-node:8545/"
]
besu-gas-price-update-recipients=[
besu-gas-price-update-recipients = [
"http://sequencer:8545/"
]
request-retry.max-retries=3
request-retry.timeout="PT6S"
request-retry.backoff-delay="PT1S"
request-retry.failures-warning-threshold=2
extra-data-enabled=true
min-mineable-fees-enabled=true
legacy-fees-multiplier=1.2
margin=4.0
extra-data-update-recipient="http://sequencer:8545/"
variable-cost-upper-bound=10000000001 # ~10 GWEI
variable-cost-lower-bound=90000001 # ~0.09 GWEI
# Defaults to expected-blob-gas
#bytes-per-data-submission=131072.0 # 2^17
[l1-dynamic-gas-price-cap-service]
disabled=false

View File

@@ -1,13 +1,13 @@
[prover]
[prover.execution]
fs-requests-directory = "/data/prover/v3/execution/requests"
fs-responses-directory = "/data/prover/v3/execution/responses"
fs-requests-directory = "tmp/local/prover/v3/execution/requests"
fs-responses-directory = "tmp/local/prover/v3/execution/responses"
[prover.blob-compression]
fs-requests-directory = "/data/prover/v3/compression/requests"
fs-responses-directory = "/data/prover/v3/compression/responses"
fs-requests-directory = "tmp/local/prover/v3/compression/requests"
fs-responses-directory = "tmp/local/prover/v3/compression/responses"
[prover.proof-aggregation]
fs-requests-directory = "/data/prover/v3/aggregation/requests"
fs-responses-directory = "/data/prover/v3/aggregation/responses"
fs-requests-directory = "tmp/local/prover/v3/aggregation/requests"
fs-responses-directory = "tmp/local/prover/v3/aggregation/responses"
[zk-traces]
eth-api="http://127.0.0.1:8745"

View File

@@ -127,7 +127,7 @@ class L1DependentApp(
if (configs.messageAnchoringService.disabled) {
log.warn("Message anchoring service is disabled")
}
if (configs.dynamicGasPriceService.disabled) {
if (configs.l2NetworkGasPricingService == null) {
log.warn("Dynamic gas price service is disabled")
}
}
@@ -907,14 +907,14 @@ class L1DependentApp(
null
}
private val gasPriceUpdaterApp: GasPriceUpdaterApp? =
if (configs.dynamicGasPriceService.enabled) {
GasPriceUpdaterApp(
private val l2NetworkGasPricingService: L2NetworkGasPricingService? =
if (configs.l2NetworkGasPricingService != null) {
L2NetworkGasPricingService(
vertx = vertx,
httpJsonRpcClientFactory = httpJsonRpcClientFactory,
l1Web3jClient = l1Web3jClient,
l1Web3jService = l1Web3jService,
config = configs.dynamicGasPriceService
config = configs.l2NetworkGasPricingService
)
} else {
null
@@ -1001,7 +1001,7 @@ class L1DependentApp(
.thenCompose { aggregationFinalizationCoordinator.start() }
.thenCompose { proofAggregationCoordinatorService.start() }
.thenCompose { messageAnchoringApp?.start() ?: SafeFuture.completedFuture(Unit) }
.thenCompose { gasPriceUpdaterApp?.start() ?: SafeFuture.completedFuture(Unit) }
.thenCompose { l2NetworkGasPricingService?.start() ?: SafeFuture.completedFuture(Unit) }
.thenCompose { l1FeeHistoryCachingService.start() }
.thenCompose { deadlineConflationCalculatorRunnerOld.start() }
.thenCompose { deadlineConflationCalculatorRunnerNew.start() }
@@ -1019,7 +1019,7 @@ class L1DependentApp(
aggregationFinalizationCoordinator.stop(),
proofAggregationCoordinatorService.stop(),
messageAnchoringApp?.stop() ?: SafeFuture.completedFuture(Unit),
gasPriceUpdaterApp?.stop() ?: SafeFuture.completedFuture(Unit),
l2NetworkGasPricingService?.stop() ?: SafeFuture.completedFuture(Unit),
l1FeeHistoryCachingService.stop(),
blockCreationMonitor.stop(),
deadlineConflationCalculatorRunnerOld.stop(),

View File

@@ -12,113 +12,104 @@ import net.consensys.linea.ethereum.gaspricing.staticcap.GasPriceUpdaterImpl
import net.consensys.linea.ethereum.gaspricing.staticcap.GasUsageRatioWeightedAverageFeesCalculator
import net.consensys.linea.ethereum.gaspricing.staticcap.MinMineableFeesPricerService
import net.consensys.linea.ethereum.gaspricing.staticcap.MinerExtraDataV1CalculatorImpl
import net.consensys.linea.ethereum.gaspricing.staticcap.TransactionCostCalculator
import net.consensys.linea.ethereum.gaspricing.staticcap.VariableFeesCalculator
import net.consensys.linea.jsonrpc.client.VertxHttpJsonRpcClientFactory
import net.consensys.linea.web3j.Web3jBlobExtended
import net.consensys.toKWeiUInt
import net.consensys.zkevm.LongRunningService
import net.consensys.zkevm.coordinator.app.config.DynamicGasPriceServiceConfig
import org.apache.logging.log4j.LogManager
import org.web3j.protocol.Web3j
import tech.pegasys.teku.infrastructure.async.SafeFuture
import java.util.concurrent.CompletableFuture
import kotlin.time.toKotlinDuration
import kotlin.time.Duration
class GasPriceUpdaterApp(
class L2NetworkGasPricingService(
vertx: Vertx,
httpJsonRpcClientFactory: VertxHttpJsonRpcClientFactory,
l1Web3jClient: Web3j,
l1Web3jService: Web3jBlobExtended,
config: DynamicGasPriceServiceConfig
config: Config
) : LongRunningService {
data class LegacyGasPricingCalculatorConfig(
val transactionCostCalculatorConfig: TransactionCostCalculator.Config?,
val naiveGasPricingCalculatorConfig: GasUsageRatioWeightedAverageFeesCalculator.Config?,
val legacyGasPricingCalculatorBounds: BoundableFeeCalculator.Config
)
data class Config(
val feeHistoryFetcherConfig: FeeHistoryFetcherImpl.Config,
val jsonRpcPricingPropagationEnabled: Boolean,
val legacy: LegacyGasPricingCalculatorConfig,
val jsonRpcGasPriceUpdaterConfig: GasPriceUpdaterImpl.Config,
val jsonRpcPriceUpdateInterval: Duration,
val extraDataPricingPropagationEnabled: Boolean,
val extraDataUpdateInterval: Duration,
val variableFeesCalculatorConfig: VariableFeesCalculator.Config,
val variableFeesCalculatorBounds: BoundableFeeCalculator.Config,
val extraDataCalculatorConfig: MinerExtraDataV1CalculatorImpl.Config,
val extraDataUpdaterConfig: ExtraDataV1UpdaterImpl.Config
)
private val log = LogManager.getLogger(this::class.java)
private val gasPricingFeesFetcher: FeesFetcher = FeeHistoryFetcherImpl(
l1Web3jClient,
l1Web3jService,
FeeHistoryFetcherImpl.Config(
config.feeHistoryBlockCount.toUInt(),
config.feeHistoryRewardPercentile
)
config.feeHistoryFetcherConfig
)
private val minMineableFeesCalculator: FeesCalculator = run {
val gasUsageRatioWeightedAverageFeesCalculator = GasUsageRatioWeightedAverageFeesCalculator(
GasUsageRatioWeightedAverageFeesCalculator.Config(
baseFeeCoefficient = config.baseFeeCoefficient,
priorityFeeCoefficient = config.priorityFeeCoefficient,
baseFeeBlobCoefficient = config.baseFeeBlobCoefficient,
blobSubmissionExpectedExecutionGas = config.blobSubmissionExpectedExecutionGas,
expectedBlobGas = config.expectedBlobGas
private val variableCostCalculator = VariableFeesCalculator(
config.variableFeesCalculatorConfig
)
private val legacyGasPricingCalculator: FeesCalculator = run {
val baseCalculator = if (config.legacy.transactionCostCalculatorConfig != null) {
TransactionCostCalculator(variableCostCalculator, config.legacy.transactionCostCalculatorConfig)
} else {
GasUsageRatioWeightedAverageFeesCalculator(
config.legacy.naiveGasPricingCalculatorConfig!!
)
)
}
BoundableFeeCalculator(
BoundableFeeCalculator.Config(
config.gasPriceUpperBound.toDouble(),
config.gasPriceLowerBound.toDouble(),
config.gasPriceFixedCost.toDouble()
),
gasUsageRatioWeightedAverageFeesCalculator
config.legacy.legacyGasPricingCalculatorBounds,
baseCalculator
)
}
private val minMineableFeesPricerService: MinMineableFeesPricerService? =
if (config.minMineableFeesEnabled) {
if (config.jsonRpcPricingPropagationEnabled) {
val l2SetGasPriceUpdater: GasPriceUpdater = GasPriceUpdaterImpl(
httpJsonRpcClientFactory = httpJsonRpcClientFactory,
config = GasPriceUpdaterImpl.Config(
gethEndpoints = config.gethGasPriceUpdateRecipients,
besuEndPoints = config.besuGasPriceUpdateRecipients,
retryConfig = config.requestRetryConfig
)
config = config.jsonRpcGasPriceUpdaterConfig
)
MinMineableFeesPricerService(
pollingInterval = config.priceUpdateInterval.toKotlinDuration(),
pollingInterval = config.jsonRpcPriceUpdateInterval,
vertx = vertx,
feesFetcher = gasPricingFeesFetcher,
feesCalculator = minMineableFeesCalculator,
feesCalculator = legacyGasPricingCalculator,
gasPriceUpdater = l2SetGasPriceUpdater
)
} else {
null
}
private val extraDataPricerService: ExtraDataV1PricerService? = if (config.extraDataEnabled) {
val variableCostCalculator = VariableFeesCalculator(
VariableFeesCalculator.Config(
blobSubmissionExpectedExecutionGas = config.blobSubmissionExpectedExecutionGas,
bytesPerDataSubmission = config.bytesPerDataSubmission,
expectedBlobGas = config.expectedBlobGas,
margin = config.margin
)
)
private val extraDataPricerService: ExtraDataV1PricerService? = if (config.extraDataPricingPropagationEnabled) {
val boundedVariableCostCalculator = BoundableFeeCalculator(
config = BoundableFeeCalculator.Config(
feeUpperBound = config.variableCostUpperBound.toDouble(),
feeLowerBound = config.variableCostLowerBound.toDouble(),
feeMargin = 0.0
),
config = config.variableFeesCalculatorBounds,
feesCalculator = variableCostCalculator
)
ExtraDataV1PricerService(
pollingInterval = config.priceUpdateInterval.toKotlinDuration(),
pollingInterval = config.extraDataUpdateInterval,
vertx = vertx,
feesFetcher = gasPricingFeesFetcher,
minerExtraDataCalculatorImpl = MinerExtraDataV1CalculatorImpl(
config = MinerExtraDataV1CalculatorImpl.Config(
fixedCostInKWei = config.gasPriceFixedCost.toKWeiUInt(),
ethGasPriceMultiplier = config.legacyFeesMultiplier
),
config = config.extraDataCalculatorConfig,
variableFeesCalculator = boundedVariableCostCalculator,
legacyFeesCalculator = minMineableFeesCalculator
legacyFeesCalculator = legacyGasPricingCalculator
),
extraDataUpdater = ExtraDataV1UpdaterImpl(
httpJsonRpcClientFactory = httpJsonRpcClientFactory,
config = ExtraDataV1UpdaterImpl.Config(
config.extraDataUpdateRecipient,
config.requestRetryConfig
)
config = config.extraDataUpdaterConfig
)
)
} else {

View File

@@ -18,6 +18,7 @@ import net.consensys.linea.traces.TracesCountersV2
import net.consensys.linea.traces.TracingModuleV1
import net.consensys.linea.traces.TracingModuleV2
import net.consensys.linea.web3j.SmartContractErrors
import net.consensys.zkevm.coordinator.app.L2NetworkGasPricingService
import net.consensys.zkevm.coordinator.clients.prover.ProversConfig
import java.math.BigInteger
import java.net.URL
@@ -120,7 +121,7 @@ data class PersistenceRetryConfig(
override val timeout: Duration? = 10.minutes.toJavaDuration()
) : RetryConfig
private interface RequestRetryConfigurable {
internal interface RequestRetryConfigurable {
val requestRetry: RequestRetryConfigTomlFriendly
val requestRetryConfig: RequestRetryConfig
get() = requestRetry.asDomain
@@ -366,52 +367,6 @@ data class MessageAnchoringServiceConfig(
}
}
data class DynamicGasPriceServiceConfig(
val priceUpdateInterval: Duration,
val feeHistoryBlockCount: Int,
val feeHistoryRewardPercentile: Double,
val baseFeeCoefficient: Double,
val priorityFeeCoefficient: Double,
val baseFeeBlobCoefficient: Double,
val expectedBlobGas: Double,
val blobSubmissionExpectedExecutionGas: Double,
val gasPriceUpperBound: ULong,
val gasPriceLowerBound: ULong,
val gasPriceFixedCost: ULong,
val gethGasPriceUpdateRecipients: List<URL>,
val besuGasPriceUpdateRecipients: List<URL>,
override val requestRetry: RequestRetryConfigTomlFriendly,
@ConfigAlias("disabled") var _disabled: Boolean = false,
val extraDataEnabled: Boolean = false,
val minMineableFeesEnabled: Boolean = false,
val legacyFeesMultiplier: Double,
val margin: Double,
val extraDataUpdateRecipient: URL,
val variableCostUpperBound: ULong,
val variableCostLowerBound: ULong,
val _bytesPerDataSubmission: Double? // If it is set to null or any default value the test fails for some reason
) : FeatureToggleable, RequestRetryConfigurable {
val bytesPerDataSubmission = _bytesPerDataSubmission ?: expectedBlobGas
override val disabled: Boolean
get() =
_disabled || (gethGasPriceUpdateRecipients.isEmpty() && besuGasPriceUpdateRecipients.isEmpty())
init {
require(feeHistoryBlockCount > 0) { "feeHistoryBlockCount must be greater than 0" }
require(gasPriceUpperBound >= gasPriceLowerBound) {
"gasPriceUpperBound must be greater than or equal to gasPriceLowerBound"
}
require(variableCostUpperBound >= variableCostLowerBound) {
"variableCostUpperBound must be greater than or equal to variableCostLowerBound"
}
require(extraDataEnabled || minMineableFeesEnabled) {
"At least one of extraDataEnabled or minMineableFeesEnabled is required. If gas price updater is not required, " +
"disable it with `disabled` config instead"
}
}
}
data class L1DynamicGasPriceCapServiceConfig(
val gasPriceCapCalculation: GasPriceCapCalculation,
val feeHistoryFetcher: FeeHistoryFetcher,
@@ -573,7 +528,7 @@ data class CoordinatorConfigTomlDto(
val api: ApiConfig,
val l2Signer: SignerConfig,
val messageAnchoringService: MessageAnchoringServiceConfig,
val dynamicGasPriceService: DynamicGasPriceServiceConfig,
val l2NetworkGasPricing: L2NetworkGasPricingTomlDto,
val l1DynamicGasPriceCapService: L1DynamicGasPriceCapServiceConfig,
val testL1Disabled: Boolean = false,
val prover: ProverConfigTomlDto
@@ -598,7 +553,7 @@ data class CoordinatorConfigTomlDto(
api = api,
l2Signer = l2Signer,
messageAnchoringService = messageAnchoringService,
dynamicGasPriceService = dynamicGasPriceService,
l2NetworkGasPricingService = if (!testL1Disabled) l2NetworkGasPricing.reified() else null,
l1DynamicGasPriceCapService = l1DynamicGasPriceCapService,
testL1Disabled = testL1Disabled,
proversConfig = prover.reified()
@@ -625,7 +580,7 @@ data class CoordinatorConfig(
val api: ApiConfig,
val l2Signer: SignerConfig,
val messageAnchoringService: MessageAnchoringServiceConfig,
val dynamicGasPriceService: DynamicGasPriceServiceConfig,
val l2NetworkGasPricingService: L2NetworkGasPricingService.Config?,
val l1DynamicGasPriceCapService: L1DynamicGasPriceCapServiceConfig,
val testL1Disabled: Boolean = false,
val proversConfig: ProversConfig
@@ -650,7 +605,6 @@ data class CoordinatorConfig(
if (testL1Disabled) {
messageAnchoringService.disabled = true
dynamicGasPriceService._disabled = true
l1DynamicGasPriceCapService.disabled = true
}
}

View File

@@ -0,0 +1,190 @@
package net.consensys.zkevm.coordinator.app.config
import com.sksamuel.hoplite.ConfigAlias
import net.consensys.linea.ethereum.gaspricing.BoundableFeeCalculator
import net.consensys.linea.ethereum.gaspricing.staticcap.ExtraDataV1UpdaterImpl
import net.consensys.linea.ethereum.gaspricing.staticcap.FeeHistoryFetcherImpl
import net.consensys.linea.ethereum.gaspricing.staticcap.GasPriceUpdaterImpl
import net.consensys.linea.ethereum.gaspricing.staticcap.GasUsageRatioWeightedAverageFeesCalculator
import net.consensys.linea.ethereum.gaspricing.staticcap.MinerExtraDataV1CalculatorImpl
import net.consensys.linea.ethereum.gaspricing.staticcap.TransactionCostCalculator
import net.consensys.linea.ethereum.gaspricing.staticcap.VariableFeesCalculator
import net.consensys.toKWeiUInt
import net.consensys.zkevm.coordinator.app.L2NetworkGasPricingService
import java.net.URL
import java.time.Duration
import kotlin.time.toKotlinDuration
// Defaults to a compressed plain transfer transaction
data class SampleTransactionGasPricingTomlDto(
val plainTransferCostMultiplier: Double = 1.0,
val compressedTxSize: Int = 125,
val expectedGas: Int = 21000
)
data class LegacyGasPricingTomlDto(
val type: Type,
val naiveGasPricing: NaiveGasPricingTomlDto?,
val sampleTransactionGasPricing: SampleTransactionGasPricingTomlDto = SampleTransactionGasPricingTomlDto(),
val gasPriceUpperBound: ULong,
val gasPriceLowerBound: ULong
) {
enum class Type {
Naive,
SampleTransaction
}
init {
if (type == Type.Naive && naiveGasPricing == null) {
throw IllegalStateException("LegacyGasPricing $type configuration is null.")
}
require(gasPriceUpperBound >= gasPriceLowerBound) {
"gasPriceUpperBound must be greater than or equal to gasPriceLowerBound"
}
}
}
data class NaiveGasPricingTomlDto(
val baseFeeCoefficient: Double,
val priorityFeeCoefficient: Double,
val baseFeeBlobCoefficient: Double
)
data class VariableCostPricingTomlDto(
val gasPriceFixedCost: ULong,
val legacyFeesMultiplier: Double,
val margin: Double,
val variableCostUpperBound: ULong,
val variableCostLowerBound: ULong
) {
init {
require(variableCostUpperBound >= variableCostLowerBound) {
"variableCostUpperBound must be greater than or equal to variableCostLowerBound"
}
}
}
data class JsonRpcPricingPropagationTomlDto(
override var disabled: Boolean = false,
val gethGasPriceUpdateRecipients: List<URL>,
val besuGasPriceUpdateRecipients: List<URL>
) : FeatureToggleable {
init {
require(disabled || (gethGasPriceUpdateRecipients.isNotEmpty() || besuGasPriceUpdateRecipients.isNotEmpty())) {
"There is no point of enabling JSON RPC pricing propagation if there are no " +
"gethGasPriceUpdateRecipients or besuGasPriceUpdateRecipients defined"
}
}
}
data class ExtraDataPricingPropagationTomlDto(
override var disabled: Boolean = false,
val extraDataUpdateRecipient: URL
) : FeatureToggleable
data class L2NetworkGasPricingTomlDto(
override var disabled: Boolean = false,
override val requestRetry: RequestRetryConfigTomlFriendly,
val priceUpdateInterval: Duration,
val feeHistoryBlockCount: Int,
val feeHistoryRewardPercentile: Double,
val blobSubmissionExpectedExecutionGas: Int,
@ConfigAlias("bytesPerDataSubmission") val _bytesPerDataSubmission: Int?,
val l1BlobGas: Int,
val legacy: LegacyGasPricingTomlDto,
val variableCostPricing: VariableCostPricingTomlDto,
val jsonRpcPricingPropagation: JsonRpcPricingPropagationTomlDto,
val extraDataPricingPropagation: ExtraDataPricingPropagationTomlDto
) : FeatureToggleable, RequestRetryConfigurable {
init {
require(feeHistoryBlockCount > 0) { "feeHistoryBlockCount must be greater than 0" }
require(blobSubmissionExpectedExecutionGas > 0) { "blobSubmissionExpectedExecutionGas must be greater than 0" }
require(l1BlobGas > 0) { "l1BlobGas must be greater than 0" }
require(disabled || (jsonRpcPricingPropagation.enabled || extraDataPricingPropagation.enabled)) {
"There is no point of enabling L2 network gas pricing if " +
"both jsonRpcPricingPropagation and extraDataPricingPropagation are disabled"
}
}
private val bytesPerDataSubmission = _bytesPerDataSubmission ?: l1BlobGas
fun reified(): L2NetworkGasPricingService.Config {
val legacyGasPricingConfig = when (legacy.type) {
LegacyGasPricingTomlDto.Type.Naive -> {
L2NetworkGasPricingService.LegacyGasPricingCalculatorConfig(
transactionCostCalculatorConfig = null,
naiveGasPricingCalculatorConfig = GasUsageRatioWeightedAverageFeesCalculator.Config(
baseFeeCoefficient = legacy.naiveGasPricing!!.baseFeeCoefficient,
priorityFeeCoefficient = legacy.naiveGasPricing.priorityFeeCoefficient,
baseFeeBlobCoefficient = legacy.naiveGasPricing.baseFeeBlobCoefficient,
blobSubmissionExpectedExecutionGas = blobSubmissionExpectedExecutionGas,
expectedBlobGas = l1BlobGas
),
legacyGasPricingCalculatorBounds = BoundableFeeCalculator.Config(
legacy.gasPriceUpperBound.toDouble(),
legacy.gasPriceLowerBound.toDouble(),
0.0
)
)
}
LegacyGasPricingTomlDto.Type.SampleTransaction -> {
L2NetworkGasPricingService.LegacyGasPricingCalculatorConfig(
transactionCostCalculatorConfig = TransactionCostCalculator.Config(
sampleTransactionCostMultiplier = legacy.sampleTransactionGasPricing.plainTransferCostMultiplier,
fixedCostWei = variableCostPricing.gasPriceFixedCost,
compressedTxSize = legacy.sampleTransactionGasPricing.compressedTxSize,
expectedGas = legacy.sampleTransactionGasPricing.expectedGas
),
naiveGasPricingCalculatorConfig = null,
legacyGasPricingCalculatorBounds = BoundableFeeCalculator.Config(
legacy.gasPriceUpperBound.toDouble(),
legacy.gasPriceLowerBound.toDouble(),
0.0
)
)
}
}
return L2NetworkGasPricingService.Config(
feeHistoryFetcherConfig = FeeHistoryFetcherImpl.Config(
feeHistoryBlockCount = feeHistoryBlockCount.toUInt(),
feeHistoryRewardPercentile = feeHistoryRewardPercentile
),
jsonRpcPricingPropagationEnabled = jsonRpcPricingPropagation.enabled,
legacy = legacyGasPricingConfig,
jsonRpcGasPriceUpdaterConfig = GasPriceUpdaterImpl.Config(
gethEndpoints = jsonRpcPricingPropagation.gethGasPriceUpdateRecipients,
besuEndPoints = jsonRpcPricingPropagation.besuGasPriceUpdateRecipients,
retryConfig = requestRetryConfig
),
jsonRpcPriceUpdateInterval = priceUpdateInterval.toKotlinDuration(),
extraDataPricingPropagationEnabled = extraDataPricingPropagation.enabled,
extraDataUpdateInterval = priceUpdateInterval.toKotlinDuration(),
variableFeesCalculatorConfig = VariableFeesCalculator.Config(
blobSubmissionExpectedExecutionGas = blobSubmissionExpectedExecutionGas.toUInt(),
bytesPerDataSubmission = l1BlobGas.toUInt(),
expectedBlobGas = bytesPerDataSubmission.toUInt(),
margin = variableCostPricing.margin
),
variableFeesCalculatorBounds = BoundableFeeCalculator.Config(
feeUpperBound = variableCostPricing.variableCostUpperBound.toDouble(),
feeLowerBound = variableCostPricing.variableCostLowerBound.toDouble(),
feeMargin = 0.0
),
extraDataCalculatorConfig = MinerExtraDataV1CalculatorImpl.Config(
fixedCostInKWei = variableCostPricing.gasPriceFixedCost.toKWeiUInt(),
ethGasPriceMultiplier = variableCostPricing.legacyFeesMultiplier
),
extraDataUpdaterConfig = ExtraDataV1UpdaterImpl.Config(
extraDataPricingPropagation.extraDataUpdateRecipient,
requestRetryConfig
)
)
}
}

View File

@@ -6,12 +6,21 @@ import com.github.michaelbull.result.onFailure
import com.github.michaelbull.result.onSuccess
import com.sksamuel.hoplite.Masked
import net.consensys.linea.BlockParameter
import net.consensys.linea.ethereum.gaspricing.BoundableFeeCalculator
import net.consensys.linea.ethereum.gaspricing.staticcap.ExtraDataV1UpdaterImpl
import net.consensys.linea.ethereum.gaspricing.staticcap.FeeHistoryFetcherImpl
import net.consensys.linea.ethereum.gaspricing.staticcap.GasPriceUpdaterImpl
import net.consensys.linea.ethereum.gaspricing.staticcap.MinerExtraDataV1CalculatorImpl
import net.consensys.linea.ethereum.gaspricing.staticcap.TransactionCostCalculator
import net.consensys.linea.ethereum.gaspricing.staticcap.VariableFeesCalculator
import net.consensys.linea.jsonrpc.client.RequestRetryConfig
import net.consensys.linea.traces.TracesCountersV1
import net.consensys.linea.traces.TracesCountersV2
import net.consensys.linea.traces.TracingModuleV1
import net.consensys.linea.traces.TracingModuleV2
import net.consensys.linea.web3j.SmartContractErrors
import net.consensys.zkevm.coordinator.app.CoordinatorAppCli
import net.consensys.zkevm.coordinator.app.L2NetworkGasPricingService
import net.consensys.zkevm.coordinator.clients.prover.FileBasedProverConfig
import net.consensys.zkevm.coordinator.clients.prover.ProverConfig
import net.consensys.zkevm.coordinator.clients.prover.ProversConfig
@@ -28,7 +37,6 @@ import java.nio.file.Path
import java.time.Duration
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds
import kotlin.time.toJavaDuration
class CoordinatorConfigTest {
companion object {
@@ -399,39 +407,65 @@ class CoordinatorConfigTest {
maxMessagesToAnchor = 100U
)
private val dynamicGasPriceServiceConfig = DynamicGasPriceServiceConfig(
priceUpdateInterval = Duration.parse("PT12S"),
feeHistoryBlockCount = 50,
feeHistoryRewardPercentile = 15.0,
baseFeeCoefficient = 0.1,
priorityFeeCoefficient = 1.0,
baseFeeBlobCoefficient = 0.1,
blobSubmissionExpectedExecutionGas = 213_000.0,
expectedBlobGas = 131072.0,
gasPriceUpperBound = 10_000_000_000u,
gasPriceLowerBound = 90_000_000u,
gasPriceFixedCost = 3000000u,
gethGasPriceUpdateRecipients = listOf(
URI("http://traces-node:8545/").toURL(),
URI("http://l2-node:8545/").toURL()
private val l2NetworkGasPricingRequestRetryConfig = RequestRetryConfig(
maxRetries = 3u,
timeout = 6.seconds,
backoffDelay = 1.seconds,
failuresWarningThreshold = 2u
)
private val l2NetworkGasPricingServiceConfig = L2NetworkGasPricingService.Config(
feeHistoryFetcherConfig = FeeHistoryFetcherImpl.Config(
feeHistoryBlockCount = 50U,
feeHistoryRewardPercentile = 15.0
),
besuGasPriceUpdateRecipients = listOf(
URI("http://sequencer:8545/").toURL()
jsonRpcPricingPropagationEnabled = true,
legacy = L2NetworkGasPricingService.LegacyGasPricingCalculatorConfig(
legacyGasPricingCalculatorBounds = BoundableFeeCalculator.Config(
feeUpperBound = 10_000_000_000.0,
feeLowerBound = 90_000_000.0,
feeMargin = 0.0
),
transactionCostCalculatorConfig = TransactionCostCalculator.Config(
sampleTransactionCostMultiplier = 1.0,
fixedCostWei = 3000000u,
compressedTxSize = 125,
expectedGas = 21000
),
naiveGasPricingCalculatorConfig = null
),
requestRetry = RequestRetryConfigTomlFriendly(
maxRetries = 3,
timeout = 6.seconds.toJavaDuration(),
backoffDelay = 1.seconds.toJavaDuration(),
failuresWarningThreshold = 2
jsonRpcGasPriceUpdaterConfig = GasPriceUpdaterImpl.Config(
gethEndpoints = listOf(
URI("http://traces-node:8545/").toURL(),
URI("http://l2-node:8545/").toURL()
),
besuEndPoints = listOf(
URI("http://sequencer:8545/").toURL()
),
retryConfig = l2NetworkGasPricingRequestRetryConfig
),
extraDataEnabled = true,
minMineableFeesEnabled = true,
legacyFeesMultiplier = 1.2,
margin = 4.0,
extraDataUpdateRecipient = URI("http://sequencer:8545/").toURL(),
variableCostUpperBound = 10_000_000_001u,
variableCostLowerBound = 90_000_001u,
_bytesPerDataSubmission = null
jsonRpcPriceUpdateInterval = 12.seconds,
extraDataPricingPropagationEnabled = true,
extraDataUpdateInterval = 12.seconds,
variableFeesCalculatorConfig = VariableFeesCalculator.Config(
blobSubmissionExpectedExecutionGas = 213_000u,
bytesPerDataSubmission = 131072u,
expectedBlobGas = 131072u,
margin = 4.0
),
variableFeesCalculatorBounds = BoundableFeeCalculator.Config(
feeUpperBound = 10_000_000_001.0,
feeLowerBound = 90_000_001.0,
feeMargin = 0.0
),
extraDataCalculatorConfig = MinerExtraDataV1CalculatorImpl.Config(
fixedCostInKWei = 3000u,
ethGasPriceMultiplier = 1.2
),
extraDataUpdaterConfig = ExtraDataV1UpdaterImpl.Config(
sequencerEndpoint = URI(/* str = */ "http://sequencer:8545/").toURL(),
retryConfig = l2NetworkGasPricingRequestRetryConfig
)
)
private val l1DynamicGasPriceCapServiceConfig = L1DynamicGasPriceCapServiceConfig(
@@ -648,7 +682,7 @@ class CoordinatorConfigTest {
api = apiConfig,
l2Signer = l2SignerConfig,
messageAnchoringService = messageAnchoringServiceConfig,
dynamicGasPriceService = dynamicGasPriceServiceConfig,
l2NetworkGasPricingService = l2NetworkGasPricingServiceConfig,
l1DynamicGasPriceCapService = l1DynamicGasPriceCapServiceConfig,
proversConfig = proversConfig
)

View File

@@ -0,0 +1,363 @@
package net.consensys.zkevm.coordinator.app.config
import com.sksamuel.hoplite.ConfigLoaderBuilder
import com.sksamuel.hoplite.toml.TomlPropertySource
import net.consensys.linea.ethereum.gaspricing.BoundableFeeCalculator
import net.consensys.linea.ethereum.gaspricing.staticcap.ExtraDataV1UpdaterImpl
import net.consensys.linea.ethereum.gaspricing.staticcap.FeeHistoryFetcherImpl
import net.consensys.linea.ethereum.gaspricing.staticcap.GasPriceUpdaterImpl
import net.consensys.linea.ethereum.gaspricing.staticcap.GasUsageRatioWeightedAverageFeesCalculator
import net.consensys.linea.ethereum.gaspricing.staticcap.MinerExtraDataV1CalculatorImpl
import net.consensys.linea.ethereum.gaspricing.staticcap.TransactionCostCalculator
import net.consensys.linea.ethereum.gaspricing.staticcap.VariableFeesCalculator
import net.consensys.linea.jsonrpc.client.RequestRetryConfig
import net.consensys.zkevm.coordinator.app.L2NetworkGasPricingService
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import java.net.URI
import java.time.Duration
import kotlin.time.Duration.Companion.seconds
import kotlin.time.toJavaDuration
class L2NetworkGasPricingConfigTest {
data class Config(
val l2NetworkGasPricing: L2NetworkGasPricingTomlDto
)
private fun parseConfig(toml: String): L2NetworkGasPricingTomlDto {
return ConfigLoaderBuilder
.default()
.addSource(TomlPropertySource(toml))
.build()
.loadConfigOrThrow<Config>().l2NetworkGasPricing
}
private val naiveL2NetworkGasPricingServiceConfigToml = """
[l2-network-gas-pricing]
disabled = false
price-update-interval = "PT12S"
fee-history-block-count = 50
fee-history-reward-percentile = 15
blob-submission-expected-execution-gas = 213000.0 # Lower to 120k as we improve efficiency
# Defaults to expected-blob-gas
#bytes-per-data-submission=131072.0 # 2^17
l1-blob-gas = 131072 # 2^17
[l2-network-gas-pricing.request-retry]
max-retries = 3
timeout = "PT6S"
backoff-delay = "PT1S"
failures-warning-threshold = 2
[l2-network-gas-pricing.variable-cost-pricing]
gas-price-fixed-cost = 3000000
legacy-fees-multiplier = 1.2
margin = 4.0
variable-cost-upper-bound = 10000000001 # ~10 GWEI
variable-cost-lower-bound = 90000001 # ~0.09 GWEI
[l2-network-gas-pricing.extra-data-pricing-propagation]
extra-data-update-recipient = "http://sequencer:8545/"
[l2-network-gas-pricing.legacy]
type="Naive"
gas-price-upper-bound = 10000000000 # 10 GWEI
gas-price-lower-bound = 90000000 # 0.09 GWEI
[l2-network-gas-pricing.legacy.naive-gas-pricing]
base-fee-coefficient = 0.1
priority-fee-coefficient = 1.0
base-fee-blob-coefficient = 0.1
[l2-network-gas-pricing.json-rpc-pricing-propagation]
geth-gas-price-update-recipients = [
"http://traces-node:8545/",
"http://l2-node:8545/"
]
besu-gas-price-update-recipients = [
"http://sequencer:8545/"
]
""".trimIndent()
private val sampleTransactionL2NetworkGasPricingServiceConfigToml = """
[l2-network-gas-pricing]
disabled = false
price-update-interval = "PT12S"
fee-history-block-count = 50
fee-history-reward-percentile = 15
blob-submission-expected-execution-gas = 213000.0 # Lower to 120k as we improve efficiency
# Defaults to expected-blob-gas
#bytes-per-data-submission=131072.0 # 2^17
l1-blob-gas = 131072 # 2^17
[l2-network-gas-pricing.request-retry]
max-retries = 3
timeout = "PT6S"
backoff-delay = "PT1S"
failures-warning-threshold = 2
[l2-network-gas-pricing.variable-cost-pricing]
gas-price-fixed-cost = 3000000
legacy-fees-multiplier = 1.2
margin = 4.0
variable-cost-upper-bound = 10000000001 # ~10 GWEI
variable-cost-lower-bound = 90000001 # ~0.09 GWEI
[l2-network-gas-pricing.extra-data-pricing-propagation]
extra-data-update-recipient = "http://sequencer:8545/"
[l2-network-gas-pricing.legacy]
type="SampleTransaction"
gas-price-upper-bound = 10000000000 # 10 GWEI
gas-price-lower-bound = 90000000 # 0.09 GWEI
[l2-network-gas-pricing.json-rpc-pricing-propagation]
geth-gas-price-update-recipients = [
"http://traces-node:8545/",
"http://l2-node:8545/"
]
besu-gas-price-update-recipients = [
"http://sequencer:8545/"
]
""".trimIndent()
@Test
fun `dto with naive legacy gas calculator is parseable`() {
val config = parseConfig(naiveL2NetworkGasPricingServiceConfigToml)
assertThat(config).isEqualTo(
L2NetworkGasPricingTomlDto(
requestRetry = RequestRetryConfigTomlFriendly(
maxRetries = 3,
timeout = 6.seconds.toJavaDuration(),
backoffDelay = 1.seconds.toJavaDuration(),
failuresWarningThreshold = 2
),
priceUpdateInterval = Duration.parse("PT12S"),
feeHistoryBlockCount = 50,
feeHistoryRewardPercentile = 15.0,
blobSubmissionExpectedExecutionGas = 213_000,
_bytesPerDataSubmission = null,
l1BlobGas = 131072,
legacy = LegacyGasPricingTomlDto(
type = LegacyGasPricingTomlDto.Type.Naive,
gasPriceUpperBound = 10_000_000_000u,
gasPriceLowerBound = 90_000_000u,
naiveGasPricing = NaiveGasPricingTomlDto(
baseFeeCoefficient = 0.1,
priorityFeeCoefficient = 1.0,
baseFeeBlobCoefficient = 0.1
)
),
variableCostPricing = VariableCostPricingTomlDto(
gasPriceFixedCost = 3000000u,
legacyFeesMultiplier = 1.2,
margin = 4.0,
variableCostUpperBound = 10_000_000_001u,
variableCostLowerBound = 90_000_001u
),
jsonRpcPricingPropagation = JsonRpcPricingPropagationTomlDto(
gethGasPriceUpdateRecipients = listOf(
URI("http://traces-node:8545/").toURL(),
URI("http://l2-node:8545/").toURL()
),
besuGasPriceUpdateRecipients = listOf(
URI("http://sequencer:8545/").toURL()
)
),
extraDataPricingPropagation = ExtraDataPricingPropagationTomlDto(
extraDataUpdateRecipient = URI("http://sequencer:8545/").toURL()
)
)
)
}
@Test
fun `dto with sample transaction legacy gas calculator is parseable`() {
val config = parseConfig(sampleTransactionL2NetworkGasPricingServiceConfigToml)
assertThat(config).isEqualTo(
L2NetworkGasPricingTomlDto(
requestRetry = RequestRetryConfigTomlFriendly(
maxRetries = 3,
timeout = 6.seconds.toJavaDuration(),
backoffDelay = 1.seconds.toJavaDuration(),
failuresWarningThreshold = 2
),
priceUpdateInterval = Duration.parse("PT12S"),
feeHistoryBlockCount = 50,
feeHistoryRewardPercentile = 15.0,
blobSubmissionExpectedExecutionGas = 213_000,
_bytesPerDataSubmission = null,
l1BlobGas = 131072,
legacy = LegacyGasPricingTomlDto(
type = LegacyGasPricingTomlDto.Type.SampleTransaction,
gasPriceUpperBound = 10_000_000_000u,
gasPriceLowerBound = 90_000_000u,
naiveGasPricing = null
),
variableCostPricing = VariableCostPricingTomlDto(
gasPriceFixedCost = 3000000u,
legacyFeesMultiplier = 1.2,
margin = 4.0,
variableCostUpperBound = 10_000_000_001u,
variableCostLowerBound = 90_000_001u
),
jsonRpcPricingPropagation = JsonRpcPricingPropagationTomlDto(
gethGasPriceUpdateRecipients = listOf(
URI("http://traces-node:8545/").toURL(),
URI("http://l2-node:8545/").toURL()
),
besuGasPriceUpdateRecipients = listOf(
URI("http://sequencer:8545/").toURL()
)
),
extraDataPricingPropagation = ExtraDataPricingPropagationTomlDto(
extraDataUpdateRecipient = URI("http://sequencer:8545/").toURL()
)
)
)
}
@Test
fun `reification is correct`() {
val config = parseConfig(naiveL2NetworkGasPricingServiceConfigToml).reified()
val l2NetworkGasPricingRequestretryConfig = RequestRetryConfig(
maxRetries = 3u,
timeout = 6.seconds,
backoffDelay = 1.seconds,
failuresWarningThreshold = 2u
)
assertThat(config).isEqualTo(
L2NetworkGasPricingService.Config(
feeHistoryFetcherConfig = FeeHistoryFetcherImpl.Config(
feeHistoryBlockCount = 50U,
feeHistoryRewardPercentile = 15.0
),
jsonRpcPricingPropagationEnabled = true,
legacy = L2NetworkGasPricingService.LegacyGasPricingCalculatorConfig(
naiveGasPricingCalculatorConfig = GasUsageRatioWeightedAverageFeesCalculator.Config(
baseFeeCoefficient = 0.1,
priorityFeeCoefficient = 1.0,
baseFeeBlobCoefficient = 0.1,
blobSubmissionExpectedExecutionGas = 213_000,
expectedBlobGas = 131072
),
legacyGasPricingCalculatorBounds = BoundableFeeCalculator.Config(
10_000_000_000.0,
90_000_000.0,
0.0
),
transactionCostCalculatorConfig = null
),
jsonRpcGasPriceUpdaterConfig = GasPriceUpdaterImpl.Config(
gethEndpoints = listOf(
URI("http://traces-node:8545/").toURL(),
URI("http://l2-node:8545/").toURL()
),
besuEndPoints = listOf(
URI("http://sequencer:8545/").toURL()
),
retryConfig = l2NetworkGasPricingRequestretryConfig
),
jsonRpcPriceUpdateInterval = 12.seconds,
extraDataPricingPropagationEnabled = true,
extraDataUpdateInterval = 12.seconds,
variableFeesCalculatorConfig = VariableFeesCalculator.Config(
blobSubmissionExpectedExecutionGas = 213_000u,
bytesPerDataSubmission = 131072u,
expectedBlobGas = 131072u,
margin = 4.0
),
variableFeesCalculatorBounds = BoundableFeeCalculator.Config(
feeUpperBound = 10_000_000_001.0,
feeLowerBound = 90_000_001.0,
feeMargin = 0.0
),
extraDataCalculatorConfig = MinerExtraDataV1CalculatorImpl.Config(
fixedCostInKWei = 3000u,
ethGasPriceMultiplier = 1.2
),
extraDataUpdaterConfig = ExtraDataV1UpdaterImpl.Config(
sequencerEndpoint = URI(/* str = */ "http://sequencer:8545/").toURL(),
retryConfig = l2NetworkGasPricingRequestretryConfig
)
)
)
}
@Test
fun `Sample transaction reification is correct`() {
val config = parseConfig(sampleTransactionL2NetworkGasPricingServiceConfigToml).reified()
val l2NetworkGasPricingRequestretryConfig = RequestRetryConfig(
maxRetries = 3u,
timeout = 6.seconds,
backoffDelay = 1.seconds,
failuresWarningThreshold = 2u
)
assertThat(config).isEqualTo(
L2NetworkGasPricingService.Config(
feeHistoryFetcherConfig = FeeHistoryFetcherImpl.Config(
feeHistoryBlockCount = 50U,
feeHistoryRewardPercentile = 15.0
),
jsonRpcPricingPropagationEnabled = true,
legacy = L2NetworkGasPricingService.LegacyGasPricingCalculatorConfig(
naiveGasPricingCalculatorConfig = null,
legacyGasPricingCalculatorBounds = BoundableFeeCalculator.Config(
10_000_000_000.0,
90_000_000.0,
0.0
),
transactionCostCalculatorConfig = TransactionCostCalculator.Config(
sampleTransactionCostMultiplier = 1.0,
fixedCostWei = 3000000u,
compressedTxSize = 125,
expectedGas = 21000
)
),
jsonRpcGasPriceUpdaterConfig = GasPriceUpdaterImpl.Config(
gethEndpoints = listOf(
URI("http://traces-node:8545/").toURL(),
URI("http://l2-node:8545/").toURL()
),
besuEndPoints = listOf(
URI("http://sequencer:8545/").toURL()
),
retryConfig = l2NetworkGasPricingRequestretryConfig
),
jsonRpcPriceUpdateInterval = 12.seconds,
extraDataPricingPropagationEnabled = true,
extraDataUpdateInterval = 12.seconds,
variableFeesCalculatorConfig = VariableFeesCalculator.Config(
blobSubmissionExpectedExecutionGas = 213_000u,
bytesPerDataSubmission = 131072u,
expectedBlobGas = 131072u,
margin = 4.0
),
variableFeesCalculatorBounds = BoundableFeeCalculator.Config(
feeUpperBound = 10_000_000_001.0,
feeLowerBound = 90_000_001.0,
feeMargin = 0.0
),
extraDataCalculatorConfig = MinerExtraDataV1CalculatorImpl.Config(
fixedCostInKWei = 3000u,
ethGasPriceMultiplier = 1.2
),
extraDataUpdaterConfig = ExtraDataV1UpdaterImpl.Config(
sequencerEndpoint = URI(/* str = */ "http://sequencer:8545/").toURL(),
retryConfig = l2NetworkGasPricingRequestretryConfig
)
)
)
}
}

View File

@@ -5,22 +5,22 @@ import net.consensys.zkevm.domain.BlockInterval
import tech.pegasys.teku.infrastructure.async.SafeFuture
class StartBlockNumberBasedSwitchPredicate<ProofRequest>(
val switchStartBlockNumberInclusive: ULong
private val switchStartBlockNumberInclusive: ULong
) where ProofRequest : BlockInterval {
fun invoke(proofRequest: ProofRequest): Boolean = proofRequest.startBlockNumber >= switchStartBlockNumberInclusive
}
class ABProverClientRouter<ProofRequest, ProofResponse>(
val proverA: ProverClient<ProofRequest, ProofResponse>,
val proverB: ProverClient<ProofRequest, ProofResponse>,
val switchToProverBPredicate: (ProofRequest) -> Boolean
private val proverA: ProverClient<ProofRequest, ProofResponse>,
private val proverB: ProverClient<ProofRequest, ProofResponse>,
private val switchToProverBPredicate: (ProofRequest) -> Boolean
) : ProverClient<ProofRequest, ProofResponse> {
override fun requestProof(proofRequest: ProofRequest): SafeFuture<ProofResponse> {
if (switchToProverBPredicate(proofRequest)) {
return proverB.requestProof(proofRequest)
return if (switchToProverBPredicate(proofRequest)) {
proverB.requestProof(proofRequest)
} else {
return proverA.requestProof(proofRequest)
proverA.requestProof(proofRequest)
}
}
}

View File

@@ -125,7 +125,6 @@ class FileBasedExecutionProverClientV2(
override fun parseResponse(
responseFilePath: Path,
proofIndex: ProofIndex
): SafeFuture<BatchExecutionProofResponse> {
return SafeFuture.completedFuture(
BatchExecutionProofResponse(

View File

@@ -138,7 +138,7 @@ open class GenericFileBasedProverClient<Request, Response, RequestDto, ResponseD
)
}
open fun parseResponse(
protected open fun parseResponse(
responseFilePath: Path,
proofIndex: ProofIndex
): SafeFuture<Response> {

View File

@@ -15,7 +15,7 @@ import org.web3j.protocol.Web3j
class ProverClientFactory(
private val vertx: Vertx,
private val config: ProversConfig,
private val metricsFacade: MetricsFacade
metricsFacade: MetricsFacade
) {
private val executionWaitingResponsesMetric = GaugeAggregator()
private val blobWaitingResponsesMetric = GaugeAggregator()

View File

@@ -10,7 +10,7 @@ interface FeesFetcher {
fun getL1EthGasPriceData(): SafeFuture<FeeHistory>
}
interface FeesCalculator {
fun interface FeesCalculator {
fun calculateFees(feeHistory: FeeHistory): Double
}

View File

@@ -91,8 +91,8 @@ class MinMineableFeesPricerServiceIntegrationTest {
baseFeeCoefficient = 0.1,
priorityFeeCoefficient = 0.1,
baseFeeBlobCoefficient = 0.1,
blobSubmissionExpectedExecutionGas = 131_000.0,
expectedBlobGas = 120_000.0
blobSubmissionExpectedExecutionGas = 131_000,
expectedBlobGas = 120_000
)
)
)

View File

@@ -10,7 +10,7 @@ class GasPriceUpdaterImpl(
private val httpJsonRpcClientFactory: VertxHttpJsonRpcClientFactory,
private val config: Config
) : GasPriceUpdater {
class Config(
data class Config(
val gethEndpoints: List<URL>,
val besuEndPoints: List<URL>,
val retryConfig: RequestRetryConfig

View File

@@ -19,8 +19,8 @@ class GasUsageRatioWeightedAverageFeesCalculator(
val baseFeeCoefficient: Double,
val priorityFeeCoefficient: Double,
val baseFeeBlobCoefficient: Double,
val blobSubmissionExpectedExecutionGas: Double,
val expectedBlobGas: Double
val blobSubmissionExpectedExecutionGas: Int,
val expectedBlobGas: Int
)
private val log: Logger = LogManager.getLogger(this::class.java)

View File

@@ -0,0 +1,32 @@
package net.consensys.linea.ethereum.gaspricing.staticcap
import net.consensys.linea.FeeHistory
import net.consensys.linea.ethereum.gaspricing.FeesCalculator
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
/**
* This calculator is supposed to compute cost of a plain transfer transaction and multiply it by a constant
* Cost of a plain transfer transaction should be computed the same way as on Sequencer
* @see <a href=”https://github.com/Consensys/linea-sequencer/blob/main/sequencer/src/main/java/net/consensys/linea/bl/TransactionProfitabilityCalculator.java#L38”>this</a>
* */
class TransactionCostCalculator(
private val dataCostCalculator: FeesCalculator,
private val config: Config
) : FeesCalculator {
data class Config(
val sampleTransactionCostMultiplier: Double,
val fixedCostWei: ULong,
val compressedTxSize: Int = 125,
val expectedGas: Int = 21000
)
private val log: Logger = LogManager.getLogger(this::class.java)
override fun calculateFees(feeHistory: FeeHistory): Double {
val dataCostCost = dataCostCalculator.calculateFees(feeHistory)
log.trace("Data cost: $dataCostCost")
return config.sampleTransactionCostMultiplier *
((dataCostCost * config.compressedTxSize / config.expectedGas) + config.fixedCostWei.toDouble())
}
}

View File

@@ -18,16 +18,12 @@ class VariableFeesCalculator(
val averageWeightedBlobBaseFeesCalculator: FeesCalculator = AverageWeightedBlobBaseFeesCalculator
) : FeesCalculator {
data class Config(
val blobSubmissionExpectedExecutionGas: Double,
val bytesPerDataSubmission: Double,
val expectedBlobGas: Double,
val blobSubmissionExpectedExecutionGas: UInt,
val bytesPerDataSubmission: UInt,
val expectedBlobGas: UInt,
val margin: Double
)
init {
require(config.bytesPerDataSubmission > 0.0)
}
private val log = LogManager.getLogger(this::class.java)
override fun calculateFees(feeHistory: FeeHistory): Double {
@@ -36,11 +32,11 @@ class VariableFeesCalculator(
val averageWeightedBlobBaseFees = averageWeightedBlobBaseFeesCalculator.calculateFees(feeHistory)
val executionFee = (averageWeightedBaseFees + averageWeightedPriorityFees) *
config.blobSubmissionExpectedExecutionGas
config.blobSubmissionExpectedExecutionGas.toDouble()
val blobFee = averageWeightedBlobBaseFees * config.expectedBlobGas
val blobFee = averageWeightedBlobBaseFees * config.expectedBlobGas.toDouble()
val variableFee = ((executionFee + blobFee) * config.margin) / config.bytesPerDataSubmission
val variableFee = ((executionFee + blobFee) * config.margin) / config.bytesPerDataSubmission.toDouble()
log.debug(
"Calculated variableFee={} wei executionFee={} wei blobFee={} wei bytesPerDataSubmission={} l1Blocks={}",

View File

@@ -9,8 +9,8 @@ class GasUsageRatioWeightedAverageFeesCalculatorTest {
baseFeeCoefficient = 0.1,
priorityFeeCoefficient = 1.2,
baseFeeBlobCoefficient = 0.1,
blobSubmissionExpectedExecutionGas = 120_000.0,
expectedBlobGas = 131_000.0
blobSubmissionExpectedExecutionGas = 120_000,
expectedBlobGas = 131_000
)
private val feesCalculator = GasUsageRatioWeightedAverageFeesCalculator(config)

View File

@@ -0,0 +1,59 @@
package net.consensys.linea.ethereum.gaspricing.staticcap
import net.consensys.linea.FeeHistory
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import kotlin.math.abs
class TransactionCostCalculatorTest {
private val config = TransactionCostCalculator.Config(1.0, 30000000U)
private val oldCalculatorConfig = GasUsageRatioWeightedAverageFeesCalculator.Config(
// We may want to price L2 gas cheaper than L1 gas
baseFeeCoefficient = 0.02,
priorityFeeCoefficient = 0.02,
baseFeeBlobCoefficient = 0.02,
blobSubmissionExpectedExecutionGas = 69000,
expectedBlobGas = 131_000
)
private val legacyFeesCalculator = GasUsageRatioWeightedAverageFeesCalculator(oldCalculatorConfig)
private val variableFeesCalculatorConfig = VariableFeesCalculator.Config(
margin = 1.2,
bytesPerDataSubmission = 131_000u,
blobSubmissionExpectedExecutionGas = 69000u,
expectedBlobGas = 131_000u
)
private val variableFeesCalculator = VariableFeesCalculator(variableFeesCalculatorConfig)
private val transactionCostCalculator = TransactionCostCalculator(variableFeesCalculator, config)
private val gWei = 1000000000UL
@Test
fun transactionCostCalculatorIsLessSusceptibleToGasSpikes() {
val regularL1Fees = FeeHistory(
oldestBlock = 100uL,
baseFeePerGas = listOf(gWei),
reward = listOf(listOf(1000UL)), // not a big impact
gasUsedRatio = listOf(0.25),
baseFeePerBlobGas = listOf(100UL),
blobGasUsedRatio = listOf(0.25)
)
val legacyGasPriceUnderRegularConditions = legacyFeesCalculator.calculateFees(regularL1Fees)
val transactionCostUnderRegularConditions = transactionCostCalculator.calculateFees(regularL1Fees)
// Under regular conditions calculators are comparable
assertThat(differenceInPercentage(legacyGasPriceUnderRegularConditions, transactionCostUnderRegularConditions))
.isLessThan(1.0)
val blobGasSpikeL1Fees =
regularL1Fees.copy(baseFeePerBlobGas = regularL1Fees.baseFeePerBlobGas.map { 1000UL * gWei })
val legacyGasPriceDuringGasSpike = legacyFeesCalculator.calculateFees(blobGasSpikeL1Fees)
val transactionCostDuringGasSpike = transactionCostCalculator.calculateFees(blobGasSpikeL1Fees)
// But during gas spike legacy gas calculator becomes less stable
assertThat(legacyGasPriceDuringGasSpike / transactionCostDuringGasSpike).isGreaterThan(2.0)
}
private fun differenceInPercentage(a: Double, b: Double) = abs(a - b) / b
}

View File

@@ -11,9 +11,9 @@ class VariableFeesCalculatorTest {
private val config = VariableFeesCalculator.Config(
margin = 1.1,
bytesPerDataSubmission = 200_000.0,
blobSubmissionExpectedExecutionGas = 120_000.0,
expectedBlobGas = 131_000.0
bytesPerDataSubmission = 200_000u,
blobSubmissionExpectedExecutionGas = 120_000u,
expectedBlobGas = 131_000u
)
@Test

View File

@@ -13,7 +13,7 @@
"scenarioType": "SelfTransactionWithPayload",
"wallet": "source",
"nbTransfers": 1,
"payload": "custom payload"
"payload": ""
}
}
]