diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 586bcf3e17..a83344864f 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -65,7 +65,6 @@ jobs:
- '.github/workflows/staterecovery-*.yml'
- '.github/workflows/main.yml'
- '.github/workflows/reuse-*.yml'
- - 'e2e/**'
postman:
- 'postman/**'
- 'sdk/**'
diff --git a/.github/workflows/reuse-linea-besu-package-run-e2e-tests.yml b/.github/workflows/reuse-linea-besu-package-run-e2e-tests.yml
index 6341488ba7..07f6a01fa6 100644
--- a/.github/workflows/reuse-linea-besu-package-run-e2e-tests.yml
+++ b/.github/workflows/reuse-linea-besu-package-run-e2e-tests.yml
@@ -78,12 +78,10 @@ jobs:
- name: Replace expected traces api version in coordinator config file
shell: bash
run: |
- sed -i 's/^\(expected-traces-api-version-v2=\).*/\1"${{ env.EXPECTED_TRACES_API_VERSION }}"/' config/coordinator/coordinator-docker.config.toml
- sed -i 's/^\(expected-traces-api-version-v2=\).*/\1"${{ env.EXPECTED_TRACES_API_VERSION }}"/' config/coordinator/coordinator-docker-traces-v2-override.config.toml
+ sed -i 's/^\(expected-traces-api-version[ ]*=[ ]*\).*/\1"${{ env.EXPECTED_TRACES_API_VERSION }}"/' config/coordinator/coordinator-config-v2.toml
echo "EXPECTED_TRACES_API_VERSION=${{ env.EXPECTED_TRACES_API_VERSION }}"
echo "BESU_PACKAGE_TAG=${{ env.BESU_PACKAGE_TAG }}"
- echo "$(grep expected-traces-api-version-v2 config/coordinator/coordinator-docker.config.toml)"
- echo "$(grep expected-traces-api-version-v2 config/coordinator/coordinator-docker-traces-v2-override.config.toml)"
+ echo "$(grep expected-traces-api-version config/coordinator/coordinator-config-v2.toml)"
- name: Spin up fresh environment with besu tracing with retry
uses: nick-fields/retry@v3
with:
diff --git a/.run/Coordinator.run.xml.template b/.run/Coordinator.run.xml.template
index 85bc6379a0..7ef9711f41 100644
--- a/.run/Coordinator.run.xml.template
+++ b/.run/Coordinator.run.xml.template
@@ -4,7 +4,7 @@
-
+
diff --git a/Makefile b/Makefile
index 41701d7d21..db0bf4aca6 100644
--- a/Makefile
+++ b/Makefile
@@ -67,7 +67,7 @@ start-env-with-tracing-v2:
## Enable L2 geth node
start-env-with-tracing-v2-extra:
- make start-env COMPOSE_PROFILES:=l1,l2 COMPOSE_FILE:=docker/compose-tracing-v2-extra-extension.yml LINEA_PROTOCOL_CONTRACTS_ONLY=true DISABLE_JSON_RPC_PRICING_PROPAGATION=false DISABLE_TYPE2_STATE_PROOF_PROVIDER=false
+ make start-env COMPOSE_PROFILES:=l1,l2 COMPOSE_FILE:=docker/compose-tracing-v2-extra-extension.yml LINEA_PROTOCOL_CONTRACTS_ONLY=true DISABLE_TYPE2_STATE_PROOF_PROVIDER=false
start-env-with-tracing-v2-ci:
make start-env COMPOSE_FILE=docker/compose-tracing-v2-ci-extension.yml DISABLE_TYPE2_STATE_PROOF_PROVIDER=false
diff --git a/config/coordinator/coordinator-config-v2-override-local-dev.toml b/config/coordinator/coordinator-config-v2-override-local-dev.toml
new file mode 100644
index 0000000000..6b6b1bdd60
--- /dev/null
+++ b/config/coordinator/coordinator-config-v2-override-local-dev.toml
@@ -0,0 +1,58 @@
+[defaults]
+l1-endpoint = "http://127.0.0.1:8445"
+l2-endpoint = "http://127.0.0.1:8545"
+
+[prover]
+[prover.execution]
+fs-requests-directory = "tmp/local/prover/v3/execution/requests"
+fs-responses-directory = "tmp/local/prover/v3/execution/responses"
+[prover.blob-compression]
+fs-requests-directory = "tmp/local/prover/v3/compression/requests"
+fs-responses-directory = "tmp/local/prover/v3/compression/responses"
+[prover.proof-aggregation]
+fs-requests-directory = "tmp/local/prover/v3/aggregation/requests"
+fs-responses-directory = "tmp/local/prover/v3/aggregation/responses"
+
+[traces]
+[traces.counters]
+endpoints = ["http://127.0.0.1:8745/"]
+[traces.conflation]
+endpoints = ["http://127.0.0.1:8745/"]
+
+[state-manager]
+endpoints = ["http://127.0.0.1:8998/"]
+
+[type2-state-proof-provider]
+disabled = true
+
+[l1-finalization-monitor]
+l1-query-block-tag="LATEST"
+
+[l1-submission.blob.signer]
+type = "Web3j"
+
+[l1-submission.aggregation.signer]
+type = "Web3j"
+
+[message-anchoring]
+disabled = false
+l1-highest-block-tag="LATEST"
+l2-highest-block-tag="LATEST"
+anchoring-tick-interval = "PT1S"
+
+[message-anchoring.l1-event-scraping]
+polling-interval = "PT1S"
+
+[message-anchoring.signer]
+type = "Web3j"
+
+[l2-network-gas-pricing]
+disabled = false
+extra-data-update-endpoint = "http://127.0.0.1:8545/"
+
+[database]
+hostname = "127.0.0.1"
+port = "5432"
+
+[api]
+observability_port = 9545
diff --git a/config/coordinator/coordinator-config-v2.toml b/config/coordinator/coordinator-config-v2.toml
new file mode 100644
index 0000000000..979e7f7969
--- /dev/null
+++ b/config/coordinator/coordinator-config-v2.toml
@@ -0,0 +1,287 @@
+[defaults]
+l1-endpoint = "http://l1-el-node:8545"
+l2-endpoint = "http://sequencer:8545"
+
+[protocol]
+[protocol.genesis]
+genesis-state-root-hash = "0x072ead6777750dc20232d1cee8dc9a395c2d350df4bbaa5096c6f59b214dcecd"
+# shnarf for contract V5
+# Keccak256(parentShnarf="0x00...00", snarkHash="0x00...00",
+# parentStateRootHash="0x072ead6777750dc20232d1cee8dc9a395c2d350df4bbaa5096c6f59b214dcecd",
+# evaludationClaim="0x00...00", evaludationPoint="0x00...00")
+genesis-shnarf = "0x47452a1b9ebadfe02bdd02f580fa1eba17680d57eec968a591644d05d78ee84f"
+[protocol.l1]
+contract-address = "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9"
+block-time = "PT1S"
+[protocol.l2]
+contract-address = "0xe537D669CA013d86EBeF1D64e40fC74CADC91987"
+contract-deployment-block-number = 3
+
+[conflation]
+disabled = false
+blocks-limit = 2
+new-blocks-polling-interval="PT1S"
+conflation-deadline = "PT6S" # =3*l2_block_time
+conflation-deadline-check-interval = "PT3S"
+conflation-deadline-last-block-confirmation-delay = "PT2S" # recommended: at least 2 * blockInterval
+l2-fetch-blocks-limit = 4000
+force-stop-conflation-at-block-inclusive=100_000_000
+
+# This is to prevent inflight trasactions that may change Smart contract state while coordinator is restarted.
+# Queries SMC for last finalised block, and keeps polling until this number of blocks observe the same state.
+# If state is updated meanwhile, it resets counter and restarts the polling.
+consistent-number-of-blocks-on-l1-to-wait = 1
+l2-endpoint = "http://sequencer:8545"
+l2-logs-endpoint = "http://sequencer:8545"
+
+[conflation.l2-request-retries]
+backoff-delay = "PT1S"
+failures-warning-threshold = 3
+
+
+[conflation.blob-compression]
+blob-compressor-version="V1_2"
+blob-size-limit = 102400 # 100KB
+handler-polling-interval = "PT1S"
+# default batches limit is aggregation-proofs-limit -1
+# batches-limit must be less than or equal to aggregation-proofs-limit-1
+batches-limit = 1
+
+[conflation.proof-aggregation]
+proofs-limit = 3
+deadline = "PT1M"
+coordinator-polling-interval = "PT2S"
+deadline-check-interval = "PT8S"
+target-end-blocks = []
+
+[prover]
+version = "v3.0.0"
+[prover.execution]
+fs-requests-directory = "/data/prover/v3/execution/requests"
+fs-responses-directory = "/data/prover/v3/execution/responses"
+[prover.blob-compression]
+fs-requests-directory = "/data/prover/v3/compression/requests"
+fs-responses-directory = "/data/prover/v3/compression/responses"
+[prover.proof-aggregation]
+fs-requests-directory = "/data/prover/v3/aggregation/requests"
+fs-responses-directory = "/data/prover/v3/aggregation/responses"
+
+#[prover.new]
+#switch-block-number-inclusive=1000
+#[prover.new.execution]
+#fs-requests-directory = "/data/prover/v3/execution/requests"
+#fs-responses-directory = "/data/prover/v3/execution/responses"
+#[prover.new.blob-compression]
+#fs-requests-directory = "/data/prover/v3/compression/requests"
+#fs-responses-directory = "/data/prover/v3/compression/responses"
+#[prover.new.proof-aggregation]
+#fs-requests-directory = "/data/prover/v3/aggregation/requests"
+#fs-responses-directory = "/data/prover/v3/aggregation/responses"
+
+[traces]
+expected-traces-api-version = "beta-v2.1-rc16.2"
+[traces.counters]
+endpoints = ["http://traces-node:8545/"]
+request-limit-per-endpoint = 1
+[traces.counters.request-retries]
+backoff-delay = "PT1S"
+failures-warning-threshold = 10
+
+[traces.conflation]
+endpoints = ["http://traces-node:8545/"]
+request-limit-per-endpoint = 1
+[traces.conflation.request-retries]
+backoff-delay = "PT1S"
+failures-warning-threshold = 10
+
+[state-manager]
+version = "2.3.0"
+endpoints = ["http://shomei:8888/"]
+request-limit-per-endpoint = 3
+[state-manager.request-retries]
+max-retries = 5
+backoff-delay = "PT2S"
+failures-warning-threshold = 2
+
+[type2-state-proof-provider]
+disabled = false
+endpoints = ["http://shomei-frontend:8888/"]
+l1-query-block-tag="LATEST"
+l1-polling-interval="PT1S"
+
+[type2-state-proof-provider.request-retries]
+backoff-delay = "PT1S"
+failures-warning-threshold = 2
+
+[l1-finalization-monitor]
+l1-polling-interval = "PT1S"
+l1-query-block-tag="LATEST"
+
+[l1-submission]
+disabled = true
+[l1-submission.dynamic-gas-price-cap]
+disabled = false
+[l1-submission.dynamic-gas-price-cap.gas-price-cap-calculation]
+adjustment-constant = 25
+blob-adjustment-constant = 25
+finalization-target-max-delay = "PT32H"
+base-fee-per-gas-percentile-window = "P7D"
+base-fee-per-gas-percentile-window-leeway = "PT10M"
+base-fee-per-gas-percentile = 10
+gas-price-caps-check-coefficient = 0.9
+# The lower bound of the "historic base fee per blob gas" used in
+# the L1 dynamic gas price cap equation
+historic-base-fee-per-blob-gas-lower-bound=100000000 # 0.1 GWEI
+# An optional config to replace the "historic average reward" used in
+# the L1 dynamic gas price cap equation
+historic-avg-reward-constant=100000000 # 0.1 GWEI
+[l1-submission.dynamic-gas-price-cap.fee-history-fetcher]
+fetch-interval = "PT1S"
+max-block-count = 1000
+reward-percentiles = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
+num-of-blocks-before-latest = 2
+storage-period = "P10D"
+
+[l1-submission.fallback-gas-price]
+fee-history-block-count = 10
+fee-history-reward-percentile = 15
+
+[l1-submission.blob]
+disabled = false
+submission-delay = "PT1S"
+submission-tick-interval = "PT1S"
+max-submission-transactions-per-tick = 10
+target-blobs-per-transaction=9
+db-max-blobs-to-return = 100
+[l1-submission.blob.gas]
+gas-limit = 10000000
+max-fee-per-gas-cap = 100000000000
+max-fee-per-blob-gas-cap = 100000000000
+max-priority-fee-per-gas-cap=20000000000
+# Note: prefixed with "fallback-", used when dynamic gas price is disabled or DB is not populated yet
+[l1-submission.blob.gas.fallback]
+priority-fee-per-gas-upper-bound = 20000000000 # 20 GWEI
+priority-fee-per-gas-lower-bound = 2000000000 # 2 GWEI
+
+
+[l1-submission.blob.signer]
+# Web3j/Web3signer
+type = "Web3signer"
+
+# The account with this private key is in genesis file
+[l1-submission.blob.signer.web3j]
+private-key = "0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a"
+
+[l1-submission.blob.signer.web3signer]
+endpoint = "http://web3signer:9000"
+max-pool-size = 10
+keep-alive = true
+public-key = "9d9031e97dd78ff8c15aa86939de9b1e791066a0224e331bc962a2099a7b1f0464b8bbafe1535f2301c72c2cb3535b172da30b02686ab0393d348614f157fbdb"
+
+[l1-submission.aggregation]
+disabled = false
+l1-endpoint = "http://l1-el-node:8545"
+submission-delay = "PT1S"
+submission-tick-interval = "PT1S"
+max-submissions-per-tick = 10
+[l1-submission.aggregation.gas]
+gas-limit = 10_000_000
+max-fee-per-gas-cap = 200_000_000_000
+max-priority-fee-per-gas-cap = 40_000_000_000
+
+[l1-submission.aggregation.gas.fallback]
+# Note: prefixed with "fallback-", used when dynamic gas price is disabled or DB is not populated yet
+priority-fee-per-gas-upper-bound = 20000000000 # 20 GWEI
+priority-fee-per-gas-lower-bound = 2000000000 # 2 GWEI
+
+[l1-submission.aggregation.signer]
+# Web3j/Web3signer
+type = "Web3signer"
+
+[l1-submission.aggregation.signer.web3j]
+private-key = "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"
+
+[l1-submission.aggregation.signer.web3signer]
+endpoint = "http://web3signer:9000"
+max-pool-size = 10
+keep-alive = true
+public-key = "ba5734d8f7091719471e7f7ed6b9df170dc70cc661ca05e688601ad984f068b0d67351e5f06073092499336ab0839ef8a521afd334e53807205fa2f08eec74f4"
+
+[message-anchoring]
+disabled = false
+l1-highest-block-tag="LATEST"
+l2-highest-block-tag="LATEST" # optional, default to LATEST it shall not be necessary as Linea has instant finality
+anchoring-tick-interval = "PT2S"
+
+[message-anchoring.l1-event-scraping]
+polling-interval = "PT1S"
+polling-timeout = "PT5S"
+
+[message-anchoring.gas]
+max-fee-per-gas-cap = 100000000000
+gas-limit = 10000000
+fee-history-block-count = 4
+fee-history-reward-percentile = 15
+
+[message-anchoring.signer]
+# Web3j/Web3signer
+type = "Web3signer"
+
+[message-anchoring.signer.web3j]
+private-key = "0x4d01ae6487860981699236a58b68f807ee5f17b12df5740b85cf4c4653be0f55"
+
+[message-anchoring.signer.web3signer]
+endpoint = "http://web3signer:9000"
+max-pool-size = 10
+keep-alive = true
+public-key = "4a788ad6fa008beed58de6418369717d7492f37d173d70e2c26d9737e2c6eeae929452ef8602a19410844db3e200a0e73f5208fd76259a8766b73953fc3e7023"
+
+
+[l2-network-gas-pricing] # old [dynamic-gas-price-service]
+disabled = false
+price-update-interval = "PT12S"
+fee-history-block-count = 50
+fee-history-reward-percentile = 15
+gas-price-fixed-cost = 3000000
+extra-data-update-endpoint = "http://sequencer:8545/"
+[l2-network-gas-pricing.extra-data-update-request-retries]
+max-retries = 4
+timeout = "PT7S"
+backoff-delay = "PT2S"
+failures-warning-threshold = 3
+
+[l2-network-gas-pricing.flat-rate-gas-pricing]
+# Relate to legacy gas pricing, goes into extradata
+# and is exposed on Bessu eth_gasPrice
+gas-price-upper-bound = 10000000000 # 10 GWEI
+gas-price-lower-bound = 90000000 # 0.09 GWEI
+compressed-tx-size = 125
+expected-gas = 21000
+
+[l2-network-gas-pricing.dynamic-gas-pricing]
+# Propagated to Sequencer and Besude through extraDataPricerService and besu
+# uses it dynaically culcuale the profitability of each transaction on:
+# eth_sendRawTransaction, linea_estimateGas, and block building
+l1-blob-gas = 131072 # 2^17 # expected-l1-blob-gas previous name: expected-blob-gas
+blob-submission-expected-execution-gas = 213000
+variable-cost-upper-bound = 10000000001 # ~10 GWEI
+variable-cost-lower-bound = 90000001 # ~0.09 GWEI
+margin = 4.0
+
+[database]
+hostname = "postgres"
+port=5432
+username = "postgres"
+password = "postgres"
+schema = "linea_coordinator"
+read-pool-size = 10
+read-pipelining-limit = 10
+transactional-pool-size = 10
+[database.persistence-retries]
+max-retries = 3
+backoff-delay = "PT1S"
+timeout = "PT10S"
+failures-warning-threshold = 2
+
+[api]
+observability-port = 9545
diff --git a/config/coordinator/coordinator-docker-traces-v2-override.config.toml b/config/coordinator/coordinator-docker-traces-v2-override.config.toml
deleted file mode 100644
index 08bf23fe6b..0000000000
--- a/config/coordinator/coordinator-docker-traces-v2-override.config.toml
+++ /dev/null
@@ -1,35 +0,0 @@
-[prover]
-[prover.execution]
-fs-requests-directory = "/data/prover/v3/execution/requests"
-fs-responses-directory = "/data/prover/v3/execution/responses"
-[prover.blob-compression]
-fs-requests-directory = "/data/prover/v3/compression/requests"
-fs-responses-directory = "/data/prover/v3/compression/responses"
-[prover.proof-aggregation]
-fs-requests-directory = "/data/prover/v3/aggregation/requests"
-fs-responses-directory = "/data/prover/v3/aggregation/responses"
-
-[traces]
-blob-compressor-version="V1_2"
-[traces.counters-v2]
-endpoints=["http://traces-node:8545/"]
-request-limit-per-endpoint=1
-request-retry.backoff-delay="PT1S"
-request-retry.failures-warning-threshold=2
-[traces.conflation-v2]
-endpoints=["http://traces-node:8545/"]
-request-limit-per-endpoint=1
-request-retry.backoff-delay="PT1S"
-request-retry.failures-warning-threshold=2
-
-[l2-network-gas-pricing.json-rpc-pricing-propagation]
-geth-gas-price-update-recipients=[
- "http://l2-node:8545/"
-]
-
-[l2-network-gas-pricing.legacy.sample-transaction-gas-pricing]
-plain-transfer-cost-multiplier=1.0
-# Ratio of 350 / 29400 is based on data from Mainnet. Only 0.3% of transactions are less profitable than this
-# Meaning 99.7% of transactions will be includable if priced using eth_gasPrice
-compressed-tx-size=350
-expected-gas=29400
diff --git a/config/coordinator/coordinator-docker-web3signer-override.config.toml b/config/coordinator/coordinator-docker-web3signer-override.config.toml
deleted file mode 100644
index b5fd4ef356..0000000000
--- a/config/coordinator/coordinator-docker-web3signer-override.config.toml
+++ /dev/null
@@ -1,13 +0,0 @@
-[finalization-signer]
-# Web3j/Web3signer
-type="Web3Signer"
-
-[data-submission-signer]
-# Web3j/Web3signer
-type="Web3Signer"
-
-[l2-signer]
-# Web3j/Web3signer
-type="Web3Signer"
-
-
diff --git a/config/coordinator/coordinator-docker.config.toml b/config/coordinator/coordinator-docker.config.toml
deleted file mode 100644
index 3a2397f54d..0000000000
--- a/config/coordinator/coordinator-docker.config.toml
+++ /dev/null
@@ -1,288 +0,0 @@
-testL1Disabled=false
-
-duplicated-logs-debounce-time="PT15S"
-
-eip4844-switch-l2-block-number=0
-
-[conflation]
-blocks-limit=3
-conflation-deadline="PT6S"
-conflation-deadline-check-interval="PT3S"
-conflation-deadline-last-block-confirmation-delay="PT2S" # recommended: at least 2 * blockInterval
-# This is to prevent inflight trasactions that may change Smart contract state while coordinator is restarted.
-# Queries SMC for last finalised block, and keeps polling until this number of blocks observe the same state.
-# If state is updated meanwhile, it resets counter and restarts the polling.
-consistent-number-of-blocks-on-l1-to-wait=1
-fetch-blocks-limit=4000
-
-[blob-compression]
-blob-size-limit=102400 # 100KB
-handler-polling-interval="PT1S"
-# default batches limit is aggregation-proofs-limit -1
-# batches-limit must be less than or equal to aggregation-proofs-limit-1
-batches-limit=1
-
-[proof-aggregation]
-aggregation-proofs-limit=3
-aggregation-deadline="PT10S"
-aggregation-coordinator-polling-interval="PT2S"
-deadline-check-interval="PT8S"
-#target-end-blocks=[33, 90, 93]
-
-[prover]
-fs-inprogress-request-writing-suffix = ".inprogress_coordinator_writing"
-fs-inprogress-proving-suffix-pattern = ".*\\.inprogress\\.prover.*"
-fs-polling-interval = "PT1S"
-fs-polling-timeout = "PT10M"
-[prover.execution]
-fs-requests-directory = "/data/prover/v2/execution/requests"
-fs-responses-directory = "/data/prover/v2/execution/responses"
-[prover.blob-compression]
-fs-requests-directory = "/data/prover/v2/compression/requests"
-fs-responses-directory = "/data/prover/v2/compression/responses"
-[prover.proof-aggregation]
-fs-requests-directory = "/data/prover/v2/aggregation/requests"
-fs-responses-directory = "/data/prover/v2/aggregation/responses"
-#[prover.new]
-#switch-block-number-inclusive=1000
-#[prover.new.execution]
-#fs-requests-directory = "/data/prover/v3/execution/requests"
-#fs-responses-directory = "/data/prover/v3/execution/responses"
-#[prover.new.blob-compression]
-#fs-requests-directory = "/data/prover/v3/compression/requests"
-#fs-responses-directory = "/data/prover/v3/compression/responses"
-#[prover.new.proof-aggregation]
-#fs-requests-directory = "/data/prover/v3/aggregation/requests"
-#fs-responses-directory = "/data/prover/v3/aggregation/responses"
-
-[traces]
-blob-compressor-version="V1_2"
-raw-execution-traces-version="0.2.0"
-expected-traces-api-version-v2="beta-v2.1-rc16.2"
-[traces.counters-v2]
-endpoints=["http://traces-node:8545/"]
-request-limit-per-endpoint=1
-request-retry.backoff-delay="PT1S"
-request-retry.failures-warning-threshold=2
-[traces.conflation-v2]
-endpoints=["http://traces-node:8545/"]
-request-limit-per-endpoint=1
-request-retry.backoff-delay="PT1S"
-request-retry.failures-warning-threshold=2
-
-[state-manager]
-version="2.3.0"
-endpoints=["http://shomei:8888/"]
-request-limit-per-endpoint=2
-request-retry.backoff-delay="PT2S"
-request-retry.failures-warning-threshold=2
-
-[type2-state-proof-provider]
-endpoints=["http://shomei-frontend:8888/"]
-request-retry.backoff-delay="PT1S"
-request-retry.failures-warning-threshold=2
-l1-query-block-tag="LATEST"
-l1-polling-interval="PT12S"
-
-[api]
-observability_port=9545
-
-[l1]
-rpc-endpoint="http://l1-el-node:8545"
-zk-evm-contract-address="0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9"
-finalization-polling-interval="PT6S"
-l1-query-block-tag="latest"
-gas-limit=10000000
-fee-history-block-count=10
-fee-history-reward-percentile=15
-# Global caps of maxFeePerGas, maxFeePerBlobGas, and maxPriorityFeePerGas
-# for L1 transactions regardless of L1 dynamic gas price cap is enabled or not
-max-fee-per-gas-cap=100000000000
-max-fee-per-blob-gas-cap=100000000000
-max-priority-fee-per-gas-cap=20000000000
-# The multiplier of global caps for L1 finalization transaction
-# E.g. if set as 2.0, it means the global caps of finalization txn
-# will always be 2 times higher than that of blob submission txn
-gas-price-cap-multiplier-for-finalization=2.0
-# blocks are 2s, this may catch in between blocks
-send-message-event-polling-interval="PT1S"
-# 10 blocks worth at 2s per block
-max-event-scraping-time="PT5S" # use by message anchoring service
-# An optional config to define the L1 block time with default as PT12S
-block-time="PT1S" # set the same as local L1 block time
-block-range-loop-limit=500
-max-messages-to-collect=1000
-finalized-block-tag="latest"
-# reset this once we know what to do on dev/UAT
-earliest-block=0
-genesis-state-root-hash="0x072ead6777750dc20232d1cee8dc9a395c2d350df4bbaa5096c6f59b214dcecd"
-# shnarf for contract V6
-# Keccak256(parentShnarf="0x00...00", snarkHash="0x00...00",
-# parentStateRootHash="0x072ead6777750dc20232d1cee8dc9a395c2d350df4bbaa5096c6f59b214dcecd",
-# evaludationClaim="0x00...00", evaludationPoint="0x00...00")
-genesis-shnarf-v6="0x47452a1b9ebadfe02bdd02f580fa1eba17680d57eec968a591644d05d78ee84f"
-
-[l2]
-rpc-endpoint="http://sequencer:8545"
-message-service-address="0xe537D669CA013d86EBeF1D64e40fC74CADC91987"
-gas-limit=10000000
-max-fee-per-gas-cap=100000000000
-fee-history-block-count=4
-fee-history-reward-percentile=15
-last-hash-search-window=25
-anchoring-receipt-polling-interval="PT01S"
-max-receipt-retries=120
-# Number of children blocks to wait before considering they won't be reverted and elegible for conflation.
-# this is a workaround to mitigate Geth fork issues with Clique PoA
-# Coordinator will consider block as finalized after being included in the chain wtih children blocks-to-finalization
-# Recommended: Geth sequencer minimum of 2, Besu sequencer minimum of 1, 0 is safe localy
-blocks-to-finalization=0
-new-block-polling-interval="PT1S"
-
-[blob-submission]
-disabled=false
-use-eth-estimate-gas=false
-db-polling-interval="PT1S"
-max-blobs-to-return=100
-proof-submission-delay="PT1S"
-max-blobs-to-submit-per-tick=10
-# These lower and upper bounds will be effective only if L1 dynamic
-# gas price cap is disabled or during fallback when there's insufficient
-# cached fee history data to compute dynamic gas price caps
-priority-fee-per-gas-upper-bound=2000000000 # 2 GWEI
-priority-fee-per-gas-lower-bound=200000000 # 0.2 GWEI
-
-[aggregation-finalization]
-disabled=false
-use-eth-estimate-gas=true
-db-polling-interval="PT1S"
-max-aggregations-to-finalize-per-tick=1
-proof-submission-delay="PT1S"
-
-[finalization-signer]
-# Web3j/Web3signer
-type="Web3j"
-
-[finalization-signer.web3j]
-private-key="0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"
-
-[finalization-signer.web3signer]
-endpoint="http://web3signer:9000"
-max-pool-size=10
-keep-alive=true
-public-key="ba5734d8f7091719471e7f7ed6b9df170dc70cc661ca05e688601ad984f068b0d67351e5f06073092499336ab0839ef8a521afd334e53807205fa2f08eec74f4"
-
-[data-submission-signer]
-# Web3j/Web3signer
-type="Web3j"
-
-# The account with this private key is in genesis file
-[data-submission-signer.web3j]
-private-key="0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a"
-
-[data-submission-signer.web3signer]
-endpoint="http://web3signer:9000"
-max-pool-size=10
-keep-alive=true
-public-key="9d9031e97dd78ff8c15aa86939de9b1e791066a0224e331bc962a2099a7b1f0464b8bbafe1535f2301c72c2cb3535b172da30b02686ab0393d348614f157fbdb"
-
-[l2-signer]
-# Web3j/Web3signer
-type="Web3j"
-
-[l2-signer.web3j]
-private-key="0x4d01ae6487860981699236a58b68f807ee5f17b12df5740b85cf4c4653be0f55"
-
-[l2-signer.web3signer]
-endpoint="http://web3signer:9000"
-max-pool-size=10
-keep-alive=true
-public-key="4a788ad6fa008beed58de6418369717d7492f37d173d70e2c26d9737e2c6eeae929452ef8602a19410844db3e200a0e73f5208fd76259a8766b73953fc3e7023"
-
-[message-anchoring]
-disabled = false
-l1-highest-block-tag="LATEST"
-l2-highest-block-tag="LATEST"
-l1-event-polling-interval="PT1S"
-anchoring-tick-interval = "PT1S"
-[message-anchoring.l1-request-retries]
-failures-warning-threshold = 1
-[message-anchoring.l2-request-retries]
-failures-warning-threshold = 1
-
-[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://l2-node:8545/"
-]
-besu-gas-price-update-recipients = []
-
-[l1-dynamic-gas-price-cap-service]
-disabled=false
-[l1-dynamic-gas-price-cap-service.gas-price-cap-calculation]
-adjustment-constant=25
-blob-adjustment-constant=25
-finalization-target-max-delay="PT30S"
-gas-fee-percentile-window="PT1M"
-gas-fee-percentile-window-leeway="PT10S"
-gas-fee-percentile=10
-gas-price-caps-check-coefficient=0.9
-# The lower bound of the "historic base fee per blob gas" used in
-# the L1 dynamic gas price cap equation
-historic-base-fee-per-blob-gas-lower-bound=100000000 # 0.1 GWEI
-# An optional config to replace the "historic average reward" used in
-# the L1 dynamic gas price cap equation
-historic-avg-reward-constant=100000000 # 0.1 GWEI
-[l1-dynamic-gas-price-cap-service.fee-history-fetcher]
-fetch-interval="PT1S"
-max-block-count=1000
-reward-percentiles=[10,20,30,40,50,60,70,80,90,100]
-num-of-blocks-before-latest=4
-[l1-dynamic-gas-price-cap-service.fee-history-storage]
-storage-period="PT2M"
-
-[database]
-host="postgres"
-port="5432"
-username="postgres"
-password="postgres"
-schema="linea_coordinator"
-read_pool_size=10
-read_pipelining_limit=10
-transactional_pool_size=10
-
-[persistence-retry]
-#max-retries = 10 commented as can be null
-backoff-delay = "PT1S"
diff --git a/config/coordinator/coordinator-local-dev.config-traces-v2.overrides.toml b/config/coordinator/coordinator-local-dev.config-traces-v2.overrides.toml
deleted file mode 100644
index a200143eaa..0000000000
--- a/config/coordinator/coordinator-local-dev.config-traces-v2.overrides.toml
+++ /dev/null
@@ -1,28 +0,0 @@
-[prover]
-[prover.execution]
-fs-requests-directory = "tmp/local/prover/v3/execution/requests"
-fs-responses-directory = "tmp/local/prover/v3/execution/responses"
-[prover.blob-compression]
-fs-requests-directory = "tmp/local/prover/v3/compression/requests"
-fs-responses-directory = "tmp/local/prover/v3/compression/responses"
-[prover.proof-aggregation]
-fs-requests-directory = "tmp/local/prover/v3/aggregation/requests"
-fs-responses-directory = "tmp/local/prover/v3/aggregation/responses"
-
-[l2]
-rpc-endpoint="http://127.0.0.1:8745"
-blocks-to-finalization=0
-
-[traces.counters-v2]
-endpoints=["http://127.0.0.1:8745"]
-
-[traces.conflation-v2]
-endpoints=["http://127.0.0.1:8745"]
-
-[type2-state-proof-provider]
-endpoints=[]
-
-[l2-network-gas-pricing.json-rpc-pricing-propagation]
-disabled=true
-geth-gas-price-update-recipients=[]
-besu-gas-price-update-recipients=[]
diff --git a/config/coordinator/coordinator-local-dev.config.overrides.toml b/config/coordinator/coordinator-local-dev.config.overrides.toml
deleted file mode 100644
index bab6c333f7..0000000000
--- a/config/coordinator/coordinator-local-dev.config.overrides.toml
+++ /dev/null
@@ -1,66 +0,0 @@
-# Can override any of this propeties in CLI as follows:
-# -Dconfig.override.sequencer.engine-api=http://127.0.0.1:8650
-
-[finalization-signer.web3signer]
-endpoint="http://127.0.0.1:9000"
-
-[data-submission-signer.web3signer]
-endpoint="http://127.0.0.1:9000"
-
-[l2-signer.web3signer]
-endpoint="http://127.0.0.1:9000"
-
-[prover]
-[prover.execution]
-fs-requests-directory="tmp/local/prover/v3/execution/requests"
-fs-responses-directory="tmp/local/prover/v3/execution/responses"
-[prover.blob-compression]
-fs-requests-directory="tmp/local/prover/v3/compression/requests"
-fs-responses-directory="tmp/local/prover/v3/compression/responses"
-[prover.proof-aggregation]
-fs-requests-directory="tmp/local/prover/v3/aggregation/requests"
-fs-responses-directory="tmp/local/prover/v3/aggregation/responses"
-
-# Config of Traces API Facade endpoint
-[traces]
-blob-compressor-version="V1_2"
-[traces.counters-v2]
-endpoints=["http://127.0.0.1:8745/"]
-[traces.conflation-v2]
-endpoints=["http://127.0.0.1:8745/"]
-
-[state-manager]
-endpoints=["http://127.0.0.1:8998/"]
-
-[type2-state-proof-provider]
-disabled=true
-endpoints=["http://127.0.0.1:8889/"]
-
-[l2-network-gas-pricing.extra-data-pricing-propagation]
-extra-data-update-recipient="http://127.0.0.1:8545/"
-
-[l2-network-gas-pricing.json-rpc-pricing-propagation]
-disabled=true
-geth-gas-price-update-recipients=["http://127.0.0.1:8845/"]
-besu-gas-price-update-recipients=[]
-
-[l1]
-rpc-endpoint="http://127.0.0.1:8445"
-blocks-to-finalization=2
-# blocks are 2s, this may catch in between blocks
-send-message-event-polling-interval="PT1S"
-# 10 blocks worth at 2s per block
-max-event-scraping-time="PT20S"
-block-range-loop-limit=10000
-finalized-block-tag="finalized"
-earliestBlock=0
-
-[l2]
-rpc-endpoint="http://127.0.0.1:9045"
-blocks-to-finalization=0
-
-[database]
-host="localhost"
-
-[api]
-observability_port=9546
diff --git a/config/coordinator/log4j2-dev.xml b/config/coordinator/log4j2-dev.xml
index 5e2538f940..f5a86e9080 100644
--- a/config/coordinator/log4j2-dev.xml
+++ b/config/coordinator/log4j2-dev.xml
@@ -116,6 +116,10 @@
+
+
+
+
diff --git a/coordinator/app/build.gradle b/coordinator/app/build.gradle
index 241e6e8e79..7072a74b33 100644
--- a/coordinator/app/build.gradle
+++ b/coordinator/app/build.gradle
@@ -107,8 +107,8 @@ run {
"config/common/smart-contract-errors.toml",
"--gas-price-cap-time-of-day-multipliers",
"config/common/gas-price-cap-time-of-day-multipliers.toml",
- "config/coordinator/coordinator-docker.config.toml",
- "config/coordinator/coordinator-local-dev.config.overrides.toml"
+ "config/coordinator/coordinator-config-v2.toml",
+ "config/coordinator/coordinator-config-v2-override-local-dev.toml"
]
}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/ConfigLoader.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/ConfigLoader.kt
deleted file mode 100644
index ef327cef13..0000000000
--- a/coordinator/app/src/main/kotlin/linea/coordinator/config/ConfigLoader.kt
+++ /dev/null
@@ -1,131 +0,0 @@
-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.TracesCountersV2
-import net.consensys.zkevm.coordinator.app.config.BlockParameterDecoder
-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.TracesLimitsV2ConfigFile
-import org.apache.logging.log4j.LogManager
-import org.apache.logging.log4j.Logger
-import java.nio.file.Path
-
-inline fun loadConfigsOrError(
- configFiles: List,
-): Result {
- val confBuilder: ConfigLoaderBuilder = ConfigLoaderBuilder.Companion
- .empty()
- .addDefaults()
- .addDecoder(BlockParameterDecoder())
- for (configFile in configFiles.reversed()) {
- // files must be added in reverse order for overriding
- confBuilder.addPathSource(configFile, false)
- }
-
- return confBuilder.build().loadConfig(emptyList()).let { config ->
- if (config.isInvalid()) {
- Err(config.getInvalidUnsafe().description())
- } else {
- Ok(config.getUnsafe())
- }
- }
-}
-
-fun logErrorIfPresent(
- configName: String,
- configFiles: List,
- configLoadingResult: Result,
- logger: Logger,
-) {
- if (configLoadingResult is Err) {
- logger.error("Failed to load $configName from files=$configFiles with error=${configLoadingResult.error}")
- }
-}
-
-inline fun loadConfigsAndLogErrors(
- configFiles: List,
- configName: String,
- logger: Logger = LogManager.getLogger("linea.coordinator.config"),
-): Result {
- return loadConfigsOrError(configFiles)
- .also { logErrorIfPresent(configName, configFiles, it, logger) }
-}
-
-fun loadConfigsOrError(
- coordinatorConfigFiles: List,
- tracesLimitsFileV2: Path,
- gasPriceCapTimeOfDayMultipliersFile: Path,
- smartContractErrorsFile: Path,
- logger: Logger = LogManager.getLogger("linea.coordinator.config"),
-): Result {
- val coordinatorBaseConfigs =
- loadConfigsAndLogErrors(coordinatorConfigFiles, "coordinator", logger)
- val tracesLimitsV2Configs =
- loadConfigsAndLogErrors(listOf(tracesLimitsFileV2), "traces limits v2", logger)
- val gasPriceCapTimeOfDayMultipliersConfig =
- loadConfigsAndLogErrors(
- listOf(gasPriceCapTimeOfDayMultipliersFile),
- "l1 submission gas prices caps",
- logger,
- )
- val smartContractErrorsConfig = loadConfigsAndLogErrors(
- listOf(smartContractErrorsFile),
- "smart contract errors",
- logger,
- )
- val configError = listOf(
- coordinatorBaseConfigs,
- tracesLimitsV2Configs,
- gasPriceCapTimeOfDayMultipliersConfig,
- smartContractErrorsConfig,
- )
- .find { it is Err }
-
- if (configError != null) {
- @Suppress("UNCHECKED_CAST")
- return configError as Result
- }
-
- val baseConfig = coordinatorBaseConfigs.get()!!
- val finalConfig = baseConfig.copy(
- conflation = baseConfig.conflation.copy(
- _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,
- tracesLimitsFileV2: Path,
- gasPriceCapTimeOfDayMultipliersFile: Path,
- smartContractErrorsFile: Path,
- logger: Logger = LogManager.getLogger("linea.coordinator.config"),
-): CoordinatorConfig {
- loadConfigsOrError(
- coordinatorConfigFiles,
- tracesLimitsFileV2,
- gasPriceCapTimeOfDayMultipliersFile,
- smartContractErrorsFile,
- logger,
- ).let {
- return it
- .getOrElse {
- throw RuntimeException("Invalid configurations: $it")
- }.reified()
- }
-}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/Mappers.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/Mappers.kt
new file mode 100644
index 0000000000..3fb0a70a5a
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/Mappers.kt
@@ -0,0 +1,13 @@
+package linea.coordinator.config
+
+import linea.domain.RetryConfig
+import net.consensys.linea.jsonrpc.client.RequestRetryConfig
+
+fun RetryConfig.toJsonRpcRetry(): RequestRetryConfig {
+ return RequestRetryConfig(
+ maxRetries = maxRetries,
+ timeout = timeout,
+ backoffDelay = backoffDelay,
+ failuresWarningThreshold = failuresWarningThreshold,
+ )
+}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/ApiConfig.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/ApiConfig.kt
new file mode 100644
index 0000000000..5010abecb8
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/ApiConfig.kt
@@ -0,0 +1,5 @@
+package linea.coordinator.config.v2
+
+data class ApiConfig(
+ val observabilityPort: UInt,
+)
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/ConflationConfig.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/ConflationConfig.kt
new file mode 100644
index 0000000000..4b8fb85c80
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/ConflationConfig.kt
@@ -0,0 +1,46 @@
+package linea.coordinator.config.v2
+
+import linea.blob.BlobCompressorVersion
+import linea.domain.RetryConfig
+import net.consensys.linea.traces.TracesCountersV2
+import java.net.URL
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
+
+data class ConflationConfig(
+ override val disabled: Boolean = false,
+ val blocksLimit: UInt? = null,
+ val forceStopConflationAtBlockInclusive: ULong? = null,
+ val blocksPollingInterval: Duration = 1.seconds,
+ val conflationDeadline: Duration? = null, // disabled by default
+ val conflationDeadlineCheckInterval: Duration = 10.seconds,
+ // 24 second without blocks must elapse before conflation deadline is considered expired
+ val conflationDeadlineLastBlockConfirmationDelay: Duration = 24.seconds,
+ val consistentNumberOfBlocksOnL1ToWait: UInt = 32u, // 1 epoch
+ val l2FetchBlocksLimit: UInt = UInt.MAX_VALUE,
+ val l2Endpoint: URL,
+ val l2RequestRetries: RetryConfig = RetryConfig.endlessRetry(
+ backoffDelay = 1.seconds,
+ failuresWarningThreshold = 3u,
+ ),
+ val l2GetLogsEndpoint: URL,
+ val blobCompression: BlobCompression = BlobCompression(),
+ val proofAggregation: ProofAggregation = ProofAggregation(),
+ val tracesLimitsV2: TracesCountersV2,
+) : FeatureToggle {
+ data class BlobCompression(
+ val blobSizeLimit: UInt = 102400u,
+ val handlerPollingInterval: Duration = 1.seconds,
+ val batchesLimit: UInt? = null,
+ val blobCompressorVersion: BlobCompressorVersion = BlobCompressorVersion.V1_2,
+ )
+
+ data class ProofAggregation(
+ val proofsLimit: UInt = 300u,
+ val deadline: Duration = Duration.INFINITE,
+ val deadlineCheckInterval: Duration = 30.seconds,
+ val coordinatorPollingInterval: Duration = 3.seconds,
+ val targetEndBlocks: List? = null,
+ val aggregationSizeMultipleOf: UInt = 1u,
+ )
+}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/CoordinatorConfig.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/CoordinatorConfig.kt
new file mode 100644
index 0000000000..e86e3ff667
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/CoordinatorConfig.kt
@@ -0,0 +1,20 @@
+package linea.coordinator.config.v2
+
+import linea.web3j.SmartContractErrors
+import net.consensys.zkevm.coordinator.clients.prover.ProversConfig
+
+data class CoordinatorConfig(
+ val protocol: ProtocolConfig,
+ val conflation: ConflationConfig,
+ val proversConfig: ProversConfig,
+ val traces: TracesConfig,
+ val stateManager: StateManagerConfig,
+ val type2StateProofProvider: Type2StateProofManagerConfig,
+ val l1FinalizationMonitor: L1FinalizationMonitorConfig,
+ val l1Submission: L1SubmissionConfig? = null,
+ val messageAnchoring: MessageAnchoringConfig? = null,
+ val l2NetworkGasPricing: L2NetworkGasPricingConfig? = null,
+ val database: DatabaseConfig,
+ val api: ApiConfig,
+ val smartContractErrors: SmartContractErrors,
+)
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/DatabaseConfig.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/DatabaseConfig.kt
new file mode 100644
index 0000000000..247aa65033
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/DatabaseConfig.kt
@@ -0,0 +1,22 @@
+package linea.coordinator.config.v2
+
+import com.sksamuel.hoplite.Masked
+import linea.domain.RetryConfig
+import kotlin.time.Duration.Companion.minutes
+import kotlin.time.Duration.Companion.seconds
+
+data class DatabaseConfig(
+ val host: String,
+ val port: Int,
+ val username: String,
+ val password: Masked,
+ val schema: String,
+ val readPoolSize: Int = 10,
+ val readPipeliningLimit: Int = 10,
+ val transactionalPoolSize: Int = 10,
+ val persistenceRetries: RetryConfig = RetryConfig(
+ backoffDelay = 1.seconds,
+ timeout = 10.minutes,
+ failuresWarningThreshold = 3u,
+ ),
+)
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/FeatureToggle.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/FeatureToggle.kt
new file mode 100644
index 0000000000..f7640790e1
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/FeatureToggle.kt
@@ -0,0 +1,8 @@
+package linea.coordinator.config.v2
+
+interface FeatureToggle {
+ val disabled: Boolean
+}
+
+fun FeatureToggle?.isDisabled(): Boolean = this?.disabled ?: true
+fun FeatureToggle?.isEnabled(): Boolean = !isDisabled()
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/L1FinalizationMonitorConfig.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/L1FinalizationMonitorConfig.kt
new file mode 100644
index 0000000000..01b25f5a0e
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/L1FinalizationMonitorConfig.kt
@@ -0,0 +1,13 @@
+package linea.coordinator.config.v2
+
+import linea.domain.BlockParameter
+import java.net.URL
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
+
+data class L1FinalizationMonitorConfig(
+ val l1Endpoint: URL,
+ val l2Endpoint: URL,
+ val l1PollingInterval: Duration = 6.seconds,
+ val l1QueryBlockTag: BlockParameter.Tag = BlockParameter.Tag.FINALIZED,
+)
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/L1SubmissionConfig.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/L1SubmissionConfig.kt
new file mode 100644
index 0000000000..0c443064a4
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/L1SubmissionConfig.kt
@@ -0,0 +1,84 @@
+package linea.coordinator.config.v2
+
+import net.consensys.linea.ethereum.gaspricing.dynamiccap.TimeOfDayMultipliers
+import java.net.URL
+import kotlin.time.Duration
+
+data class L1SubmissionConfig(
+ val dynamicGasPriceCap: DynamicGasPriceCapConfig,
+ val fallbackGasPrice: FallbackGasPriceConfig,
+ val blob: BlobSubmissionConfig,
+ val aggregation: AggregationSubmissionConfig,
+) : FeatureToggle {
+ override val disabled: Boolean
+ get() = blob.disabled && aggregation.disabled
+
+ data class DynamicGasPriceCapConfig(
+ override val disabled: Boolean,
+ val gasPriceCapCalculation: GasPriceCapCalculationConfig,
+ val feeHistoryFetcher: FeeHistoryFetcherConfig,
+ val timeOfDayMultipliers: TimeOfDayMultipliers,
+ ) : FeatureToggle {
+ data class GasPriceCapCalculationConfig(
+ val adjustmentConstant: UInt,
+ val blobAdjustmentConstant: UInt,
+ val finalizationTargetMaxDelay: Duration,
+ val baseFeePerGasPercentileWindow: Duration,
+ val baseFeePerGasPercentileWindowLeeway: Duration,
+ val baseFeePerGasPercentile: UInt,
+ val gasPriceCapsCheckCoefficient: Double,
+ val historicBaseFeePerBlobGasLowerBound: ULong,
+ val historicAvgRewardConstant: ULong,
+ val timeOfTheDayMultipliers: Map,
+ )
+
+ data class FeeHistoryFetcherConfig(
+ val l1Endpoint: URL,
+ val fetchInterval: Duration,
+ val maxBlockCount: UInt,
+ val rewardPercentiles: List,
+ val numOfBlocksBeforeLatest: UInt,
+ val storagePeriod: Duration,
+ )
+ }
+
+ data class FallbackGasPriceConfig(
+ val feeHistoryBlockCount: UInt,
+ val feeHistoryRewardPercentile: UInt,
+ )
+
+ data class GasConfig(
+ val gasLimit: ULong,
+ val maxFeePerGasCap: ULong,
+ val maxPriorityFeePerGasCap: ULong,
+ val maxFeePerBlobGasCap: ULong? = null,
+ val fallback: FallbackGasConfig,
+ ) {
+ data class FallbackGasConfig(
+ val priorityFeePerGasUpperBound: ULong,
+ val priorityFeePerGasLowerBound: ULong,
+ )
+ }
+
+ data class BlobSubmissionConfig(
+ override val disabled: Boolean,
+ val l1Endpoint: URL,
+ val submissionDelay: Duration,
+ val submissionTickInterval: Duration,
+ val maxSubmissionTransactionsPerTick: UInt,
+ val targetBlobsPerTransaction: UInt,
+ val dbMaxBlobsToReturn: UInt,
+ val gas: GasConfig,
+ val signer: SignerConfig,
+ ) : FeatureToggle
+
+ data class AggregationSubmissionConfig(
+ override val disabled: Boolean,
+ val l1Endpoint: URL,
+ val submissionDelay: Duration,
+ val submissionTickInterval: Duration,
+ val maxSubmissionsPerTick: UInt,
+ val gas: GasConfig,
+ val signer: SignerConfig,
+ ) : FeatureToggle
+}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/L2NetworkGasPricingConfig.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/L2NetworkGasPricingConfig.kt
new file mode 100644
index 0000000000..225d8c5b48
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/L2NetworkGasPricingConfig.kt
@@ -0,0 +1,34 @@
+package linea.coordinator.config.v2
+
+import linea.domain.RetryConfig
+import java.net.URL
+import kotlin.time.Duration
+
+data class L2NetworkGasPricingConfig(
+ override val disabled: Boolean,
+ val priceUpdateInterval: Duration,
+ val feeHistoryBlockCount: UInt,
+ val feeHistoryRewardPercentile: UInt,
+ val gasPriceFixedCost: ULong,
+ val dynamicGasPricing: DynamicGasPricing,
+ val flatRateGasPricing: FlatRateGasPricing,
+ val extraDataUpdateEndpoint: URL,
+ val extraDataUpdateRequestRetries: RetryConfig,
+ val l1Endpoint: URL,
+) : FeatureToggle {
+ data class DynamicGasPricing(
+ val l1BlobGas: ULong,
+ val blobSubmissionExpectedExecutionGas: ULong,
+ val variableCostUpperBound: ULong,
+ val variableCostLowerBound: ULong,
+ val margin: Double,
+ )
+
+ data class FlatRateGasPricing(
+ val gasPriceLowerBound: ULong,
+ val gasPriceUpperBound: ULong,
+ val plainTransferCostMultiplier: Double = 1.0,
+ val compressedTxSize: UInt = 125u,
+ val expectedGas: UInt = 21000u,
+ )
+}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/MessageAnchoringConfig.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/MessageAnchoringConfig.kt
new file mode 100644
index 0000000000..4cb78d3d57
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/MessageAnchoringConfig.kt
@@ -0,0 +1,71 @@
+package linea.coordinator.config.v2
+
+import linea.domain.BlockParameter
+import linea.domain.RetryConfig
+import java.net.URL
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.Duration.Companion.seconds
+
+data class MessageAnchoringConfig(
+ override val disabled: Boolean = false,
+ val l1Endpoint: URL,
+ val l1HighestBlockTag: BlockParameter = BlockParameter.Tag.FINALIZED,
+ val l1RequestRetries: RetryConfig = RetryConfig.endlessRetry(
+ backoffDelay = 1.seconds,
+ failuresWarningThreshold = 3u,
+ ),
+ val l1EventScrapping: L1EventScrapping = L1EventScrapping(),
+ val l2Endpoint: URL,
+ val l2HighestBlockTag: BlockParameter = BlockParameter.Tag.LATEST,
+ val l2RequestRetries: RetryConfig = RetryConfig.endlessRetry(
+ backoffDelay = 1.seconds,
+ failuresWarningThreshold = 3u,
+ ),
+ val anchoringTickInterval: Duration = 2.seconds,
+ val messageQueueCapacity: UInt = 10_000u,
+ val maxMessagesToAnchorPerL2Transaction: UInt = 100u,
+ val signer: SignerConfig,
+ val gas: GasConfig = GasConfig(),
+) : FeatureToggle {
+ init {
+ require(messageQueueCapacity >= 1u) {
+ "messageQueueCapacity=$messageQueueCapacity must be equal or greater than 1"
+ }
+ require(maxMessagesToAnchorPerL2Transaction >= 1u) {
+ "maxMessagesToAnchorPerL2Transaction=$maxMessagesToAnchorPerL2Transaction be equal or greater than 1"
+ }
+ require(anchoringTickInterval >= 1.milliseconds) {
+ "anchoringTickInterval must be equal or greater than 1ms"
+ }
+ }
+
+ data class L1EventScrapping(
+ val pollingInterval: Duration = 2.seconds,
+ val pollingTimeout: Duration = 5.seconds,
+ val ethLogsSearchSuccessBackoffDelay: Duration = 1.milliseconds,
+ val ethLogsSearchBlockChunkSize: UInt = 1000u,
+ ) {
+ init {
+ require(pollingInterval >= 1.milliseconds) {
+ "pollingInterval=$pollingInterval must be equal or greater than 1ms"
+ }
+ require(pollingTimeout >= 1.milliseconds) {
+ "pollingTimeout=$pollingTimeout must be equal or greater than 1ms"
+ }
+ require(ethLogsSearchSuccessBackoffDelay >= 1.milliseconds) {
+ "ethLogsSearchSuccessBackoffDelay=$ethLogsSearchSuccessBackoffDelay must be equal or greater than 1ms"
+ }
+ require(ethLogsSearchBlockChunkSize >= 1u) {
+ "ethLogsSearchBlockChunkSize=$ethLogsSearchBlockChunkSize must be equal or greater than 1"
+ }
+ }
+ }
+
+ data class GasConfig(
+ val maxFeePerGasCap: ULong = 100_000_000_000uL, // 100 gwei
+ val gasLimit: ULong = 2_500_000uL,
+ val feeHistoryBlockCount: UInt = 4u,
+ val feeHistoryRewardPercentile: UInt = 15u,
+ )
+}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/ProtocolConfig.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/ProtocolConfig.kt
new file mode 100644
index 0000000000..01328360ae
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/ProtocolConfig.kt
@@ -0,0 +1,44 @@
+package linea.coordinator.config.v2
+
+import linea.domain.BlockParameter
+import kotlin.time.Duration
+
+data class ProtocolConfig(
+ val genesis: Genesis,
+ val l1: Layer1Config,
+ val l2: Layer2Config,
+) {
+ data class Genesis(
+ val genesisStateRootHash: ByteArray,
+ val genesisShnarf: ByteArray,
+ ) {
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as Genesis
+
+ if (!genesisStateRootHash.contentEquals(other.genesisStateRootHash)) return false
+ if (!genesisShnarf.contentEquals(other.genesisShnarf)) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = genesisStateRootHash.contentHashCode()
+ result = 31 * result + genesisShnarf.contentHashCode()
+ return result
+ }
+ }
+
+ data class Layer1Config(
+ val contractAddress: String,
+ val blockTime: Duration,
+ )
+
+ data class Layer2Config(
+ val contractAddress: String,
+ val contractDeploymentBlockNumber: BlockParameter.BlockNumber?,
+ )
+}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/ProverConfig.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/ProverConfig.kt
new file mode 100644
index 0000000000..b000de001f
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/ProverConfig.kt
@@ -0,0 +1,21 @@
+package linea.coordinator.config.v2
+
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
+
+data class ProverConfig(
+ val version: String,
+ val fsInprogressRequestWritingSuffix: String = ".inprogress_coordinator_writing",
+ val fsInprogressProvingSuffixPattern: String = "\\.inprogress\\.prover.*",
+ val fsPollingInterval: Duration = 15.seconds,
+ val fsPollingTimeout: Duration? = null,
+ val execution: ProverDirectoriesToml,
+ val blobCompression: ProverDirectoriesToml,
+ val proofAggregation: ProverDirectoriesToml,
+ val new: ProverConfig? = null,
+) {
+ data class ProverDirectoriesToml(
+ val fsRequestsDirectory: String,
+ val fsResponsesDirectory: String,
+ )
+}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/SignerConfig.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/SignerConfig.kt
new file mode 100644
index 0000000000..3f0d8d89d9
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/SignerConfig.kt
@@ -0,0 +1,76 @@
+package linea.coordinator.config.v2
+
+import java.net.URL
+
+data class SignerConfig(
+ val type: SignerType,
+ val web3j: Web3jConfig?,
+ val web3signer: Web3SignerConfig?,
+) {
+ init {
+ when {
+ type == SignerType.WEB3J && web3j == null -> {
+ throw IllegalArgumentException("signetType=$type requires web3j config")
+ }
+
+ type == SignerType.WEB3SIGNER && web3signer == null -> {
+ throw IllegalArgumentException("signetType=$type requires web3signer config")
+ }
+ }
+ }
+
+ enum class SignerType() {
+ WEB3J,
+ WEB3SIGNER,
+ }
+
+ data class Web3jConfig(
+ val privateKey: ByteArray,
+ ) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as Web3jConfig
+
+ return privateKey.contentEquals(other.privateKey)
+ }
+
+ override fun hashCode(): Int {
+ return privateKey.hashCode()
+ }
+
+ override fun toString(): String {
+ return "Web3jConfig(privateKey=***${privateKey.size}bytes***)"
+ }
+ }
+
+ data class Web3SignerConfig(
+ val endpoint: URL,
+ val publicKey: ByteArray,
+ val maxPoolSize: Int = 10,
+ val keepAlive: Boolean = true,
+ ) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as Web3SignerConfig
+
+ if (maxPoolSize != other.maxPoolSize) return false
+ if (keepAlive != other.keepAlive) return false
+ if (endpoint != other.endpoint) return false
+ if (!publicKey.contentEquals(other.publicKey)) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = maxPoolSize
+ result = 31 * result + keepAlive.hashCode()
+ result = 31 * result + endpoint.hashCode()
+ result = 31 * result + publicKey.contentHashCode()
+ return result
+ }
+ }
+}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/StateManagerConfig.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/StateManagerConfig.kt
new file mode 100644
index 0000000000..4df4456ca4
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/StateManagerConfig.kt
@@ -0,0 +1,15 @@
+package linea.coordinator.config.v2
+
+import linea.domain.RetryConfig
+import java.net.URL
+import kotlin.time.Duration.Companion.seconds
+
+data class StateManagerConfig(
+ val version: String,
+ val endpoints: List,
+ val requestLimitPerEndpoint: UInt = UInt.MAX_VALUE,
+ val requestRetries: RetryConfig = RetryConfig.endlessRetry(
+ backoffDelay = 1.seconds,
+ failuresWarningThreshold = 3u,
+ ),
+)
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/TracesConfig.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/TracesConfig.kt
new file mode 100644
index 0000000000..5ce8f6ac9e
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/TracesConfig.kt
@@ -0,0 +1,22 @@
+package linea.coordinator.config.v2
+
+import linea.domain.RetryConfig
+import java.net.URL
+import kotlin.time.Duration.Companion.seconds
+
+data class TracesConfig(
+ val expectedTracesApiVersion: String,
+ val counters: ClientApiConfig,
+ val conflation: ClientApiConfig,
+ // val switchBlockNumberInclusive: UInt? = null,
+ // val new: TracesConfig? = null
+) {
+ data class ClientApiConfig(
+ val endpoints: List,
+ val requestLimitPerEndpoint: UInt = 100u,
+ val requestRetries: RetryConfig = RetryConfig.endlessRetry(
+ backoffDelay = 1.seconds,
+ failuresWarningThreshold = 3u,
+ ),
+ )
+}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/Type2StateProofManagerConfig.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/Type2StateProofManagerConfig.kt
new file mode 100644
index 0000000000..69d3ea6a6b
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/Type2StateProofManagerConfig.kt
@@ -0,0 +1,18 @@
+package linea.coordinator.config.v2
+
+import linea.domain.BlockParameter
+import linea.domain.RetryConfig
+import java.net.URL
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
+
+data class Type2StateProofManagerConfig(
+ override val disabled: Boolean = false,
+ val endpoints: List,
+ val requestRetries: RetryConfig = RetryConfig.endlessRetry(
+ backoffDelay = 1.seconds,
+ failuresWarningThreshold = 3u,
+ ),
+ val l1QueryBlockTag: BlockParameter.Tag = BlockParameter.Tag.FINALIZED,
+ val l1PollingInterval: Duration = 6.seconds,
+) : FeatureToggle
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/ApiConfigToml.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/ApiConfigToml.kt
new file mode 100644
index 0000000000..09b0b325fc
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/ApiConfigToml.kt
@@ -0,0 +1,11 @@
+package linea.coordinator.config.v2.toml
+
+import linea.coordinator.config.v2.ApiConfig
+
+data class ApiConfigToml(
+ val observabilityPort: UInt = 9545u,
+) {
+ fun reified(): ApiConfig {
+ return ApiConfig(observabilityPort)
+ }
+}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/ConfigLoaderHelper.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/ConfigLoaderHelper.kt
new file mode 100644
index 0000000000..2ea03bc89e
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/ConfigLoaderHelper.kt
@@ -0,0 +1,169 @@
+package linea.coordinator.config.v2.toml
+
+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.github.michaelbull.result.recoverIf
+import com.sksamuel.hoplite.ConfigLoaderBuilder
+import com.sksamuel.hoplite.ConfigResult
+import com.sksamuel.hoplite.ExperimentalHoplite
+import com.sksamuel.hoplite.fp.Validated
+import com.sksamuel.hoplite.toml.TomlPropertySource
+import linea.coordinator.config.v2.CoordinatorConfig
+import linea.coordinator.config.v2.toml.decoders.BlockParameterDecoder
+import linea.coordinator.config.v2.toml.decoders.BlockParameterNumberDecoder
+import linea.coordinator.config.v2.toml.decoders.BlockParameterTagDecoder
+import linea.coordinator.config.v2.toml.decoders.TomlByteArrayHexDecoder
+import linea.coordinator.config.v2.toml.decoders.TomlKotlinDurationDecoder
+import linea.coordinator.config.v2.toml.decoders.TomlSignerTypeDecoder
+import org.apache.logging.log4j.Level
+import org.apache.logging.log4j.LogManager
+import org.apache.logging.log4j.Logger
+import java.nio.file.Path
+
+fun ConfigLoaderBuilder.addCoordinatorTomlDecoders(strict: Boolean): ConfigLoaderBuilder {
+ return this
+ .addDecoder(BlockParameterTagDecoder())
+ .addDecoder(BlockParameterNumberDecoder())
+ .addDecoder(BlockParameterDecoder())
+ .addDecoder(TomlByteArrayHexDecoder())
+ .addDecoder(TomlKotlinDurationDecoder())
+ .addDecoder(TomlSignerTypeDecoder())
+ .apply { if (strict) this.strict() }
+}
+
+@OptIn(ExperimentalHoplite::class)
+inline fun parseConfig(toml: String, strict: Boolean = true): T {
+ return ConfigLoaderBuilder
+ .default()
+ .withExplicitSealedTypes()
+ .addCoordinatorTomlDecoders(strict)
+ .addSource(TomlPropertySource(toml))
+ .build()
+ .loadConfigOrThrow()
+}
+
+@OptIn(ExperimentalHoplite::class)
+inline fun loadConfigsOrError(
+ configFiles: List,
+ strict: Boolean,
+): Result {
+ val confLoader = ConfigLoaderBuilder
+ .empty()
+ .addDefaults()
+ .withExplicitSealedTypes()
+ .addCoordinatorTomlDecoders(strict)
+ .build()
+
+ return confLoader
+ .loadConfig(configFiles.reversed().map { it.toAbsolutePath().toString() })
+ .let { configResult: ConfigResult ->
+ when (configResult) {
+ is Validated.Valid -> Ok(configResult.value)
+ is Validated.Invalid -> Err(configResult.getInvalidUnsafe().description())
+ }
+ }
+}
+
+fun logErrorIfPresent(
+ configLoadingResult: Result,
+ logger: Logger,
+ logLevel: Level = Level.ERROR,
+) {
+ if (configLoadingResult is Err) {
+ logger.log(logLevel, configLoadingResult.error)
+ }
+}
+
+inline fun loadConfigsAndLogErrors(
+ configFiles: List,
+ logger: Logger = LogManager.getLogger("linea.coordinator.config"),
+ strict: Boolean,
+): Result {
+ return loadConfigsOrError(configFiles, strict = strict)
+ .also {
+ val logLevel = if (strict) Level.WARN else Level.ERROR
+ logErrorIfPresent(it, logger, logLevel)
+ }
+}
+
+fun loadConfigsOrError(
+ coordinatorConfigFiles: List,
+ tracesLimitsFileV2: Path,
+ gasPriceCapTimeOfDayMultipliersFile: Path,
+ smartContractErrorsFile: Path,
+ logger: Logger = LogManager.getLogger("linea.coordinator.config"),
+ strict: Boolean = false,
+): Result {
+ val coordinatorBaseConfigs =
+ loadConfigsAndLogErrors(coordinatorConfigFiles, logger, strict)
+ val tracesLimitsV2Configs =
+ loadConfigsAndLogErrors(listOf(tracesLimitsFileV2), logger, strict)
+ val gasPriceCapTimeOfDayMultipliersConfig =
+ loadConfigsAndLogErrors(
+ listOf(gasPriceCapTimeOfDayMultipliersFile),
+ logger,
+ strict,
+ )
+ val smartContractErrorsConfig = loadConfigsAndLogErrors(
+ listOf(smartContractErrorsFile),
+ logger,
+ strict,
+ )
+ val configError = listOf(
+ coordinatorBaseConfigs,
+ tracesLimitsV2Configs,
+ gasPriceCapTimeOfDayMultipliersConfig,
+ smartContractErrorsConfig,
+ )
+ .find { it is Err }
+
+ if (configError != null) {
+ @Suppress("UNCHECKED_CAST")
+ return configError as Result
+ }
+
+ val finalConfig = CoordinatorConfigToml(
+ configs = coordinatorBaseConfigs.get()!!,
+ tracesLimitsV2 = tracesLimitsV2Configs.get()!!,
+ l1DynamicGasPriceCapTimeOfDayMultipliers = gasPriceCapTimeOfDayMultipliersConfig.get(),
+ smartContractErrors = smartContractErrorsConfig.get(),
+ )
+ return Ok(finalConfig)
+}
+
+fun loadConfigs(
+ coordinatorConfigFiles: List,
+ tracesLimitsFileV2: Path,
+ gasPriceCapTimeOfDayMultipliersFile: Path,
+ smartContractErrorsFile: Path,
+ logger: Logger = LogManager.getLogger("linea.coordinator.config"),
+ enforceStrict: Boolean = false,
+): CoordinatorConfig {
+ return loadConfigsOrError(
+ coordinatorConfigFiles,
+ tracesLimitsFileV2,
+ gasPriceCapTimeOfDayMultipliersFile,
+ smartContractErrorsFile,
+ logger,
+ strict = true,
+ )
+ .recoverIf({ !enforceStrict }, {
+ loadConfigsOrError(
+ coordinatorConfigFiles,
+ tracesLimitsFileV2,
+ gasPriceCapTimeOfDayMultipliersFile,
+ smartContractErrorsFile,
+ logger,
+ strict = false,
+ ).getOrElse {
+ throw RuntimeException("Invalid configurations: $it")
+ }
+ })
+ .getOrElse {
+ throw RuntimeException("Invalid configurations: $it")
+ }
+ .reified()
+}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/ConflationToml.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/ConflationToml.kt
new file mode 100644
index 0000000000..c449ed236d
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/ConflationToml.kt
@@ -0,0 +1,91 @@
+package linea.coordinator.config.v2.toml
+
+import linea.blob.BlobCompressorVersion
+import linea.coordinator.config.v2.ConflationConfig
+import net.consensys.linea.traces.TracesCountersV2
+import java.net.URL
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
+
+data class ConflationToml(
+ val disabled: Boolean = false,
+ val blocksLimit: UInt? = null,
+ val forceStopConflationAtBlockInclusive: ULong? = null,
+ val conflationDeadline: Duration? = null,
+ val conflationDeadlineCheckInterval: Duration = 30.seconds,
+ val conflationDeadlineLastBlockConfirmationDelay: Duration = 30.seconds,
+ val consistentNumberOfBlocksOnL1ToWait: UInt = 32u, // 1 epoch
+ val newBlocksPollingInterval: Duration = 1.seconds,
+ val l2FetchBlocksLimit: UInt? = null,
+ val l2Endpoint: URL? = null,
+ val l2RequestRetries: RequestRetriesToml? = null,
+ val l2LogsEndpoint: URL? = null,
+ val blobCompression: BlobCompressionToml = BlobCompressionToml(),
+ val proofAggregation: ProofAggregationToml = ProofAggregationToml(),
+) {
+
+ data class BlobCompressionToml(
+ val blobSizeLimit: UInt = 102400u,
+ val handlerPollingInterval: Duration = 1.seconds,
+ val batchesLimit: UInt? = null,
+ val blobCompressorVersion: BlobCompressorVersion = BlobCompressorVersion.V1_2,
+ ) {
+ fun reified(): ConflationConfig.BlobCompression {
+ return ConflationConfig.BlobCompression(
+ blobSizeLimit = this.blobSizeLimit,
+ handlerPollingInterval = this.handlerPollingInterval,
+ batchesLimit = this.batchesLimit,
+ blobCompressorVersion = this.blobCompressorVersion,
+ )
+ }
+ }
+
+ data class ProofAggregationToml(
+ val proofsLimit: UInt = 300u,
+ val deadline: Duration? = null,
+ val deadlineCheckInterval: Duration = 30.seconds,
+ val coordinatorPollingInterval: Duration = 3.seconds,
+ val targetEndBlocks: List? = null,
+ val aggregationSizeMultipleOf: UInt = 1u,
+ ) {
+ fun reified(): ConflationConfig.ProofAggregation {
+ return ConflationConfig.ProofAggregation(
+ proofsLimit = this.proofsLimit,
+ deadline = this.deadline ?: Duration.INFINITE,
+ deadlineCheckInterval = this.deadlineCheckInterval,
+ coordinatorPollingInterval = this.coordinatorPollingInterval,
+ targetEndBlocks = this.targetEndBlocks,
+ aggregationSizeMultipleOf = this.aggregationSizeMultipleOf,
+ )
+ }
+ }
+
+ fun reified(
+ defaults: DefaultsToml,
+ tracesCountersLimitsV2: TracesCountersV2,
+ ): ConflationConfig {
+ return ConflationConfig(
+ disabled = this.disabled,
+ blocksLimit = this.blocksLimit,
+ forceStopConflationAtBlockInclusive = this.forceStopConflationAtBlockInclusive,
+ blocksPollingInterval = this.newBlocksPollingInterval,
+ conflationDeadline = this.conflationDeadline,
+ conflationDeadlineCheckInterval = this.conflationDeadlineCheckInterval,
+ conflationDeadlineLastBlockConfirmationDelay = this.conflationDeadlineLastBlockConfirmationDelay,
+ consistentNumberOfBlocksOnL1ToWait = this.consistentNumberOfBlocksOnL1ToWait,
+ l2FetchBlocksLimit = this.l2FetchBlocksLimit ?: UInt.MAX_VALUE,
+ l2Endpoint = this.l2Endpoint
+ ?: defaults.l2Endpoint
+ ?: throw AssertionError("l2Endpoint config missing"),
+ l2RequestRetries = this.l2RequestRetries?.asDomain
+ ?: defaults.l2RequestRetries.asDomain,
+ l2GetLogsEndpoint = this.l2LogsEndpoint
+ ?: this.l2Endpoint
+ ?: defaults.l2Endpoint
+ ?: throw AssertionError("please set l2GetLogsEndpoint or l2Endpoint config"),
+ blobCompression = this.blobCompression.reified(),
+ proofAggregation = this.proofAggregation.reified(),
+ tracesLimitsV2 = tracesCountersLimitsV2,
+ )
+ }
+}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/CoordinatorConfigFilesToml.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/CoordinatorConfigFilesToml.kt
new file mode 100644
index 0000000000..191221f844
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/CoordinatorConfigFilesToml.kt
@@ -0,0 +1,73 @@
+package linea.coordinator.config.v2.toml
+
+import linea.coordinator.config.v2.CoordinatorConfig
+import linea.web3j.SmartContractErrors
+import net.consensys.linea.ethereum.gaspricing.dynamiccap.TimeOfDayMultipliers
+import net.consensys.linea.traces.TracesCountersV2
+import net.consensys.linea.traces.TracingModuleV2
+
+data class CoordinatorConfigFileToml(
+ val defaults: DefaultsToml = DefaultsToml(),
+ val protocol: ProtocolToml,
+ val conflation: ConflationToml = ConflationToml(),
+ val prover: ProverToml,
+ val traces: TracesToml,
+ val stateManager: StateManagerToml,
+ val type2StateProofProvider: Type2StateProofManagerToml,
+ val l1FinalizationMonitor: L1FinalizationMonitorConfigToml,
+ val l1Submission: L1SubmissionConfigToml,
+ val messageAnchoring: MessageAnchoringConfigToml,
+ val l2NetworkGasPricing: L2NetworkGasPricingConfigToml,
+ val database: DatabaseToml,
+ val api: ApiConfigToml = ApiConfigToml(),
+)
+
+data class TracesLimitsConfigFileToml(
+ val tracesLimits: Map,
+)
+
+data class GasPriceCapTimeOfDayMultipliersConfigFileToml(
+ val gasPriceCapTimeOfDayMultipliers: TimeOfDayMultipliers,
+)
+
+data class SmartContractErrorCodesConfigFileToml(val smartContractErrors: SmartContractErrors)
+
+data class CoordinatorConfigToml(
+ val configs: CoordinatorConfigFileToml,
+ val tracesLimitsV2: TracesLimitsConfigFileToml,
+ val l1DynamicGasPriceCapTimeOfDayMultipliers: GasPriceCapTimeOfDayMultipliersConfigFileToml? = null,
+ val smartContractErrors: SmartContractErrorCodesConfigFileToml? = null,
+) {
+ fun reified(): CoordinatorConfig {
+ return CoordinatorConfig(
+ protocol = configs.protocol.reified(),
+ conflation = configs.conflation.reified(
+ defaults = configs.defaults,
+ tracesCountersLimitsV2 = TracesCountersV2(tracesLimitsV2.tracesLimits),
+ ),
+ proversConfig = this.configs.prover.reified(),
+ traces = this.configs.traces.reified(),
+ stateManager = this.configs.stateManager.reified(),
+ type2StateProofProvider = this.configs.type2StateProofProvider.reified(),
+ l1FinalizationMonitor = this.configs.l1FinalizationMonitor.reified(
+ defaults = this.configs.defaults,
+ ),
+ l1Submission = this.configs.l1Submission.reified(
+ l1DefaultEndpoint = this.configs.defaults.l1Endpoint,
+ timeOfDayMultipliers = l1DynamicGasPriceCapTimeOfDayMultipliers
+ ?.gasPriceCapTimeOfDayMultipliers
+ ?: emptyMap(),
+ ),
+ messageAnchoring = this.configs.messageAnchoring.reified(
+ l1DefaultEndpoint = this.configs.defaults.l1Endpoint,
+ l2DefaultEndpoint = this.configs.defaults.l2Endpoint,
+ ),
+ l2NetworkGasPricing = this.configs.l2NetworkGasPricing.reified(
+ l1DefaultEndpoint = this.configs.defaults.l1Endpoint,
+ ),
+ database = this.configs.database.reified(),
+ api = this.configs.api.reified(),
+ smartContractErrors = smartContractErrors?.smartContractErrors ?: emptyMap(),
+ )
+ }
+}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/DatabaseToml.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/DatabaseToml.kt
new file mode 100644
index 0000000000..19b2a207c7
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/DatabaseToml.kt
@@ -0,0 +1,36 @@
+package linea.coordinator.config.v2.toml
+
+import com.sksamuel.hoplite.Masked
+import linea.coordinator.config.v2.DatabaseConfig
+import kotlin.time.Duration.Companion.minutes
+import kotlin.time.Duration.Companion.seconds
+
+data class DatabaseToml(
+ val hostname: String,
+ val port: UInt = 5432u,
+ val username: String,
+ val password: Masked,
+ val schema: String = "linea_coordinator",
+ val readPoolSize: Int = 10,
+ val readPipeliningLimit: Int = 10,
+ val transactionalPoolSize: Int = 10,
+ val persistenceRetries: RequestRetriesToml = RequestRetriesToml(
+ backoffDelay = 1.seconds,
+ timeout = 10.minutes,
+ failuresWarningThreshold = 3u,
+ ),
+) {
+ fun reified(): DatabaseConfig {
+ return DatabaseConfig(
+ host = this.hostname,
+ port = this.port.toInt(),
+ username = this.username,
+ password = this.password,
+ schema = this.schema,
+ readPoolSize = this.readPoolSize,
+ readPipeliningLimit = this.readPipeliningLimit,
+ transactionalPoolSize = this.transactionalPoolSize,
+ persistenceRetries = this.persistenceRetries.asDomain,
+ )
+ }
+}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/DefaultsToml.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/DefaultsToml.kt
new file mode 100644
index 0000000000..4a7b094a4c
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/DefaultsToml.kt
@@ -0,0 +1,17 @@
+package linea.coordinator.config.v2.toml
+
+import java.net.URL
+import kotlin.time.Duration.Companion.seconds
+
+data class DefaultsToml(
+ val l1Endpoint: URL? = null,
+ val l2Endpoint: URL? = null,
+ val l1RequestRetries: RequestRetriesToml = RequestRetriesToml.endlessRetry(
+ backoffDelay = 1.seconds,
+ failuresWarningThreshold = 3u,
+ ),
+ val l2RequestRetries: RequestRetriesToml = RequestRetriesToml.endlessRetry(
+ backoffDelay = 1.seconds,
+ failuresWarningThreshold = 3u,
+ ),
+)
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/L1FinalizationMonitorConfigToml.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/L1FinalizationMonitorConfigToml.kt
new file mode 100644
index 0000000000..516bf586c0
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/L1FinalizationMonitorConfigToml.kt
@@ -0,0 +1,25 @@
+package linea.coordinator.config.v2.toml
+
+import linea.coordinator.config.v2.L1FinalizationMonitorConfig
+import linea.domain.BlockParameter
+import java.net.URL
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
+
+data class L1FinalizationMonitorConfigToml(
+ val l1Endpoint: URL?,
+ val l2Endpoint: URL?,
+ val l1PollingInterval: Duration = 6.seconds,
+ val l1QueryBlockTag: BlockParameter.Tag = BlockParameter.Tag.FINALIZED,
+) {
+ fun reified(
+ defaults: DefaultsToml,
+ ): L1FinalizationMonitorConfig {
+ return L1FinalizationMonitorConfig(
+ l1Endpoint = this.l1Endpoint ?: defaults.l1Endpoint ?: throw AssertionError("l1Endpoint missing"),
+ l2Endpoint = this.l2Endpoint ?: defaults.l2Endpoint ?: throw AssertionError("l2Endpoint missing"),
+ l1PollingInterval = this.l1PollingInterval,
+ l1QueryBlockTag = this.l1QueryBlockTag,
+ )
+ }
+}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/L1SubmissionConfigToml.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/L1SubmissionConfigToml.kt
new file mode 100644
index 0000000000..aed86f5748
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/L1SubmissionConfigToml.kt
@@ -0,0 +1,172 @@
+package linea.coordinator.config.v2.toml
+
+import linea.coordinator.config.v2.L1SubmissionConfig
+import linea.coordinator.config.v2.L1SubmissionConfig.DynamicGasPriceCapConfig.GasPriceCapCalculationConfig
+import net.consensys.linea.ethereum.gaspricing.dynamiccap.TimeOfDayMultipliers
+import java.net.URL
+import kotlin.ULong
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.days
+import kotlin.time.Duration.Companion.seconds
+
+data class L1SubmissionConfigToml(
+ val disabled: Boolean = false,
+ val dynamicGasPriceCap: DynamicGasPriceCapToml,
+ val fallbackGasPrice: FallbackGasPriceToml,
+ val blob: BlobSubmissionConfigToml,
+ val aggregation: AggregationSubmissionToml,
+) {
+
+ data class DynamicGasPriceCapToml(
+ val disabled: Boolean = false,
+ val gasPriceCapCalculation: GasPriceCapCalculationToml,
+ val feeHistoryFetcher: FeeHistoryFetcherConfig = FeeHistoryFetcherConfig(),
+ ) {
+ data class GasPriceCapCalculationToml(
+ val adjustmentConstant: UInt,
+ val blobAdjustmentConstant: UInt,
+ val finalizationTargetMaxDelay: Duration,
+ val baseFeePerGasPercentileWindow: Duration,
+ val baseFeePerGasPercentileWindowLeeway: Duration,
+ val baseFeePerGasPercentile: UInt,
+ val gasPriceCapsCheckCoefficient: Double,
+ val historicBaseFeePerBlobGasLowerBound: ULong,
+ val historicAvgRewardConstant: ULong,
+ ) {
+ fun reified(
+ timeOfTheDayMultipliers: TimeOfDayMultipliers,
+ ): GasPriceCapCalculationConfig {
+ return GasPriceCapCalculationConfig(
+ adjustmentConstant = this.adjustmentConstant,
+ blobAdjustmentConstant = this.blobAdjustmentConstant,
+ finalizationTargetMaxDelay = this.finalizationTargetMaxDelay,
+ baseFeePerGasPercentileWindow = this.baseFeePerGasPercentileWindow,
+ baseFeePerGasPercentileWindowLeeway = this.baseFeePerGasPercentileWindowLeeway,
+ baseFeePerGasPercentile = this.baseFeePerGasPercentile,
+ gasPriceCapsCheckCoefficient = this.gasPriceCapsCheckCoefficient,
+ historicBaseFeePerBlobGasLowerBound = this.historicBaseFeePerBlobGasLowerBound,
+ historicAvgRewardConstant = this.historicAvgRewardConstant,
+ timeOfTheDayMultipliers = timeOfTheDayMultipliers,
+ )
+ }
+ }
+
+ data class FeeHistoryFetcherConfig(
+ val l1Endpoint: URL? = null,
+ val fetchInterval: Duration = 3.seconds,
+ val maxBlockCount: UInt = 1000u,
+ val rewardPercentiles: List = listOf(10u, 20u, 30u, 40u, 50u, 60u, 70u, 80u, 90u, 100u),
+ val numOfBlocksBeforeLatest: UInt = 4u,
+ val storagePeriod: Duration = 10.days,
+ ) {
+ fun reified(
+ defaultL1Endpoint: URL?,
+ ): L1SubmissionConfig.DynamicGasPriceCapConfig.FeeHistoryFetcherConfig {
+ return L1SubmissionConfig.DynamicGasPriceCapConfig.FeeHistoryFetcherConfig(
+ l1Endpoint = this.l1Endpoint ?: defaultL1Endpoint
+ ?: throw AssertionError("l1Endpoint config missing"),
+ fetchInterval = this.fetchInterval,
+ maxBlockCount = this.maxBlockCount,
+ rewardPercentiles = this.rewardPercentiles,
+ numOfBlocksBeforeLatest = this.numOfBlocksBeforeLatest,
+ storagePeriod = this.storagePeriod,
+ )
+ }
+ }
+ }
+
+ data class FallbackGasPriceToml(
+ val feeHistoryBlockCount: UInt,
+ val feeHistoryRewardPercentile: UInt,
+ )
+
+ data class GasConfigToml(
+ val gasLimit: ULong,
+ val maxFeePerGasCap: ULong,
+ val maxFeePerBlobGasCap: ULong? = null,
+ val maxPriorityFeePerGasCap: ULong,
+ val fallback: FallbackGasConfig,
+ ) {
+ data class FallbackGasConfig(
+ val priorityFeePerGasUpperBound: ULong,
+ val priorityFeePerGasLowerBound: ULong,
+ )
+
+ fun reified(): L1SubmissionConfig.GasConfig {
+ return L1SubmissionConfig.GasConfig(
+ gasLimit = this.gasLimit,
+ maxFeePerGasCap = this.maxFeePerGasCap,
+ maxPriorityFeePerGasCap = this.maxPriorityFeePerGasCap,
+ maxFeePerBlobGasCap = this.maxFeePerBlobGasCap,
+ fallback = L1SubmissionConfig.GasConfig.FallbackGasConfig(
+ priorityFeePerGasLowerBound = this.fallback.priorityFeePerGasLowerBound,
+ priorityFeePerGasUpperBound = this.fallback.priorityFeePerGasUpperBound,
+ ),
+ )
+ }
+ }
+
+ data class BlobSubmissionConfigToml(
+ val disabled: Boolean = false,
+ val l1Endpoint: URL? = null,
+ val submissionDelay: Duration = 0.seconds,
+ val submissionTickInterval: Duration = 12.seconds,
+ val maxSubmissionTransactionsPerTick: UInt = 2u,
+ // eip-7691 on Prague fork allows up to 9 blobs per transaction.
+ // however, Geth nodes fail with "transaction too large" error. only 7 blobs are accepted
+ val targetBlobsPerTransaction: UInt = 7u,
+ val dbMaxBlobsToReturn: UInt = 100u,
+ val gas: GasConfigToml,
+ val signer: SignerConfigToml,
+ )
+
+ data class AggregationSubmissionToml(
+ val disabled: Boolean = false,
+ val l1Endpoint: URL? = null,
+ val submissionDelay: Duration = 0.seconds,
+ val submissionTickInterval: Duration = 24.seconds,
+ val maxSubmissionsPerTick: UInt = 1u,
+ val gas: GasConfigToml,
+ val signer: SignerConfigToml,
+ )
+
+ fun reified(
+ l1DefaultEndpoint: URL?,
+ timeOfDayMultipliers: TimeOfDayMultipliers,
+ ): L1SubmissionConfig {
+ return L1SubmissionConfig(
+ dynamicGasPriceCap = L1SubmissionConfig.DynamicGasPriceCapConfig(
+ disabled = this.dynamicGasPriceCap.disabled,
+ gasPriceCapCalculation = this.dynamicGasPriceCap.gasPriceCapCalculation.reified(timeOfDayMultipliers),
+ feeHistoryFetcher = this.dynamicGasPriceCap.feeHistoryFetcher.reified(l1DefaultEndpoint),
+ timeOfDayMultipliers = timeOfDayMultipliers,
+ ),
+ fallbackGasPrice = L1SubmissionConfig.FallbackGasPriceConfig(
+ feeHistoryBlockCount = this.fallbackGasPrice.feeHistoryBlockCount,
+ feeHistoryRewardPercentile = this.fallbackGasPrice.feeHistoryRewardPercentile,
+ ),
+ blob = L1SubmissionConfig.BlobSubmissionConfig(
+ disabled = this.blob.disabled,
+ l1Endpoint = this.blob.l1Endpoint ?: l1DefaultEndpoint
+ ?: throw AssertionError("l1Endpoint config missing"),
+ submissionDelay = this.blob.submissionDelay,
+ submissionTickInterval = this.blob.submissionTickInterval,
+ maxSubmissionTransactionsPerTick = this.blob.maxSubmissionTransactionsPerTick,
+ targetBlobsPerTransaction = this.blob.targetBlobsPerTransaction,
+ dbMaxBlobsToReturn = this.blob.dbMaxBlobsToReturn,
+ gas = this.blob.gas.reified(),
+ signer = this.blob.signer.reified(),
+ ),
+ aggregation = L1SubmissionConfig.AggregationSubmissionConfig(
+ disabled = this.aggregation.disabled,
+ l1Endpoint = this.aggregation.l1Endpoint ?: l1DefaultEndpoint
+ ?: throw AssertionError("l1Endpoint config missing"),
+ submissionDelay = this.aggregation.submissionDelay,
+ submissionTickInterval = this.aggregation.submissionTickInterval,
+ maxSubmissionsPerTick = this.aggregation.maxSubmissionsPerTick,
+ gas = this.aggregation.gas.reified(),
+ signer = this.aggregation.signer.reified(),
+ ),
+ )
+ }
+}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/L2NetworkGasPricingConfigToml.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/L2NetworkGasPricingConfigToml.kt
new file mode 100644
index 0000000000..10b7118bf2
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/L2NetworkGasPricingConfigToml.kt
@@ -0,0 +1,83 @@
+package linea.coordinator.config.v2.toml
+
+import linea.coordinator.config.v2.L2NetworkGasPricingConfig
+import java.net.URL
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
+
+data class L2NetworkGasPricingConfigToml(
+ val disabled: Boolean = false,
+ val l1Endpoint: URL? = null,
+ val priceUpdateInterval: Duration = 12.seconds,
+ val feeHistoryBlockCount: UInt = 1000u,
+ val feeHistoryRewardPercentile: UInt = 15u,
+ val gasPriceFixedCost: ULong,
+ val extraDataUpdateEndpoint: URL,
+ val extraDataUpdateRequestRetries: RequestRetriesToml = RequestRetriesToml(
+ timeout = 8.seconds,
+ backoffDelay = 1.seconds,
+ failuresWarningThreshold = 3u,
+ ),
+ val dynamicGasPricing: DynamicGasPricingToml,
+ val flatRateGasPricing: FlatRateGasPricingToml,
+) {
+ init {
+ require(feeHistoryBlockCount > 0u) { "feeHistoryBlockCount=$feeHistoryBlockCount must be greater than 0" }
+ require(feeHistoryRewardPercentile in 1u..100u) {
+ "feeHistoryRewardPercentile=$feeHistoryRewardPercentile must be between 1..100"
+ }
+ }
+
+ data class DynamicGasPricingToml(
+ val l1BlobGas: ULong,
+ val blobSubmissionExpectedExecutionGas: ULong,
+ val variableCostUpperBound: ULong,
+ val variableCostLowerBound: ULong,
+ val margin: Double,
+ ) {
+ fun reified(): L2NetworkGasPricingConfig.DynamicGasPricing {
+ return L2NetworkGasPricingConfig.DynamicGasPricing(
+ l1BlobGas = this.l1BlobGas,
+ blobSubmissionExpectedExecutionGas = this.blobSubmissionExpectedExecutionGas,
+ variableCostUpperBound = this.variableCostUpperBound,
+ variableCostLowerBound = this.variableCostLowerBound,
+ margin = this.margin,
+ )
+ }
+ }
+
+ data class FlatRateGasPricingToml(
+ val gasPriceLowerBound: ULong,
+ val gasPriceUpperBound: ULong,
+ val plainTransferCostMultiplier: Double = 1.0,
+ val compressedTxSize: UInt = 125u,
+ val expectedGas: UInt = 21000u,
+ ) {
+ fun reified(): L2NetworkGasPricingConfig.FlatRateGasPricing {
+ return L2NetworkGasPricingConfig.FlatRateGasPricing(
+ gasPriceLowerBound = this.gasPriceLowerBound,
+ gasPriceUpperBound = this.gasPriceUpperBound,
+ plainTransferCostMultiplier = this.plainTransferCostMultiplier,
+ compressedTxSize = this.compressedTxSize,
+ expectedGas = this.expectedGas,
+ )
+ }
+ }
+
+ fun reified(
+ l1DefaultEndpoint: URL?,
+ ): L2NetworkGasPricingConfig {
+ return L2NetworkGasPricingConfig(
+ disabled = disabled,
+ priceUpdateInterval = this.priceUpdateInterval,
+ feeHistoryBlockCount = this.feeHistoryBlockCount,
+ feeHistoryRewardPercentile = this.feeHistoryRewardPercentile,
+ gasPriceFixedCost = this.gasPriceFixedCost,
+ dynamicGasPricing = this.dynamicGasPricing.reified(),
+ flatRateGasPricing = this.flatRateGasPricing.reified(),
+ extraDataUpdateEndpoint = this.extraDataUpdateEndpoint,
+ extraDataUpdateRequestRetries = this.extraDataUpdateRequestRetries.asDomain,
+ l1Endpoint = this.l1Endpoint ?: l1DefaultEndpoint ?: throw AssertionError("l1Endpoint must be set"),
+ )
+ }
+}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/MessageAnchoringConfigToml.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/MessageAnchoringConfigToml.kt
new file mode 100644
index 0000000000..cb4caa55fb
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/MessageAnchoringConfigToml.kt
@@ -0,0 +1,106 @@
+package linea.coordinator.config.v2.toml
+
+import linea.coordinator.config.v2.MessageAnchoringConfig
+import linea.domain.BlockParameter
+import java.net.URL
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.Duration.Companion.seconds
+
+data class MessageAnchoringConfigToml(
+ var disabled: Boolean = false,
+ val anchoringTickInterval: Duration = 10.seconds,
+ val messageQueueCapacity: UInt = 10_000u,
+ val maxMessagesToAnchorPerL2Transaction: UInt = 100u,
+ val l1Endpoint: URL? = null, // shall default to L1 endpoint
+ val l1HighestBlockTag: BlockParameter = BlockParameter.Tag.FINALIZED,
+ val l1RequestRetries: RequestRetriesToml = RequestRetriesToml.endlessRetry(
+ backoffDelay = 1.seconds,
+ failuresWarningThreshold = 3u,
+ ),
+ val l1EventScraping: L1EventScrapping = L1EventScrapping(),
+ val l2Endpoint: URL? = null,
+ val l2HighestBlockTag: BlockParameter = BlockParameter.Tag.LATEST,
+ val l2RequestRetries: RequestRetriesToml = RequestRetriesToml(
+ maxRetries = null,
+ backoffDelay = 1.seconds,
+ timeout = 8.seconds,
+ failuresWarningThreshold = 3u,
+ ),
+ val signer: SignerConfigToml,
+ val gas: GasConfig = GasConfig(),
+) {
+ init {
+ require(messageQueueCapacity >= 1u) {
+ "messageQueueCapacity=$messageQueueCapacity be equal or greater than 1"
+ }
+ require(maxMessagesToAnchorPerL2Transaction >= 1u) {
+ "maxMessagesToAnchorPerL2Transaction=$maxMessagesToAnchorPerL2Transaction be equal or greater than 1"
+ }
+
+ require(anchoringTickInterval >= 1.milliseconds) {
+ "anchoringTickInterval must be equal or greater than 1ms"
+ }
+ }
+
+ data class L1EventScrapping(
+ val pollingInterval: Duration = 6.seconds,
+ val pollingTimeout: Duration = 5.seconds,
+ val ethLogsSearchSuccessBackoffDelay: Duration = 1.milliseconds,
+ val ethLogsSearchBlockChunkSize: UInt = 1000u,
+ ) {
+ init {
+
+ require(pollingInterval >= 1.milliseconds) {
+ "pollingInterval=$pollingInterval must be equal or greater than 1ms"
+ }
+ require(pollingTimeout >= 1.milliseconds) {
+ "pollingTimeout=$pollingTimeout must be equal or greater than 1ms"
+ }
+ require(ethLogsSearchSuccessBackoffDelay >= 1.milliseconds) {
+ "ethLogsSearchSuccessBackoffDelay=$ethLogsSearchSuccessBackoffDelay must be equal or greater than 1ms"
+ }
+ require(ethLogsSearchBlockChunkSize >= 1u) {
+ "ethLogsSearchBlockChunkSize=$ethLogsSearchBlockChunkSize must be equal or greater than 1"
+ }
+ }
+ }
+
+ data class GasConfig(
+ val maxFeePerGasCap: ULong = 100_000_000_000uL, // 100 gwei
+ val gasLimit: ULong = 2_500_000uL,
+ val feeHistoryBlockCount: UInt = 4u,
+ val feeHistoryRewardPercentile: UInt = 15u,
+ )
+
+ fun reified(
+ l1DefaultEndpoint: URL?,
+ l2DefaultEndpoint: URL?,
+ ): MessageAnchoringConfig {
+ return MessageAnchoringConfig(
+ disabled = disabled,
+ l1Endpoint = l1Endpoint ?: l1DefaultEndpoint ?: throw AssertionError("l1Endpoint must be set"),
+ l2Endpoint = l2Endpoint ?: l2DefaultEndpoint ?: throw AssertionError("l2Endpoint must be set"),
+ l1HighestBlockTag = l1HighestBlockTag,
+ l2HighestBlockTag = l2HighestBlockTag,
+ l1RequestRetries = l1RequestRetries.asDomain,
+ l2RequestRetries = l2RequestRetries.asDomain,
+ l1EventScrapping = MessageAnchoringConfig.L1EventScrapping(
+ pollingInterval = l1EventScraping.pollingInterval,
+ pollingTimeout = l1EventScraping.pollingTimeout,
+ ethLogsSearchSuccessBackoffDelay = l1EventScraping.ethLogsSearchSuccessBackoffDelay,
+ ethLogsSearchBlockChunkSize = l1EventScraping.ethLogsSearchBlockChunkSize,
+ ),
+ anchoringTickInterval = anchoringTickInterval,
+ messageQueueCapacity = messageQueueCapacity.toUInt(),
+ maxMessagesToAnchorPerL2Transaction = maxMessagesToAnchorPerL2Transaction.toUInt(),
+ signer = signer.reified(),
+ gas = MessageAnchoringConfig.GasConfig(
+ maxFeePerGasCap = gas.maxFeePerGasCap,
+ gasLimit = gas.gasLimit,
+ feeHistoryBlockCount = gas.feeHistoryBlockCount,
+ feeHistoryRewardPercentile = gas.feeHistoryRewardPercentile,
+ ),
+ )
+ }
+}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/ProtocolToml.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/ProtocolToml.kt
new file mode 100644
index 0000000000..91120ce00e
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/ProtocolToml.kt
@@ -0,0 +1,65 @@
+package linea.coordinator.config.v2.toml
+
+import linea.coordinator.config.v2.ProtocolConfig
+import linea.domain.BlockParameter
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
+
+data class ProtocolToml(
+ val genesis: Genesis,
+ val l1: Layer1Config,
+ val l2: Layer2Config,
+) {
+ data class Genesis(
+ val genesisStateRootHash: ByteArray,
+ val genesisShnarf: ByteArray,
+ ) {
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as Genesis
+
+ if (!genesisStateRootHash.contentEquals(other.genesisStateRootHash)) return false
+ if (!genesisShnarf.contentEquals(other.genesisShnarf)) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = genesisStateRootHash.contentHashCode()
+ result = 31 * result + genesisShnarf.contentHashCode()
+ return result
+ }
+ }
+
+ data class Layer1Config(
+ val contractAddress: String,
+ val blockTime: Duration = 12.seconds,
+ )
+
+ data class Layer2Config(
+ val contractAddress: String,
+ // hoplite limitation: it does not work with nullable BlockParameter.BlockNumber?
+ val contractDeploymentBlockNumber: ULong?,
+ )
+
+ fun reified(): ProtocolConfig {
+ return ProtocolConfig(
+ genesis = ProtocolConfig.Genesis(
+ genesisStateRootHash = this.genesis.genesisStateRootHash,
+ genesisShnarf = this.genesis.genesisShnarf,
+ ),
+ l1 = ProtocolConfig.Layer1Config(
+ contractAddress = this.l1.contractAddress,
+ blockTime = this.l1.blockTime,
+ ),
+ l2 = ProtocolConfig.Layer2Config(
+ contractAddress = this.l2.contractAddress,
+ contractDeploymentBlockNumber = this.l2.contractDeploymentBlockNumber
+ ?.let { BlockParameter.BlockNumber(it) },
+ ),
+ )
+ }
+}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/ProverToml.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/ProverToml.kt
new file mode 100644
index 0000000000..dd09c5539e
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/ProverToml.kt
@@ -0,0 +1,88 @@
+package linea.coordinator.config.v2.toml
+
+import net.consensys.zkevm.coordinator.clients.prover.FileBasedProverConfig
+import net.consensys.zkevm.coordinator.clients.prover.ProverConfig
+import net.consensys.zkevm.coordinator.clients.prover.ProversConfig
+import java.nio.file.Path
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
+
+data class ProverToml(
+ val version: String,
+ val fsInprogressRequestWritingSuffix: String = ".inprogress_coordinator_writing",
+ val fsInprogressProvingSuffixPattern: String = "\\.inprogress\\.prover.*",
+ val fsPollingInterval: Duration = 15.seconds,
+ val fsPollingTimeout: Duration = Duration.INFINITE,
+ val execution: ProverDirectoriesToml,
+ val blobCompression: ProverDirectoriesToml,
+ val proofAggregation: ProverDirectoriesToml,
+ val switchBlockNumberInclusive: ULong? = null,
+ val new: ProverToml? = null,
+) {
+ data class ProverDirectoriesToml(
+ val fsRequestsDirectory: String,
+ val fsResponsesDirectory: String,
+ )
+
+ fun reified(): ProversConfig {
+ return ProversConfig(
+ proverA = ProverConfig(
+ execution = FileBasedProverConfig(
+ requestsDirectory = Path.of(this.execution.fsRequestsDirectory),
+ responsesDirectory = Path.of(this.execution.fsResponsesDirectory),
+ inprogressProvingSuffixPattern = this.fsInprogressProvingSuffixPattern,
+ inprogressRequestWritingSuffix = this.fsInprogressRequestWritingSuffix,
+ pollingInterval = this.fsPollingInterval,
+ pollingTimeout = this.fsPollingTimeout,
+ ),
+ blobCompression = FileBasedProverConfig(
+ requestsDirectory = Path.of(this.blobCompression.fsRequestsDirectory),
+ responsesDirectory = Path.of(this.blobCompression.fsResponsesDirectory),
+ inprogressProvingSuffixPattern = this.fsInprogressProvingSuffixPattern,
+ inprogressRequestWritingSuffix = this.fsInprogressRequestWritingSuffix,
+ pollingInterval = this.fsPollingInterval,
+ pollingTimeout = this.fsPollingTimeout,
+
+ ),
+ proofAggregation = FileBasedProverConfig(
+ requestsDirectory = Path.of(this.proofAggregation.fsRequestsDirectory),
+ responsesDirectory = Path.of(this.proofAggregation.fsResponsesDirectory),
+ inprogressProvingSuffixPattern = this.fsInprogressProvingSuffixPattern,
+ inprogressRequestWritingSuffix = this.fsInprogressRequestWritingSuffix,
+ pollingInterval = this.fsPollingInterval,
+ pollingTimeout = this.fsPollingTimeout,
+ ),
+ ),
+ switchBlockNumberInclusive = this.switchBlockNumberInclusive ?: this.new?.switchBlockNumberInclusive,
+ proverB = this.new?.let { newProverConfig ->
+ ProverConfig(
+ execution = FileBasedProverConfig(
+ requestsDirectory = Path.of(newProverConfig.execution.fsRequestsDirectory),
+ responsesDirectory = Path.of(newProverConfig.execution.fsResponsesDirectory),
+ inprogressProvingSuffixPattern = newProverConfig.fsInprogressProvingSuffixPattern,
+ inprogressRequestWritingSuffix = newProverConfig.fsInprogressRequestWritingSuffix,
+ pollingInterval = newProverConfig.fsPollingInterval,
+ pollingTimeout = newProverConfig.fsPollingTimeout,
+ ),
+ blobCompression = FileBasedProverConfig(
+ requestsDirectory = Path.of(newProverConfig.blobCompression.fsRequestsDirectory),
+ responsesDirectory = Path.of(newProverConfig.blobCompression.fsResponsesDirectory),
+ inprogressProvingSuffixPattern = newProverConfig.fsInprogressProvingSuffixPattern,
+ inprogressRequestWritingSuffix = newProverConfig.fsInprogressRequestWritingSuffix,
+ pollingInterval = newProverConfig.fsPollingInterval,
+ pollingTimeout = newProverConfig.fsPollingTimeout,
+
+ ),
+ proofAggregation = FileBasedProverConfig(
+ requestsDirectory = Path.of(newProverConfig.proofAggregation.fsRequestsDirectory),
+ responsesDirectory = Path.of(newProverConfig.proofAggregation.fsResponsesDirectory),
+ inprogressProvingSuffixPattern = newProverConfig.fsInprogressProvingSuffixPattern,
+ inprogressRequestWritingSuffix = newProverConfig.fsInprogressRequestWritingSuffix,
+ pollingInterval = newProverConfig.fsPollingInterval,
+ pollingTimeout = newProverConfig.fsPollingTimeout,
+ ),
+ )
+ },
+ )
+ }
+}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/RequestRetriesToml.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/RequestRetriesToml.kt
new file mode 100644
index 0000000000..48f8334e7e
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/RequestRetriesToml.kt
@@ -0,0 +1,54 @@
+package linea.coordinator.config.v2.toml
+
+import net.consensys.linea.jsonrpc.client.RequestRetryConfig
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.Duration.Companion.seconds
+
+data class RequestRetriesToml(
+ val maxRetries: UInt? = null,
+ val timeout: Duration? = null,
+ val backoffDelay: Duration = 1.seconds,
+ val failuresWarningThreshold: UInt? = null,
+) {
+ init {
+ maxRetries?.also {
+ require(maxRetries >= 1u) { "maxRetries must be >=1. value=$maxRetries" }
+ }
+ timeout?.also {
+ require(timeout >= 1.milliseconds) { "timeout must be >= 1ms. value=$timeout" }
+ }
+ require(backoffDelay >= 1.milliseconds) {
+ "backoffDelay must be >= 1ms. value=$backoffDelay"
+ }
+ require(failuresWarningThreshold == null || failuresWarningThreshold > 0u) {
+ "failuresWarningThreshold must be greater than or equal to 0. value=$failuresWarningThreshold"
+ }
+ }
+
+ internal val asJsonRpcRetryConfig = RequestRetryConfig(
+ maxRetries = maxRetries?.toUInt(),
+ timeout = timeout,
+ backoffDelay = backoffDelay,
+ failuresWarningThreshold = failuresWarningThreshold?.toUInt() ?: 0u,
+ )
+
+ internal val asDomain: linea.domain.RetryConfig = linea.domain.RetryConfig(
+ maxRetries = maxRetries?.toUInt(),
+ timeout = timeout,
+ backoffDelay = backoffDelay,
+ failuresWarningThreshold = failuresWarningThreshold?.toUInt() ?: 0u,
+ )
+
+ companion object {
+ fun endlessRetry(
+ backoffDelay: Duration,
+ failuresWarningThreshold: UInt,
+ ) = RequestRetriesToml(
+ maxRetries = null,
+ timeout = null,
+ backoffDelay = backoffDelay,
+ failuresWarningThreshold = failuresWarningThreshold,
+ )
+ }
+}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/SignerConfigToml.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/SignerConfigToml.kt
new file mode 100644
index 0000000000..d0bff4f89c
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/SignerConfigToml.kt
@@ -0,0 +1,116 @@
+package linea.coordinator.config.v2.toml
+
+import com.sksamuel.hoplite.Masked
+import linea.coordinator.config.v2.SignerConfig
+import linea.kotlin.decodeHex
+import java.net.URL
+
+data class SignerConfigToml(
+ val type: SignerType,
+ val web3j: Web3jConfig?,
+ val web3signer: Web3SignerConfig?,
+) {
+ init {
+ when {
+ type == SignerType.WEB3J && web3j == null -> {
+ throw IllegalArgumentException("signetType=$type requires web3j config")
+ }
+
+ type == SignerType.WEB3SIGNER && web3signer == null -> {
+ throw IllegalArgumentException("signetType=$type requires web3signer config")
+ }
+ }
+ }
+
+ enum class SignerType(val mame: String) {
+ WEB3J("web3j"),
+ WEB3SIGNER("web3signer"),
+ ;
+
+ companion object {
+ fun valueOfIgnoreCase(name: String): SignerType {
+ return SignerType.entries.firstOrNull { it.mame.equals(name, ignoreCase = true) }
+ ?: throw IllegalArgumentException("Unknown signer type: $name")
+ }
+ }
+ fun reified(): SignerConfig.SignerType {
+ return when (this) {
+ WEB3J -> SignerConfig.SignerType.WEB3J
+ WEB3SIGNER -> SignerConfig.SignerType.WEB3SIGNER
+ }
+ }
+ }
+
+ data class Web3jConfig(
+ val privateKey: Masked,
+ ) {
+ init {
+ runCatching {
+ privateKey.value.decodeHex()
+ }.onFailure { throw IllegalArgumentException("Invalid hexadecimal encoding of privateKey") }
+ .onSuccess { require(it.size == 32) { "privateKey must be 32 bytes (64 hex characters)" } }
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as Web3jConfig
+
+ return privateKey.value.decodeHex().contentEquals(other.privateKey.value.decodeHex())
+ }
+
+ override fun hashCode(): Int {
+ return privateKey.hashCode()
+ }
+ }
+
+ data class Web3SignerConfig(
+ val endpoint: URL,
+ val publicKey: ByteArray,
+ val maxPoolSize: Int = 10,
+ val keepAlive: Boolean = true,
+ ) {
+ init {
+ require(publicKey.size == 64) { "publicKey must be 64 bytes (128 hex characters)" }
+ require(maxPoolSize > 0) { "maxPoolSize must be greater than 0" }
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as Web3SignerConfig
+
+ if (maxPoolSize != other.maxPoolSize) return false
+ if (keepAlive != other.keepAlive) return false
+ if (endpoint != other.endpoint) return false
+ if (!publicKey.contentEquals(other.publicKey)) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = maxPoolSize
+ result = 31 * result + keepAlive.hashCode()
+ result = 31 * result + endpoint.hashCode()
+ result = 31 * result + publicKey.contentHashCode()
+ return result
+ }
+ }
+
+ fun reified(): SignerConfig {
+ return SignerConfig(
+ type = type.reified(),
+ web3j = web3j?.let { SignerConfig.Web3jConfig(it.privateKey.value.decodeHex()) },
+ web3signer = web3signer?.let {
+ SignerConfig.Web3SignerConfig(
+ endpoint = it.endpoint,
+ publicKey = it.publicKey,
+ maxPoolSize = it.maxPoolSize,
+ keepAlive = it.keepAlive,
+ )
+ },
+ )
+ }
+}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/StateManagerToml.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/StateManagerToml.kt
new file mode 100644
index 0000000000..0e91f68fd1
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/StateManagerToml.kt
@@ -0,0 +1,24 @@
+package linea.coordinator.config.v2.toml
+
+import linea.coordinator.config.v2.StateManagerConfig
+import java.net.URL
+import kotlin.time.Duration.Companion.seconds
+
+data class StateManagerToml(
+ val version: String,
+ val endpoints: List,
+ val requestLimitPerEndpoint: UInt = UInt.MAX_VALUE,
+ val requestRetries: RequestRetriesToml = RequestRetriesToml.endlessRetry(
+ backoffDelay = 1.seconds,
+ failuresWarningThreshold = 3u,
+ ),
+) {
+ fun reified(): StateManagerConfig {
+ return StateManagerConfig(
+ version = this.version,
+ endpoints = this.endpoints,
+ requestLimitPerEndpoint = this.requestLimitPerEndpoint,
+ requestRetries = this.requestRetries.asDomain,
+ )
+ }
+}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/TracesToml.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/TracesToml.kt
new file mode 100644
index 0000000000..031731dae4
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/TracesToml.kt
@@ -0,0 +1,66 @@
+package linea.coordinator.config.v2.toml
+
+import linea.coordinator.config.v2.TracesConfig
+import java.net.URL
+import kotlin.time.Duration.Companion.seconds
+
+data class TracesToml(
+ val expectedTracesApiVersion: String,
+ val counters: ClientApiConfigToml,
+ val conflation: ClientApiConfigToml,
+ val switchBlockNumberInclusive: UInt? = null,
+ val new: TracesToml? = null,
+) {
+ data class ClientApiConfigToml(
+ val endpoints: List,
+ val requestLimitPerEndpoint: UInt = UInt.MAX_VALUE,
+ val requestRetries: RequestRetriesToml = RequestRetriesToml.endlessRetry(
+ backoffDelay = 1.seconds,
+ failuresWarningThreshold = 3u,
+ ),
+ ) {
+ override fun toString(): String {
+ return "ClientApiConfigToml(" +
+ "endpoints=$endpoints, " +
+ "requestLimitPerEndpoint=$requestLimitPerEndpoint, " +
+ "requestRetries=$requestRetries" +
+ ")"
+ }
+ }
+
+ fun reified(): TracesConfig {
+ return TracesConfig(
+ expectedTracesApiVersion = expectedTracesApiVersion,
+ counters = TracesConfig.ClientApiConfig(
+ endpoints = counters.endpoints,
+ requestLimitPerEndpoint = counters.requestLimitPerEndpoint,
+ requestRetries = counters.requestRetries.asDomain,
+ ),
+ conflation = TracesConfig.ClientApiConfig(
+ endpoints = conflation.endpoints,
+ requestLimitPerEndpoint = conflation.requestLimitPerEndpoint,
+ requestRetries = conflation.requestRetries.asDomain,
+ ),
+ /*
+ switchBlockNumberInclusive = switchBlockNumberInclusive,
+ new = new?.let { newTracesConfig ->
+ TracesConfig(
+ expectedTracesApiVersion = newTracesConfig.expectedTracesApiVersion,
+ counters = TracesConfig.ClientApiConfig(
+ endpoints = newTracesConfig.counters.endpoints,
+ requestLimitPerEndpoint = newTracesConfig.counters.requestLimitPerEndpoint,
+ requestRetries = newTracesConfig.counters.requestRetries.asDomain
+ ),
+ conflation = TracesConfig.ClientApiConfig(
+ endpoints = newTracesConfig.conflation.endpoints,
+ requestLimitPerEndpoint = newTracesConfig.conflation.requestLimitPerEndpoint,
+ requestRetries = newTracesConfig.conflation.requestRetries.asDomain
+ ),
+ switchBlockNumberInclusive = null,
+ new = null
+ )
+ }
+ */
+ )
+ }
+}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/Type2StateProofManagerToml.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/Type2StateProofManagerToml.kt
new file mode 100644
index 0000000000..ec59871832
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/Type2StateProofManagerToml.kt
@@ -0,0 +1,28 @@
+package linea.coordinator.config.v2.toml
+
+import linea.coordinator.config.v2.Type2StateProofManagerConfig
+import linea.domain.BlockParameter
+import java.net.URL
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
+
+data class Type2StateProofManagerToml(
+ val disabled: Boolean = false,
+ val endpoints: List,
+ val requestRetries: RequestRetriesToml = RequestRetriesToml.endlessRetry(
+ backoffDelay = 1.seconds,
+ failuresWarningThreshold = 3u,
+ ),
+ val l1QueryBlockTag: BlockParameter.Tag = BlockParameter.Tag.FINALIZED,
+ val l1PollingInterval: Duration = 6.seconds,
+) {
+ fun reified(): Type2StateProofManagerConfig {
+ return Type2StateProofManagerConfig(
+ disabled = this.disabled,
+ endpoints = this.endpoints,
+ requestRetries = this.requestRetries.asDomain,
+ l1QueryBlockTag = this.l1QueryBlockTag,
+ l1PollingInterval = this.l1PollingInterval,
+ )
+ }
+}
diff --git a/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/config/BlockParameterDecoder.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/decoders/BlockParameterDecoder.kt
similarity index 52%
rename from coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/config/BlockParameterDecoder.kt
rename to coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/decoders/BlockParameterDecoder.kt
index daad8fbe93..4c42dd08ed 100644
--- a/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/config/BlockParameterDecoder.kt
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/decoders/BlockParameterDecoder.kt
@@ -1,4 +1,4 @@
-package net.consensys.zkevm.coordinator.app.config
+package linea.coordinator.config.v2.toml.decoders
import com.sksamuel.hoplite.ConfigFailure
import com.sksamuel.hoplite.ConfigResult
@@ -12,21 +12,27 @@ import com.sksamuel.hoplite.fp.valid
import linea.domain.BlockParameter
import kotlin.reflect.KType
-class BlockParameterDecoder : Decoder {
- override fun supports(type: KType): Boolean = type.classifier == BlockParameter::class
- override fun decode(node: Node, type: KType, context: DecoderContext): ConfigResult {
+@Suppress("UNCHECKED_CAST")
+open class AbstractBlockParameterDecoder : Decoder {
+ override fun supports(type: KType): Boolean = type.classifier in listOf(
+ BlockParameter::class,
+ BlockParameter.Tag::class,
+ BlockParameter.BlockNumber::class,
+ )
+
+ override fun decode(node: Node, type: KType, context: DecoderContext): ConfigResult {
return when (node) {
is StringNode -> runCatching {
BlockParameter.parse(node.value)
}.fold(
- { it.valid() },
+ { (it as T).valid() },
{ ConfigFailure.DecodeError(node, type).invalid() },
)
is LongNode -> runCatching {
BlockParameter.fromNumber(node.value)
}.fold(
- { it.valid() },
+ { (it as T).valid() },
{ ConfigFailure.DecodeError(node, type).invalid() },
)
@@ -34,3 +40,15 @@ class BlockParameterDecoder : Decoder {
}
}
}
+
+class BlockParameterTagDecoder : AbstractBlockParameterDecoder() {
+ override fun supports(type: KType): Boolean = type.classifier == BlockParameter.Tag::class
+}
+
+class BlockParameterNumberDecoder : AbstractBlockParameterDecoder() {
+ override fun supports(type: KType): Boolean = type.classifier == BlockParameter.BlockNumber::class
+}
+
+class BlockParameterDecoder : AbstractBlockParameterDecoder() {
+ override fun supports(type: KType): Boolean = type.classifier == BlockParameter::class
+}
diff --git a/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/decoders/Decoders.kt b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/decoders/Decoders.kt
new file mode 100644
index 0000000000..39e54f2577
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/linea/coordinator/config/v2/toml/decoders/Decoders.kt
@@ -0,0 +1,83 @@
+package linea.coordinator.config.v2.toml.decoders
+
+import com.sksamuel.hoplite.ConfigFailure
+import com.sksamuel.hoplite.ConfigResult
+import com.sksamuel.hoplite.DecoderContext
+import com.sksamuel.hoplite.Node
+import com.sksamuel.hoplite.StringNode
+import com.sksamuel.hoplite.decoder.Decoder
+import com.sksamuel.hoplite.fp.invalid
+import com.sksamuel.hoplite.fp.valid
+import linea.coordinator.config.v2.toml.SignerConfigToml
+import linea.kotlin.decodeHex
+import kotlin.reflect.KType
+import kotlin.time.Duration
+
+class TomlByteArrayHexDecoder : Decoder {
+ override fun decode(
+ node: Node,
+ type: KType,
+ context: DecoderContext,
+ ): ConfigResult {
+ return when (node) {
+ is StringNode -> runCatching {
+ node.value.decodeHex()
+ }.fold(
+ { it.valid() },
+ { ConfigFailure.DecodeError(node, type).invalid() },
+ )
+
+ else -> { ConfigFailure.DecodeError(node, type).invalid() }
+ }
+ }
+
+ override fun supports(type: KType): Boolean {
+ return type.classifier == ByteArray::class
+ }
+}
+
+class TomlKotlinDurationDecoder : Decoder {
+ override fun decode(
+ node: Node,
+ type: KType,
+ context: DecoderContext,
+ ): ConfigResult {
+ return when (node) {
+ is StringNode -> runCatching {
+ Duration.parse(node.value)
+ }.fold(
+ { it.valid() },
+ { ConfigFailure.DecodeError(node, type).invalid() },
+ )
+
+ else -> { ConfigFailure.DecodeError(node, type).invalid() }
+ }
+ }
+
+ override fun supports(type: KType): Boolean {
+ return type.classifier == Duration::class
+ }
+}
+
+class TomlSignerTypeDecoder : Decoder {
+ override fun decode(
+ node: Node,
+ type: KType,
+ context: DecoderContext,
+ ): ConfigResult {
+ return when (node) {
+ is StringNode -> runCatching {
+ SignerConfigToml.SignerType.valueOfIgnoreCase(node.value.lowercase())
+ }.fold(
+ { it.valid() },
+ { ConfigFailure.DecodeError(node, type).invalid() },
+ )
+
+ else -> { ConfigFailure.DecodeError(node, type).invalid() }
+ }
+ }
+
+ override fun supports(type: KType): Boolean {
+ return type.classifier == SignerConfigToml.SignerType::class
+ }
+}
diff --git a/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/BlockchainClientHelper.kt b/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/BlockchainClientHelper.kt
index f3983b72da..c41c1acfcc 100644
--- a/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/BlockchainClientHelper.kt
+++ b/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/BlockchainClientHelper.kt
@@ -3,12 +3,11 @@ package net.consensys.zkevm.coordinator.app
import io.vertx.core.Vertx
import io.vertx.core.http.HttpVersion
import io.vertx.ext.web.client.WebClientOptions
+import linea.kotlin.encodeHex
import linea.web3j.SmartContractErrors
import linea.web3j.transactionmanager.AsyncFriendlyTransactionManager
import net.consensys.linea.contract.l1.Web3JLineaRollupSmartContractClient
import net.consensys.linea.httprest.client.VertxHttpRestClient
-import net.consensys.zkevm.coordinator.app.config.L1Config
-import net.consensys.zkevm.coordinator.app.config.SignerConfig
import net.consensys.zkevm.coordinator.clients.smartcontract.LineaRollupSmartContractClient
import net.consensys.zkevm.ethereum.crypto.Web3SignerRestClient
import net.consensys.zkevm.ethereum.crypto.Web3SignerTxSignService
@@ -18,30 +17,29 @@ import org.web3j.protocol.Web3j
import org.web3j.service.TxSignServiceImpl
import org.web3j.tx.gas.ContractGasProvider
import org.web3j.utils.Numeric
-import java.net.URI
fun createTransactionManager(
vertx: Vertx,
- signerConfig: SignerConfig,
+ signerConfig: linea.coordinator.config.v2.SignerConfig,
client: Web3j,
): AsyncFriendlyTransactionManager {
val transactionSignService = when (signerConfig.type) {
- SignerConfig.Type.Web3j -> {
- TxSignServiceImpl(Credentials.create(signerConfig.web3j!!.privateKey.value))
+ linea.coordinator.config.v2.SignerConfig.SignerType.WEB3J -> {
+ TxSignServiceImpl(Credentials.create(signerConfig.web3j!!.privateKey.encodeHex()))
}
- SignerConfig.Type.Web3Signer -> {
+ linea.coordinator.config.v2.SignerConfig.SignerType.WEB3SIGNER -> {
val web3SignerConfig = signerConfig.web3signer!!
- val endpoint = URI(web3SignerConfig.endpoint)
+ val endpoint = web3SignerConfig.endpoint
val webClientOptions: WebClientOptions =
WebClientOptions()
.setKeepAlive(web3SignerConfig.keepAlive)
.setProtocolVersion(HttpVersion.HTTP_1_1)
- .setMaxPoolSize(web3SignerConfig.maxPoolSize.toInt())
+ .setMaxPoolSize(web3SignerConfig.maxPoolSize)
.setDefaultHost(endpoint.host)
.setDefaultPort(endpoint.port)
val httpRestClient = VertxHttpRestClient(webClientOptions, vertx)
- val signer = Web3SignerRestClient(httpRestClient, signerConfig.web3signer.publicKey)
+ val signer = Web3SignerRestClient(httpRestClient, signerConfig.web3signer.publicKey.encodeHex())
val signerAdapter = ECKeypairSignerAdapter(signer, Numeric.toBigInt(signerConfig.web3signer.publicKey))
val web3SignerCredentials = Credentials.create(signerAdapter)
Web3SignerTxSignService(web3SignerCredentials)
@@ -52,7 +50,7 @@ fun createTransactionManager(
}
fun createLineaRollupContractClient(
- l1Config: L1Config,
+ contractAddress: String,
transactionManager: AsyncFriendlyTransactionManager,
contractGasProvider: ContractGasProvider,
web3jClient: Web3j,
@@ -60,7 +58,7 @@ fun createLineaRollupContractClient(
useEthEstimateGas: Boolean,
): LineaRollupSmartContractClient {
return Web3JLineaRollupSmartContractClient.load(
- contractAddress = l1Config.zkEvmContractAddress,
+ contractAddress = contractAddress,
web3j = web3jClient,
transactionManager = transactionManager,
contractGasProvider = contractGasProvider,
diff --git a/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/CoordinatorApp.kt b/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/CoordinatorApp.kt
index 752d73c7f4..426ccbcb0d 100644
--- a/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/CoordinatorApp.kt
+++ b/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/CoordinatorApp.kt
@@ -4,15 +4,14 @@ import io.micrometer.core.instrument.MeterRegistry
import io.vertx.core.Vertx
import io.vertx.micrometer.backends.BackendRegistries
import io.vertx.sqlclient.SqlClient
-import linea.web3j.createWeb3jHttpClient
+import linea.coordinator.config.v2.CoordinatorConfig
+import linea.coordinator.config.v2.DatabaseConfig
import net.consensys.linea.async.toSafeFuture
import net.consensys.linea.jsonrpc.client.LoadBalancingJsonRpcClient
import net.consensys.linea.jsonrpc.client.VertxHttpJsonRpcClientFactory
import net.consensys.linea.metrics.micrometer.MicrometerMetricsFacade
import net.consensys.linea.vertx.loadVertxConfig
import net.consensys.zkevm.coordinator.api.Api
-import net.consensys.zkevm.coordinator.app.config.CoordinatorConfig
-import net.consensys.zkevm.coordinator.app.config.DatabaseConfig
import net.consensys.zkevm.fileio.DirectoryCleaner
import net.consensys.zkevm.persistence.dao.aggregation.AggregationsRepositoryImpl
import net.consensys.zkevm.persistence.dao.aggregation.PostgresAggregationsDao
@@ -23,17 +22,12 @@ import net.consensys.zkevm.persistence.dao.batch.persistence.RetryingBatchesPost
import net.consensys.zkevm.persistence.dao.blob.BlobsPostgresDao
import net.consensys.zkevm.persistence.dao.blob.BlobsRepositoryImpl
import net.consensys.zkevm.persistence.dao.blob.RetryingBlobsPostgresDao
-import net.consensys.zkevm.persistence.dao.feehistory.FeeHistoriesPostgresDao
-import net.consensys.zkevm.persistence.dao.feehistory.FeeHistoriesRepositoryImpl
import net.consensys.zkevm.persistence.db.Db
import net.consensys.zkevm.persistence.db.PersistenceRetryer
import org.apache.logging.log4j.Level
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
-import org.web3j.protocol.Web3j
import tech.pegasys.teku.infrastructure.async.SafeFuture
-import kotlin.time.Duration.Companion.seconds
-import kotlin.time.toKotlinDuration
class CoordinatorApp(private val configs: CoordinatorConfig) {
private val log: Logger = LogManager.getLogger(this::class.java)
@@ -59,20 +53,13 @@ class CoordinatorApp(private val configs: CoordinatorConfig) {
),
vertx,
)
- private val l2Web3jClient: Web3j = createWeb3jHttpClient(
- rpcUrl = configs.l2.rpcEndpoint.toString(),
- log = LogManager.getLogger("clients.l2.eth-api.rpc-node"),
- pollingInterval = 1.seconds,
- requestResponseLogLevel = Level.TRACE,
- failuresLogLevel = Level.DEBUG,
- )
private val persistenceRetryer = PersistenceRetryer(
vertx = vertx,
config = PersistenceRetryer.Config(
- backoffDelay = configs.persistenceRetry.backoffDelay.toKotlinDuration(),
- maxRetries = configs.persistenceRetry.maxRetries,
- timeout = configs.persistenceRetry.timeout?.toKotlinDuration(),
+ backoffDelay = configs.database.persistenceRetries.backoffDelay,
+ maxRetries = configs.database.persistenceRetries.maxRetries?.toInt(),
+ timeout = configs.database.persistenceRetries.timeout,
),
)
@@ -92,7 +79,7 @@ class CoordinatorApp(private val configs: CoordinatorConfig) {
blobsDao = RetryingBlobsPostgresDao(
delegate = BlobsPostgresDao(
config = BlobsPostgresDao.Config(
- maxBlobsToReturn = configs.blobSubmission.maxBlobsToReturn.toUInt(),
+ maxBlobsToReturn = configs.l1Submission?.blob?.dbMaxBlobsToReturn ?: 50u,
),
connection = sqlClient,
),
@@ -109,30 +96,15 @@ class CoordinatorApp(private val configs: CoordinatorConfig) {
),
)
- private val l1FeeHistoriesRepository =
- FeeHistoriesRepositoryImpl(
- FeeHistoriesRepositoryImpl.Config(
- rewardPercentiles = configs.l1DynamicGasPriceCapService.feeHistoryFetcher.rewardPercentiles,
- minBaseFeePerBlobGasToCache =
- configs.l1DynamicGasPriceCapService.gasPriceCapCalculation.historicBaseFeePerBlobGasLowerBound,
- fixedAverageRewardToCache =
- configs.l1DynamicGasPriceCapService.gasPriceCapCalculation.historicAvgRewardConstant,
- ),
- FeeHistoriesPostgresDao(
- sqlClient,
- ),
- )
-
private val l1App = L1DependentApp(
configs = configs,
vertx = vertx,
- l2Web3jClient = l2Web3jClient,
httpJsonRpcClientFactory = httpJsonRpcClientFactory,
batchesRepository = batchesRepository,
blobsRepository = blobsRepository,
aggregationsRepository = aggregationsRepository,
- l1FeeHistoriesRepository = l1FeeHistoriesRepository,
- smartContractErrors = configs.conflation.smartContractErrors,
+ sqlClient = sqlClient,
+ smartContractErrors = configs.smartContractErrors,
metricsFacade = micrometerMetricsFacade,
)
@@ -175,7 +147,6 @@ class CoordinatorApp(private val configs: CoordinatorConfig) {
return kotlin.runCatching {
SafeFuture.allOf(
l1App.stop(),
- SafeFuture.fromRunnable { l2Web3jClient.shutdown() },
api.stop().toSafeFuture(),
).thenApply {
LoadBalancingJsonRpcClient.stop()
diff --git a/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/CoordinatorAppCli.kt b/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/CoordinatorAppCli.kt
index 7fcba1284d..858cc789f2 100644
--- a/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/CoordinatorAppCli.kt
+++ b/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/CoordinatorAppCli.kt
@@ -1,6 +1,6 @@
package net.consensys.zkevm.coordinator.app
-import net.consensys.zkevm.coordinator.app.config.CoordinatorConfig
+import linea.coordinator.config.v2.CoordinatorConfig
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
import picocli.CommandLine
@@ -89,7 +89,7 @@ internal constructor(private val errorWriter: PrintWriter, private val startActi
}
}
- val configs = linea.coordinator.config.loadConfigs(
+ val configs = linea.coordinator.config.v2.toml.loadConfigs(
coordinatorConfigFiles = configFiles.map { it.toPath() },
tracesLimitsFileV2 = tracesLimitsV2File.toPath(),
smartContractErrorsFile = smartContractErrorsFile.toPath(),
diff --git a/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/CoordinatorAppMain.kt b/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/CoordinatorAppMain.kt
index 829cb98452..82caf6e924 100644
--- a/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/CoordinatorAppMain.kt
+++ b/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/CoordinatorAppMain.kt
@@ -1,6 +1,6 @@
package net.consensys.zkevm.coordinator.app
-import net.consensys.zkevm.coordinator.app.config.CoordinatorConfig
+import linea.coordinator.config.v2.CoordinatorConfig
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.core.LoggerContext
import org.apache.logging.log4j.core.config.Configurator
diff --git a/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/DisabledLongRunningService.kt b/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/DisabledLongRunningService.kt
new file mode 100644
index 0000000000..6944d1a527
--- /dev/null
+++ b/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/DisabledLongRunningService.kt
@@ -0,0 +1,15 @@
+package net.consensys.zkevm.coordinator.app
+
+import net.consensys.zkevm.LongRunningService
+import tech.pegasys.teku.infrastructure.async.SafeFuture
+import java.util.concurrent.CompletableFuture
+
+object DisabledLongRunningService : LongRunningService {
+ override fun start(): CompletableFuture {
+ return SafeFuture.completedFuture(Unit)
+ }
+
+ override fun stop(): CompletableFuture {
+ return SafeFuture.completedFuture(Unit)
+ }
+}
diff --git a/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/L1DependentApp.kt b/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/L1DependentApp.kt
index a7e84521f1..9e9952e04c 100644
--- a/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/L1DependentApp.kt
+++ b/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/L1DependentApp.kt
@@ -3,18 +3,26 @@ package net.consensys.zkevm.coordinator.app
import build.linea.clients.StateManagerClientV1
import build.linea.clients.StateManagerV1JsonRpcClient
import io.vertx.core.Vertx
+import io.vertx.sqlclient.SqlClient
import kotlinx.datetime.Clock
import linea.anchoring.MessageAnchoringApp
import linea.blob.ShnarfCalculatorVersion
import linea.contract.l1.LineaRollupSmartContractClientReadOnly
import linea.contract.l1.Web3JLineaRollupSmartContractClientReadOnly
import linea.contract.l2.Web3JL2MessageServiceSmartContractClient
+import linea.coordinator.config.toJsonRpcRetry
+import linea.coordinator.config.v2.CoordinatorConfig
+import linea.coordinator.config.v2.isDisabled
+import linea.coordinator.config.v2.isEnabled
import linea.domain.BlockNumberAndHash
+import linea.domain.RetryConfig
import linea.encoding.BlockRLPEncoder
+import linea.kotlin.toKWeiUInt
import linea.web3j.ExtendedWeb3JImpl
import linea.web3j.SmartContractErrors
import linea.web3j.Web3jBlobExtended
import linea.web3j.createWeb3jHttpClient
+import linea.web3j.createWeb3jHttpService
import linea.web3j.ethapi.createEthApiClient
import net.consensys.linea.contract.l1.GenesisStateProvider
import net.consensys.linea.ethereum.gaspricing.BoundableFeeCalculator
@@ -22,23 +30,23 @@ import net.consensys.linea.ethereum.gaspricing.FeesCalculator
import net.consensys.linea.ethereum.gaspricing.FeesFetcher
import net.consensys.linea.ethereum.gaspricing.WMAFeesCalculator
import net.consensys.linea.ethereum.gaspricing.WMAGasProvider
-import net.consensys.linea.ethereum.gaspricing.dynamiccap.FeeHistoriesRepositoryWithCache
import net.consensys.linea.ethereum.gaspricing.dynamiccap.FeeHistoryCachingService
-import net.consensys.linea.ethereum.gaspricing.dynamiccap.GasPriceCapCalculator
import net.consensys.linea.ethereum.gaspricing.dynamiccap.GasPriceCapCalculatorImpl
import net.consensys.linea.ethereum.gaspricing.dynamiccap.GasPriceCapFeeHistoryFetcher
import net.consensys.linea.ethereum.gaspricing.dynamiccap.GasPriceCapFeeHistoryFetcherImpl
import net.consensys.linea.ethereum.gaspricing.dynamiccap.GasPriceCapProviderForDataSubmission
import net.consensys.linea.ethereum.gaspricing.dynamiccap.GasPriceCapProviderForFinalization
import net.consensys.linea.ethereum.gaspricing.dynamiccap.GasPriceCapProviderImpl
+import net.consensys.linea.ethereum.gaspricing.staticcap.ExtraDataV1UpdaterImpl
import net.consensys.linea.ethereum.gaspricing.staticcap.FeeHistoryFetcherImpl
+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.metrics.LineaMetricsCategory
import net.consensys.linea.metrics.MetricsFacade
import net.consensys.linea.traces.TracesCountersV2
import net.consensys.zkevm.LongRunningService
-import net.consensys.zkevm.coordinator.app.config.CoordinatorConfig
-import net.consensys.zkevm.coordinator.app.config.Type2StateProofProviderConfig
import net.consensys.zkevm.coordinator.blockcreation.BatchesRepoBasedLastProvenBlockNumberProvider
import net.consensys.zkevm.coordinator.blockcreation.BlockCreationMonitor
import net.consensys.zkevm.coordinator.blockcreation.GethCliqueSafeBlockProvider
@@ -96,53 +104,61 @@ import net.consensys.zkevm.persistence.BatchesRepository
import net.consensys.zkevm.persistence.BlobsRepository
import net.consensys.zkevm.persistence.dao.aggregation.RecordsCleanupFinalizationHandler
import net.consensys.zkevm.persistence.dao.batch.persistence.BatchProofHandlerImpl
+import net.consensys.zkevm.persistence.dao.feehistory.FeeHistoriesPostgresDao
+import net.consensys.zkevm.persistence.dao.feehistory.FeeHistoriesRepositoryImpl
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
import org.web3j.protocol.Web3j
-import org.web3j.protocol.http.HttpService
import tech.pegasys.teku.infrastructure.async.SafeFuture
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
-import kotlin.time.toKotlinDuration
class L1DependentApp(
private val configs: CoordinatorConfig,
private val vertx: Vertx,
- private val l2Web3jClient: Web3j,
private val httpJsonRpcClientFactory: VertxHttpJsonRpcClientFactory,
private val batchesRepository: BatchesRepository,
private val blobsRepository: BlobsRepository,
private val aggregationsRepository: AggregationsRepository,
- l1FeeHistoriesRepository: FeeHistoriesRepositoryWithCache,
+ private val sqlClient: SqlClient,
private val smartContractErrors: SmartContractErrors,
private val metricsFacade: MetricsFacade,
) : LongRunningService {
private val log = LogManager.getLogger(this::class.java)
init {
- if (configs.messageAnchoring.disabled) {
- log.warn("Message anchoring service is disabled")
+ if (configs.l1Submission.isDisabled()) {
+ log.warn("L1 submission disabled for blobs and aggregations")
+ } else {
+ if (configs.l1Submission!!.blob.isDisabled()) {
+ log.warn("L1 submission disabled for blobs")
+ }
+ if (configs.l1Submission.aggregation.isDisabled()) {
+ log.warn("L1 submission disabled for aggregations")
+ }
+ }
+
+ if (configs.messageAnchoring.isDisabled()) {
+ log.warn("Message anchoring is disabled")
}
- if (configs.l2NetworkGasPricingService == null) {
- log.warn("Dynamic gas price service is disabled")
+ if (configs.l2NetworkGasPricing.isDisabled()) {
+ log.warn("L2 Network dynamic gas pricing is disabled")
}
}
- private val l2TransactionManager = createTransactionManager(
- vertx,
- configs.l2Signer,
- l2Web3jClient,
- )
-
- private val l1Web3jClient = createWeb3jHttpClient(
- rpcUrl = configs.l1.rpcEndpoint.toString(),
- log = LogManager.getLogger("clients.l1.eth-api"),
- pollingInterval = 1.seconds,
- )
- private val l1Web3jService = Web3jBlobExtended(HttpService(configs.l1.ethFeeHistoryEndpoint.toString()))
-
- private val l1ChainId = l1Web3jClient.ethChainId().send().chainId.toLong()
+ private val l1ChainId = run {
+ createEthApiClient(
+ rpcUrl = configs.l1FinalizationMonitor.l1Endpoint.toString(),
+ log = LogManager.getLogger("clients.l1.eth"),
+ vertx = vertx,
+ requestRetryConfig = RetryConfig.endlessRetry(
+ backoffDelay = 1.seconds,
+ failuresWarningThreshold = 3u,
+ ),
+ ).getChainId().get()
+ }
private val proverClientFactory = ProverClientFactory(
vertx = vertx,
@@ -150,12 +166,13 @@ class L1DependentApp(
metricsFacade = metricsFacade,
)
- private val l2ExtendedWeb3j = ExtendedWeb3JImpl(l2Web3jClient)
-
private val finalizationTransactionManager = createTransactionManager(
- vertx,
- configs.finalizationSigner,
- l1Web3jClient,
+ vertx = vertx,
+ signerConfig = configs.l1Submission!!.aggregation.signer,
+ client = createWeb3jHttpClient(
+ rpcUrl = configs.l1Submission.aggregation.l1Endpoint.toString(),
+ log = LogManager.getLogger("clients.l1.eth.finalization"),
+ ),
)
private val l1MinPriorityFeeCalculator: FeesCalculator = WMAFeesCalculator(
@@ -167,8 +184,8 @@ class L1DependentApp(
private val l1DataSubmissionPriorityFeeCalculator: FeesCalculator = BoundableFeeCalculator(
BoundableFeeCalculator.Config(
- feeUpperBound = configs.blobSubmission.priorityFeePerGasUpperBound.toDouble(),
- feeLowerBound = configs.blobSubmission.priorityFeePerGasLowerBound.toDouble(),
+ feeUpperBound = configs.l1Submission!!.blob.gas.fallback.priorityFeePerGasUpperBound.toDouble(),
+ feeLowerBound = configs.l1Submission.blob.gas.fallback.priorityFeePerGasLowerBound.toDouble(),
feeMargin = 0.0,
),
l1MinPriorityFeeCalculator,
@@ -176,92 +193,131 @@ class L1DependentApp(
private val l1FinalizationPriorityFeeCalculator: FeesCalculator = BoundableFeeCalculator(
BoundableFeeCalculator.Config(
- feeUpperBound = configs.l1.maxPriorityFeePerGasCap.toDouble() * configs.l1.gasPriceCapMultiplierForFinalization,
- feeLowerBound = 0.0,
+ feeUpperBound = configs.l1Submission!!.aggregation.gas.fallback.priorityFeePerGasUpperBound.toDouble(),
+ feeLowerBound = configs.l1Submission.aggregation.gas.fallback.priorityFeePerGasUpperBound.toDouble(),
feeMargin = 0.0,
),
l1MinPriorityFeeCalculator,
)
- private val feesFetcher: FeesFetcher = FeeHistoryFetcherImpl(
- l1Web3jClient,
- l1Web3jService,
- FeeHistoryFetcherImpl.Config(
- configs.l1.feeHistoryBlockCount.toUInt(),
- configs.l1.feeHistoryRewardPercentile,
- ),
- )
+ private val feesFetcher: FeesFetcher = run {
+ val httpService = createWeb3jHttpService(
+ configs.l1Submission!!.dynamicGasPriceCap.feeHistoryFetcher.l1Endpoint.toString(),
+ log = LogManager.getLogger("clients.l1.eth.fees-fetcher"),
+ )
+ val l1Web3jClient = createWeb3jHttpClient(httpService)
+ FeeHistoryFetcherImpl(
+ web3jClient = l1Web3jClient,
+ web3jService = Web3jBlobExtended(httpService),
+ config = FeeHistoryFetcherImpl.Config(
+ feeHistoryBlockCount = configs.l1Submission.fallbackGasPrice.feeHistoryBlockCount,
+ feeHistoryRewardPercentile = configs.l1Submission.fallbackGasPrice.feeHistoryRewardPercentile.toDouble(),
+ ),
+ )
+ }
- private val lineaRollupClient: LineaRollupSmartContractClientReadOnly = Web3JLineaRollupSmartContractClientReadOnly(
- contractAddress = configs.l1.zkEvmContractAddress,
- web3j = l1Web3jClient,
- )
+ val lineaRollupClientForFinalizationMonitor: LineaRollupSmartContractClientReadOnly =
+ Web3JLineaRollupSmartContractClientReadOnly(
+ contractAddress = configs.protocol.l1.contractAddress,
+ web3j = createWeb3jHttpClient(
+ rpcUrl = configs.l1FinalizationMonitor.l1Endpoint.toString(),
+ log = LogManager.getLogger("clients.l1.eth.finalization-monitor"),
+ ),
+ )
private val l1FinalizationMonitor = run {
FinalizationMonitorImpl(
config =
FinalizationMonitorImpl.Config(
- pollingInterval = configs.l1.finalizationPollingInterval.toKotlinDuration(),
- l1QueryBlockTag = configs.l1.l1QueryBlockTag,
+ pollingInterval = configs.l1FinalizationMonitor.l1PollingInterval,
+ l1QueryBlockTag = configs.l1FinalizationMonitor.l1QueryBlockTag,
+ ),
+ contract = lineaRollupClientForFinalizationMonitor,
+ l2Client = createWeb3jHttpClient(
+ rpcUrl = configs.l1FinalizationMonitor.l2Endpoint.toString(),
+ log = LogManager.getLogger("clients.l2.eth.finalization-monitor"),
),
- contract = lineaRollupClient,
- l2Client = l2Web3jClient,
vertx = vertx,
)
}
- private val l1FinalizationHandlerForShomeiRpc: LongRunningService = setupL1FinalizationMonitorForShomeiFrontend(
- type2StateProofProviderConfig = configs.type2StateProofProvider,
- httpJsonRpcClientFactory = httpJsonRpcClientFactory,
- lineaRollupClient = lineaRollupClient,
- l2Web3jClient = l2Web3jClient,
- vertx = vertx,
- )
+ private val l1FinalizationHandlerForShomeiRpc: LongRunningService = run {
+ val l2Web3jClient: Web3j = createWeb3jHttpClient(
+ rpcUrl = configs.l1FinalizationMonitor.l2Endpoint.toString(),
+ log = LogManager.getLogger("clients.l2.eth.shomei-frontend"),
+ )
+ setupL1FinalizationMonitorForShomeiFrontend(
+ type2StateProofProviderConfig = configs.type2StateProofProvider,
+ httpJsonRpcClientFactory = httpJsonRpcClientFactory,
+ lineaRollupClient = lineaRollupClientForFinalizationMonitor,
+ l2Web3jClient = l2Web3jClient,
+ vertx = vertx,
+ )
+ }
+
+ private val l1FeeHistoriesRepository =
+ FeeHistoriesRepositoryImpl(
+ FeeHistoriesRepositoryImpl.Config(
+ rewardPercentiles = configs.l1Submission!!.dynamicGasPriceCap.feeHistoryFetcher
+ .rewardPercentiles.map { it.toDouble() },
+ minBaseFeePerBlobGasToCache = configs.l1Submission.dynamicGasPriceCap
+ .gasPriceCapCalculation.historicBaseFeePerBlobGasLowerBound,
+ fixedAverageRewardToCache = configs.l1Submission.dynamicGasPriceCap
+ .gasPriceCapCalculation.historicAvgRewardConstant,
+ ),
+ FeeHistoriesPostgresDao(
+ sqlClient,
+ ),
+ )
private val gasPriceCapProvider =
- if (configs.l1DynamicGasPriceCapService.enabled) {
+ if (configs.l1Submission.isEnabled() && configs.l1Submission!!.dynamicGasPriceCap.isEnabled()) {
val feeHistoryPercentileWindowInBlocks =
- configs.l1DynamicGasPriceCapService.gasPriceCapCalculation.gasFeePercentileWindow
- .toKotlinDuration().inWholeSeconds.div(configs.l1.blockTime.seconds).toUInt()
+ configs.l1Submission.dynamicGasPriceCap.gasPriceCapCalculation.baseFeePerGasPercentileWindow
+ .div(configs.protocol.l1.blockTime).toUInt()
val feeHistoryPercentileWindowLeewayInBlocks =
- configs.l1DynamicGasPriceCapService.gasPriceCapCalculation.gasFeePercentileWindowLeeway
- .toKotlinDuration().inWholeSeconds.div(configs.l1.blockTime.seconds).toUInt()
+ configs.l1Submission.dynamicGasPriceCap.gasPriceCapCalculation.baseFeePerGasPercentileWindowLeeway
+ .div(configs.protocol.l1.blockTime).toUInt()
- val l1GasPriceCapCalculator: GasPriceCapCalculator = GasPriceCapCalculatorImpl()
+ val l2Web3jClient: Web3j =
+ createWeb3jHttpClient(
+ rpcUrl = configs.l1FinalizationMonitor.l2Endpoint.toString(),
+ log = LogManager.getLogger("clients.l2.eth.gascap-provider"),
+ )
GasPriceCapProviderImpl(
config = GasPriceCapProviderImpl.Config(
- enabled = configs.l1DynamicGasPriceCapService.enabled,
+ enabled = configs.l1Submission.dynamicGasPriceCap.isEnabled(),
gasFeePercentile =
- configs.l1DynamicGasPriceCapService.gasPriceCapCalculation.gasFeePercentile,
+ configs.l1Submission.dynamicGasPriceCap.gasPriceCapCalculation.baseFeePerGasPercentile.toDouble(),
gasFeePercentileWindowInBlocks = feeHistoryPercentileWindowInBlocks,
gasFeePercentileWindowLeewayInBlocks = feeHistoryPercentileWindowLeewayInBlocks,
timeOfDayMultipliers =
- configs.l1DynamicGasPriceCapService.gasPriceCapCalculation.timeOfDayMultipliers!!,
+ configs.l1Submission.dynamicGasPriceCap.gasPriceCapCalculation.timeOfTheDayMultipliers,
adjustmentConstant =
- configs.l1DynamicGasPriceCapService.gasPriceCapCalculation.adjustmentConstant,
+ configs.l1Submission.dynamicGasPriceCap.gasPriceCapCalculation.adjustmentConstant,
blobAdjustmentConstant =
- configs.l1DynamicGasPriceCapService.gasPriceCapCalculation.blobAdjustmentConstant,
+ configs.l1Submission.dynamicGasPriceCap.gasPriceCapCalculation.blobAdjustmentConstant,
finalizationTargetMaxDelay =
- configs.l1DynamicGasPriceCapService.gasPriceCapCalculation.finalizationTargetMaxDelay.toKotlinDuration(),
+ configs.l1Submission.dynamicGasPriceCap.gasPriceCapCalculation.finalizationTargetMaxDelay,
gasPriceCapsCoefficient =
- configs.l1DynamicGasPriceCapService.gasPriceCapCalculation.gasPriceCapsCheckCoefficient,
+ configs.l1Submission.dynamicGasPriceCap.gasPriceCapCalculation.gasPriceCapsCheckCoefficient,
),
- l2ExtendedWeb3JClient = l2ExtendedWeb3j,
+ l2ExtendedWeb3JClient = ExtendedWeb3JImpl(l2Web3jClient),
feeHistoriesRepository = l1FeeHistoriesRepository,
- gasPriceCapCalculator = l1GasPriceCapCalculator,
+ gasPriceCapCalculator = GasPriceCapCalculatorImpl(),
)
} else {
null
}
- private val gasPriceCapProviderForDataSubmission = if (configs.l1DynamicGasPriceCapService.enabled) {
+ private val gasPriceCapProviderForDataSubmission = if (configs.l1Submission!!.dynamicGasPriceCap.isEnabled()) {
GasPriceCapProviderForDataSubmission(
config = GasPriceCapProviderForDataSubmission.Config(
- maxPriorityFeePerGasCap = configs.l1.maxPriorityFeePerGasCap,
- maxFeePerGasCap = configs.l1.maxFeePerGasCap,
- maxFeePerBlobGasCap = configs.l1.maxFeePerBlobGasCap,
+ maxPriorityFeePerGasCap = configs.l1Submission.blob.gas.maxPriorityFeePerGasCap,
+ maxFeePerGasCap = configs.l1Submission.blob.gas.maxFeePerGasCap,
+ maxFeePerBlobGasCap = configs.l1Submission.blob.gas.maxFeePerBlobGasCap!!,
),
gasPriceCapProvider = gasPriceCapProvider!!,
metricsFacade = metricsFacade,
@@ -270,12 +326,11 @@ class L1DependentApp(
null
}
- private val gasPriceCapProviderForFinalization = if (configs.l1DynamicGasPriceCapService.enabled) {
+ private val gasPriceCapProviderForFinalization = if (configs.l1Submission!!.dynamicGasPriceCap.isEnabled()) {
GasPriceCapProviderForFinalization(
config = GasPriceCapProviderForFinalization.Config(
- maxPriorityFeePerGasCap = configs.l1.maxPriorityFeePerGasCap,
- maxFeePerGasCap = configs.l1.maxFeePerGasCap,
- gasPriceCapMultiplier = configs.l1.gasPriceCapMultiplierForFinalization,
+ maxPriorityFeePerGasCap = configs.l1Submission.aggregation.gas.maxPriorityFeePerGasCap,
+ maxFeePerGasCap = configs.l1Submission.aggregation.gas.maxFeePerGasCap,
),
gasPriceCapProvider = gasPriceCapProvider!!,
metricsFacade = metricsFacade,
@@ -294,43 +349,52 @@ class L1DependentApp(
lastFinalizedBlock,
).get()
- private fun createDeadlineConflationCalculatorRunner(): DeadlineConflationCalculatorRunner {
+ val l2Web3jClientForBlockCreation: Web3j = createWeb3jHttpClient(
+ rpcUrl = configs.conflation.l2Endpoint.toString(),
+ log = LogManager.getLogger("clients.l2.eth.conflation"),
+ )
+
+ private fun createDeadlineConflationCalculatorRunner(): DeadlineConflationCalculatorRunner? {
+ if (configs.conflation.isDisabled() || configs.conflation.conflationDeadline == null) {
+ log.info("Conflation deadline calculator is disabled")
+ return null
+ }
+
return DeadlineConflationCalculatorRunner(
- conflationDeadlineCheckInterval = configs.conflation.conflationDeadlineCheckInterval.toKotlinDuration(),
+ conflationDeadlineCheckInterval = configs.conflation.conflationDeadlineCheckInterval,
delegate = ConflationCalculatorByTimeDeadline(
config = ConflationCalculatorByTimeDeadline.Config(
- conflationDeadline = configs.conflation.conflationDeadline.toKotlinDuration(),
+ conflationDeadline = configs.conflation.conflationDeadline,
conflationDeadlineLastBlockConfirmationDelay =
- configs.conflation.conflationDeadlineLastBlockConfirmationDelay.toKotlinDuration(),
+ configs.conflation.conflationDeadlineLastBlockConfirmationDelay,
),
lastBlockNumber = lastProcessedBlockNumber,
clock = Clock.System,
latestBlockProvider = GethCliqueSafeBlockProvider(
- l2ExtendedWeb3j.web3jClient,
- GethCliqueSafeBlockProvider.Config(configs.l2.blocksToFinalization.toLong()),
+ l2Web3jClientForBlockCreation,
+ GethCliqueSafeBlockProvider.Config(blocksToFinalization = 0),
),
),
)
}
- private val deadlineConflationCalculatorRunnerOld = createDeadlineConflationCalculatorRunner()
- private val deadlineConflationCalculatorRunnerNew = createDeadlineConflationCalculatorRunner()
+ private val deadlineConflationCalculatorRunner = createDeadlineConflationCalculatorRunner()
private fun addBlocksLimitCalculatorIfDefined(calculators: MutableList) {
if (configs.conflation.blocksLimit != null) {
calculators.add(
ConflationCalculatorByBlockLimit(
- blockLimit = configs.conflation.blocksLimit.toUInt(),
+ blockLimit = configs.conflation.blocksLimit,
),
)
}
}
private fun addTargetEndBlockConflationCalculatorIfDefined(calculators: MutableList) {
- if (configs.conflation.conflationTargetEndBlockNumbers.isNotEmpty()) {
+ if (configs.conflation.proofAggregation.targetEndBlocks?.isNotEmpty() ?: false) {
calculators.add(
ConflationCalculatorByTargetBlockNumbers(
- targetEndBlockNumbers = configs.conflation.conflationTargetEndBlockNumbers,
+ targetEndBlockNumbers = configs.conflation.proofAggregation.targetEndBlocks!!.toSet(),
),
)
}
@@ -359,10 +423,9 @@ class L1DependentApp(
val logger = LogManager.getLogger(GlobalBlockConflationCalculator::class.java)
// To fail faster for JNA reasons
- val compressorVersion = configs.traces.blobCompressorVersion
val blobCompressor = GoBackedBlobCompressor.getInstance(
- compressorVersion = compressorVersion,
- dataLimit = configs.blobCompression.blobSizeLimit.toUInt(),
+ compressorVersion = configs.conflation.blobCompression.blobCompressorVersion,
+ dataLimit = configs.conflation.blobCompression.blobSizeLimit,
metricsFacade = metricsFacade,
)
@@ -372,14 +435,12 @@ class L1DependentApp(
val globalCalculator = GlobalBlockConflationCalculator(
lastBlockNumber = lastProcessedBlockNumber,
syncCalculators = createCalculatorsForBlobsAndConflation(logger, compressedBlobCalculator),
- deferredTriggerConflationCalculators = listOf(deadlineConflationCalculatorRunnerNew),
+ deferredTriggerConflationCalculators = listOfNotNull(deadlineConflationCalculatorRunner),
emptyTracesCounters = TracesCountersV2.EMPTY_TRACES_COUNT,
log = logger,
)
- val batchesLimit =
- configs.blobCompression.batchesLimit ?: (configs.proofAggregation.aggregationProofsLimit.toUInt() - 1U)
-
+ val batchesLimit = configs.conflation.blocksLimit ?: (configs.conflation.proofAggregation.proofsLimit - 1U)
GlobalBlobAwareConflationCalculator(
conflationCalculator = globalCalculator,
blobCalculator = compressedBlobCalculator,
@@ -394,7 +455,7 @@ class L1DependentApp(
rpcClientFactory = httpJsonRpcClientFactory,
endpoints = configs.stateManager.endpoints.map { it.toURI() },
maxInflightRequestsPerClient = configs.stateManager.requestLimitPerEndpoint,
- requestRetry = configs.stateManager.requestRetryConfig,
+ requestRetry = configs.stateManager.requestRetries.toJsonRpcRetry(),
zkStateManagerVersion = configs.stateManager.version,
logger = LogManager.getLogger("clients.StateManagerShomeiClient"),
)
@@ -404,33 +465,39 @@ class L1DependentApp(
// dynamic gas pricing is disabled and will act as a fallback gas provider
// if L1 dynamic gas pricing is enabled
val primaryOrFallbackGasProvider = WMAGasProvider(
- chainId = l1ChainId,
+ chainId = l1ChainId.toLong(),
feesFetcher = feesFetcher,
priorityFeeCalculator = l1DataSubmissionPriorityFeeCalculator,
config = WMAGasProvider.Config(
- gasLimit = configs.l1.gasLimit,
- maxFeePerGasCap = configs.l1.maxFeePerGasCap,
- maxPriorityFeePerGasCap = configs.l1.maxPriorityFeePerGasCap,
- maxFeePerBlobGasCap = configs.l1.maxFeePerBlobGasCap,
+ gasLimit = configs.l1Submission!!.blob.gas.gasLimit,
+ maxFeePerGasCap = configs.l1Submission.blob.gas.maxFeePerGasCap,
+ maxPriorityFeePerGasCap = configs.l1Submission.blob.gas.maxPriorityFeePerGasCap,
+ maxFeePerBlobGasCap = configs.l1Submission.blob.gas.maxFeePerBlobGasCap!!,
),
)
+ val l1Web3jClient = createWeb3jHttpClient(
+ rpcUrl = configs.l1Submission.blob.l1Endpoint.toString(),
+ log = LogManager.getLogger("clients.l1.eth.data-submission"),
+ )
createLineaRollupContractClient(
- l1Config = configs.l1,
+ contractAddress = configs.protocol.l1.contractAddress,
transactionManager = createTransactionManager(
vertx,
- configs.dataSubmissionSigner,
- l1Web3jClient,
+ signerConfig = configs.l1Submission.blob.signer,
+ client = l1Web3jClient,
),
contractGasProvider = primaryOrFallbackGasProvider,
web3jClient = l1Web3jClient,
smartContractErrors = smartContractErrors,
- useEthEstimateGas = configs.blobSubmission.useEthEstimateGas,
+ // eth_estimateGas would fail because we submit multiple blob tx
+ // and 2nd would fail with revert reason
+ useEthEstimateGas = false,
)
}
private val genesisStateProvider = GenesisStateProvider(
- configs.l1.genesisStateRootHash,
- configs.l1.genesisShnarfV6,
+ stateRootHash = configs.protocol.genesis.genesisStateRootHash,
+ shnarf = configs.protocol.genesis.genesisShnarf,
)
private val blobCompressionProofCoordinator = run {
@@ -466,7 +533,7 @@ class L1DependentApp(
zkStateClient = zkStateClient,
),
config = BlobCompressionProofCoordinator.Config(
- pollingInterval = configs.blobCompression.handlerPollingInterval.toKotlinDuration(),
+ pollingInterval = configs.conflation.blobCompression.handlerPollingInterval,
),
blobCompressionProofHandler = blobCompressionProofHandler,
metricsFacade = metricsFacade,
@@ -506,7 +573,7 @@ class L1DependentApp(
private val latestBlobSubmittedBlockNumberTracker = LatestBlobSubmittedBlockNumberTracker(0UL)
private val blobSubmissionCoordinator = run {
- if (!configs.blobSubmission.enabled) {
+ if (configs.l1Submission.isDisabled() || configs.l1Submission!!.blob.isDisabled()) {
DisabledLongRunningService
} else {
metricsFacade.createGauge(
@@ -534,10 +601,12 @@ class L1DependentApp(
BlobSubmissionCoordinator.create(
config = BlobSubmissionCoordinator.Config(
- configs.blobSubmission.dbPollingInterval.toKotlinDuration(),
- configs.blobSubmission.proofSubmissionDelay.toKotlinDuration(),
- configs.blobSubmission.maxBlobsToSubmitPerTick.toUInt(),
- configs.blobSubmission.targetBlobsToSendPerTransaction.toUInt(),
+ pollingInterval = configs.l1Submission.blob.submissionTickInterval,
+ proofSubmissionDelay = configs.l1Submission.blob.submissionDelay,
+ maxBlobsToSubmitPerTick =
+ configs.l1Submission.blob.maxSubmissionTransactionsPerTick *
+ configs.l1Submission.blob.targetBlobsPerTransaction,
+ targetBlobsToSubmitPerTx = configs.l1Submission.blob.targetBlobsPerTransaction,
),
blobsRepository = blobsRepository,
aggregationsRepository = aggregationsRepository,
@@ -572,18 +641,22 @@ class L1DependentApp(
measurementSupplier = highestAggregationTracker,
)
+ val l2Web3jClient = createWeb3jHttpClient(
+ rpcUrl = configs.conflation.l2Endpoint.toString(),
+ log = LogManager.getLogger("clients.l2.eth.conflation"),
+ )
+
ProofAggregationCoordinatorService
.create(
vertx = vertx,
- aggregationCoordinatorPollingInterval =
- configs.proofAggregation.aggregationCoordinatorPollingInterval.toKotlinDuration(),
- deadlineCheckInterval = configs.proofAggregation.deadlineCheckInterval.toKotlinDuration(),
- aggregationDeadline = configs.proofAggregation.aggregationDeadline.toKotlinDuration(),
+ aggregationCoordinatorPollingInterval = configs.conflation.proofAggregation.coordinatorPollingInterval,
+ deadlineCheckInterval = configs.conflation.proofAggregation.deadlineCheckInterval,
+ aggregationDeadline = configs.conflation.proofAggregation.deadline,
latestBlockProvider = GethCliqueSafeBlockProvider(
- l2ExtendedWeb3j.web3jClient,
- GethCliqueSafeBlockProvider.Config(configs.l2.blocksToFinalization.toLong()),
+ web3j = l2Web3jClient,
+ config = GethCliqueSafeBlockProvider.Config(0),
),
- maxProofsPerAggregation = configs.proofAggregation.aggregationProofsLimit.toUInt(),
+ maxProofsPerAggregation = configs.conflation.proofAggregation.proofsLimit,
startBlockNumberInclusive = lastConsecutiveAggregatedBlockNumber + 1u,
aggregationsRepository = aggregationsRepository,
consecutiveProvenBlobsProvider = maxBlobEndBlockNumberTracker,
@@ -596,56 +669,51 @@ class L1DependentApp(
),
vertx = vertx,
),
- l2MessageService = Web3JL2MessageServiceSmartContractClient.create(
+ l2MessageService = Web3JL2MessageServiceSmartContractClient.createReadOnly(
web3jClient = l2Web3jClient,
- contractAddress = configs.l2.messageServiceAddress,
- gasLimit = configs.l2.gasLimit,
- maxFeePerGasCap = configs.l2.maxFeePerGasCap,
- feeHistoryBlockCount = configs.l2.feeHistoryBlockCount,
- feeHistoryRewardPercentile = configs.l2.feeHistoryRewardPercentile,
- transactionManager = l2TransactionManager,
+ contractAddress = configs.protocol.l2.contractAddress,
smartContractErrors = smartContractErrors,
- smartContractDeploymentBlockNumber = configs.l2.messageServiceDeploymentBlockNumber,
+ smartContractDeploymentBlockNumber = configs.protocol.l2.contractDeploymentBlockNumber?.getNumber(),
),
- aggregationDeadlineDelay = configs.conflation.conflationDeadlineLastBlockConfirmationDelay.toKotlinDuration(),
- targetEndBlockNumbers = configs.proofAggregation.targetEndBlocks,
+ aggregationDeadlineDelay = configs.conflation.conflationDeadlineLastBlockConfirmationDelay,
+ targetEndBlockNumbers = configs.conflation.proofAggregation.targetEndBlocks ?: emptyList(),
metricsFacade = metricsFacade,
- provenAggregationEndBlockNumberConsumer = { highestAggregationTracker(it) },
- aggregationSizeMultipleOf = configs.proofAggregation.aggregationSizeMultipleOf.toUInt(),
+ provenAggregationEndBlockNumberConsumer = { aggEndBlockNumber -> highestAggregationTracker(aggEndBlockNumber) },
+ aggregationSizeMultipleOf = configs.conflation.proofAggregation.aggregationSizeMultipleOf,
)
}
private val aggregationFinalizationCoordinator = run {
- if (!configs.aggregationFinalization.enabled) {
+ if (configs.l1Submission.isDisabled() || configs.l1Submission?.aggregation.isDisabled()) {
DisabledLongRunningService
} else {
+ configs.l1Submission!!
+
+ val l1Web3jClient = createWeb3jHttpClient(
+ rpcUrl = configs.l1FinalizationMonitor.l1Endpoint.toString(),
+ log = LogManager.getLogger("clients.l1.eth.finalization"),
+ )
// The below gas provider will act as the primary gas provider if L1
// dynamic gas pricing is disabled and will act as a fallback gas provider
// if L1 dynamic gas pricing is enabled
val primaryOrFallbackGasProvider = WMAGasProvider(
- chainId = l1ChainId,
+ chainId = l1ChainId.toLong(),
feesFetcher = feesFetcher,
priorityFeeCalculator = l1FinalizationPriorityFeeCalculator,
config = WMAGasProvider.Config(
- gasLimit = configs.l1.gasLimit,
- maxFeePerGasCap = (
- configs.l1.maxFeePerGasCap.toDouble() *
- configs.l1.gasPriceCapMultiplierForFinalization
- ).toULong(),
- maxPriorityFeePerGasCap = (
- configs.l1.maxPriorityFeePerGasCap.toDouble() *
- configs.l1.gasPriceCapMultiplierForFinalization
- ).toULong(),
- maxFeePerBlobGasCap = configs.l1.maxFeePerBlobGasCap,
+ gasLimit = configs.l1Submission.aggregation.gas.gasLimit,
+ maxFeePerGasCap = configs.l1Submission.aggregation.gas.maxFeePerGasCap,
+ maxPriorityFeePerGasCap = configs.l1Submission.aggregation.gas.maxPriorityFeePerGasCap,
+ maxFeePerBlobGasCap = 0UL, // we do not submit blobs in finalization tx
),
)
val lineaSmartContractClientForFinalization: LineaRollupSmartContractClient = createLineaRollupContractClient(
- l1Config = configs.l1,
+ contractAddress = configs.protocol.l1.contractAddress,
transactionManager = finalizationTransactionManager,
contractGasProvider = primaryOrFallbackGasProvider,
web3jClient = l1Web3jClient,
smartContractErrors = smartContractErrors,
- useEthEstimateGas = configs.aggregationFinalization.useEthEstimateGas,
+ useEthEstimateGas = true,
)
val latestFinalizationSubmittedBlockNumberTracker = LatestFinalizationSubmittedBlockNumberTracker(0UL)
@@ -674,8 +742,8 @@ class L1DependentApp(
AggregationFinalizationCoordinator.create(
config = AggregationFinalizationCoordinator.Config(
- configs.aggregationFinalization.dbPollingInterval.toKotlinDuration(),
- configs.aggregationFinalization.proofSubmissionDelay.toKotlinDuration(),
+ pollingInterval = configs.l1Submission.aggregation.submissionTickInterval,
+ proofSubmissionDelay = configs.l1Submission.aggregation.submissionDelay,
),
aggregationsRepository = aggregationsRepository,
blobsRepository = blobsRepository,
@@ -693,40 +761,36 @@ class L1DependentApp(
}
private val block2BatchCoordinator = run {
- val tracesCountersLog = LogManager.getLogger("clients.TracesCounters")
val tracesCountersClient = run {
- val tracesCounterV2Config = configs.traces.countersV2
- val expectedTracesApiVersionV2 = configs.traces.expectedTracesApiVersionV2
+ val tracesCountersLog = LogManager.getLogger("clients.traces.counters")
TracesGeneratorJsonRpcClientV2(
vertx = vertx,
rpcClient = httpJsonRpcClientFactory.createWithLoadBalancing(
- endpoints = tracesCounterV2Config.endpoints.toSet(),
- maxInflightRequestsPerClient = tracesCounterV2Config.requestLimitPerEndpoint,
+ endpoints = configs.traces.counters.endpoints.toSet(),
+ maxInflightRequestsPerClient = configs.traces.counters.requestLimitPerEndpoint,
log = tracesCountersLog,
),
config = TracesGeneratorJsonRpcClientV2.Config(
- expectedTracesApiVersion = expectedTracesApiVersionV2,
+ expectedTracesApiVersion = configs.traces.expectedTracesApiVersion,
),
- retryConfig = tracesCounterV2Config.requestRetryConfig,
+ retryConfig = configs.traces.counters.requestRetries.toJsonRpcRetry(),
log = tracesCountersLog,
)
}
- val tracesConflationLog = LogManager.getLogger("clients.TracesConflation")
val tracesConflationClient = run {
- val tracesConflationConfigV2 = configs.traces.conflationV2
- val expectedTracesApiVersionV2 = configs.traces.expectedTracesApiVersionV2
+ val tracesConflationLog = LogManager.getLogger("clients.traces.conflation")
TracesGeneratorJsonRpcClientV2(
vertx = vertx,
rpcClient = httpJsonRpcClientFactory.createWithLoadBalancing(
- endpoints = tracesConflationConfigV2.endpoints.toSet(),
- maxInflightRequestsPerClient = tracesConflationConfigV2.requestLimitPerEndpoint,
+ endpoints = configs.traces.conflation.endpoints.toSet(),
+ maxInflightRequestsPerClient = configs.traces.conflation.requestLimitPerEndpoint,
log = tracesConflationLog,
),
config = TracesGeneratorJsonRpcClientV2.Config(
- expectedTracesApiVersion = expectedTracesApiVersionV2,
+ expectedTracesApiVersion = configs.traces.expectedTracesApiVersion,
),
- retryConfig = tracesConflationConfigV2.requestRetryConfig,
+ retryConfig = configs.traces.conflation.requestRetries.toJsonRpcRetry(),
log = tracesConflationLog,
)
}
@@ -750,7 +814,8 @@ class L1DependentApp(
),
)
val executionProverClient: ExecutionProverClientV2 = proverClientFactory.executionProverClient(
- tracesVersion = configs.traces.rawExecutionTracesVersion,
+ // we cannot use configs.traces.expectedTracesApiVersion because it breaks prover expected version pattern
+ tracesVersion = "2.1.0",
stateManagerVersion = configs.stateManager.version,
)
@@ -759,14 +824,12 @@ class L1DependentApp(
zkProofProductionCoordinator = ZkProofCreationCoordinatorImpl(
executionProverClient = executionProverClient,
l2EthApiClient = createEthApiClient(
- web3jClient = l2Web3jClient,
- requestRetryConfig = linea.domain.RetryConfig(
- backoffDelay = 1.seconds,
- failuresWarningThreshold = 3u,
- ),
+ rpcUrl = configs.conflation.l2Endpoint.toString(),
+ log = LogManager.getLogger("clients.l2.eth.conflation"),
+ requestRetryConfig = configs.conflation.l2RequestRetries,
vertx = vertx,
),
- messageServiceAddress = configs.l2.messageServiceAddress,
+ messageServiceAddress = configs.protocol.l2.contractAddress,
),
batchProofHandler = batchProofHandler,
vertx = vertx,
@@ -826,18 +889,18 @@ class L1DependentApp(
log.info("Resuming conflation from block={} inclusive", lastProcessedBlockNumber + 1UL)
val blockCreationMonitor = BlockCreationMonitor(
vertx = vertx,
- web3j = l2ExtendedWeb3j,
+ web3j = ExtendedWeb3JImpl(l2Web3jClientForBlockCreation),
startingBlockNumberExclusive = lastProcessedBlockNumber.toLong(),
blockCreationListener = block2BatchCoordinator,
lastProvenBlockNumberProviderAsync = lastProvenBlockNumberProvider,
config = BlockCreationMonitor.Config(
- pollingInterval = configs.l2.newBlockPollingInterval.toKotlinDuration(),
- blocksToFinalization = configs.l2.blocksToFinalization.toLong(),
- blocksFetchLimit = configs.conflation.fetchBlocksLimit.toLong(),
+ pollingInterval = configs.conflation.blocksPollingInterval,
+ blocksToFinalization = 0L,
+ blocksFetchLimit = configs.conflation.l2FetchBlocksLimit.toLong(),
// We need to add 1 to l2InclusiveBlockNumberToStopAndFlushAggregation because conflation calculator requires
// block_number = l2InclusiveBlockNumberToStopAndFlushAggregation + 1 to trigger conflation at
// l2InclusiveBlockNumberToStopAndFlushAggregation
- lastL2BlockNumberToProcessInclusive = configs.l2InclusiveBlockNumberToStopAndFlushAggregation?.let { it + 1uL },
+ lastL2BlockNumberToProcessInclusive = configs.conflation.forceStopConflationAtBlockInclusive?.inc(),
),
)
blockCreationMonitor
@@ -846,23 +909,36 @@ class L1DependentApp(
private fun lastFinalizedBlock(): SafeFuture {
val l1BasedLastFinalizedBlockProvider = L1BasedLastFinalizedBlockProvider(
vertx,
- lineaRollupClient,
- configs.conflation.consistentNumberOfBlocksOnL1ToWait.toUInt(),
+ lineaRollupSmartContractClient = lineaRollupClientForFinalizationMonitor,
+ consistentNumberOfBlocksOnL1 = configs.conflation.consistentNumberOfBlocksOnL1ToWait,
)
return l1BasedLastFinalizedBlockProvider.getLastFinalizedBlock()
}
- private val messageAnchoringApp: LongRunningService = if (configs.messageAnchoring.enabled
- ) {
+ private val messageAnchoringApp: LongRunningService = if (configs.messageAnchoring.isEnabled()) {
+ configs.messageAnchoring!!
+ val l1Web3jClient = createWeb3jHttpClient(
+ rpcUrl = configs.messageAnchoring.l1Endpoint.toString(),
+ log = LogManager.getLogger("clients.l1.eth.message-anchoring"),
+ )
+ val l2Web3jClient = createWeb3jHttpClient(
+ rpcUrl = configs.messageAnchoring.l2Endpoint.toString(),
+ log = LogManager.getLogger("clients.l2.eth.message-anchoring"),
+ )
+ val l2TransactionManager = createTransactionManager(
+ vertx = vertx,
+ signerConfig = configs.messageAnchoring.signer,
+ client = l2Web3jClient,
+ )
MessageAnchoringApp(
vertx = vertx,
config = MessageAnchoringApp.Config(
- l1RequestRetryConfig = configs.messageAnchoring.l1RequestRetryConfig,
- l1PollingInterval = configs.messageAnchoring.l1EventPollingInterval,
- l1SuccessBackoffDelay = configs.messageAnchoring.l1SuccessBackoffDelay,
- l1ContractAddress = configs.l1.zkEvmContractAddress,
- l1EventPollingTimeout = configs.messageAnchoring.l1EventPollingTimeout,
- l1EventSearchBlockChunk = configs.messageAnchoring.l1EventSearchBlockChunk,
+ l1RequestRetryConfig = configs.messageAnchoring.l1RequestRetries,
+ l1PollingInterval = configs.messageAnchoring.l1EventScrapping.pollingInterval,
+ l1SuccessBackoffDelay = configs.messageAnchoring.l1EventScrapping.ethLogsSearchSuccessBackoffDelay,
+ l1ContractAddress = configs.protocol.l1.contractAddress,
+ l1EventPollingTimeout = configs.messageAnchoring.l1EventScrapping.pollingTimeout,
+ l1EventSearchBlockChunk = configs.messageAnchoring.l1EventScrapping.ethLogsSearchBlockChunkSize,
l1HighestBlockTag = configs.messageAnchoring.l1HighestBlockTag,
l2HighestBlockTag = configs.messageAnchoring.l2HighestBlockTag,
anchoringTickInterval = configs.messageAnchoring.anchoringTickInterval,
@@ -876,14 +952,14 @@ class L1DependentApp(
),
l2MessageService = Web3JL2MessageServiceSmartContractClient.create(
web3jClient = l2Web3jClient,
- contractAddress = configs.l2.messageServiceAddress,
- gasLimit = configs.l2.gasLimit,
- maxFeePerGasCap = configs.l2.maxFeePerGasCap,
- feeHistoryBlockCount = configs.l2.feeHistoryBlockCount,
- feeHistoryRewardPercentile = configs.l2.feeHistoryRewardPercentile,
+ contractAddress = configs.protocol.l2.contractAddress,
+ gasLimit = configs.messageAnchoring.gas.gasLimit,
+ maxFeePerGasCap = configs.messageAnchoring.gas.maxFeePerGasCap,
+ feeHistoryBlockCount = configs.messageAnchoring.gas.feeHistoryBlockCount,
+ feeHistoryRewardPercentile = configs.messageAnchoring.gas.feeHistoryRewardPercentile.toDouble(),
transactionManager = l2TransactionManager,
smartContractErrors = smartContractErrors,
- smartContractDeploymentBlockNumber = configs.l2.messageServiceDeploymentBlockNumber,
+ smartContractDeploymentBlockNumber = configs.protocol.l2.contractDeploymentBlockNumber?.getNumber(),
),
)
} else {
@@ -891,55 +967,119 @@ class L1DependentApp(
}
private val l2NetworkGasPricingService: L2NetworkGasPricingService? =
- if (configs.l2NetworkGasPricingService != null) {
+ if (configs.l2NetworkGasPricing.isEnabled()) {
+ configs.l2NetworkGasPricing!!
+
+ val legacyConfig = L2NetworkGasPricingService.LegacyGasPricingCalculatorConfig(
+ transactionCostCalculatorConfig = TransactionCostCalculator.Config(
+ sampleTransactionCostMultiplier = configs.l2NetworkGasPricing.flatRateGasPricing.plainTransferCostMultiplier,
+ fixedCostWei = configs.l2NetworkGasPricing.gasPriceFixedCost,
+ compressedTxSize = configs.l2NetworkGasPricing.flatRateGasPricing.compressedTxSize.toInt(),
+ expectedGas = configs.l2NetworkGasPricing.flatRateGasPricing.expectedGas.toInt(),
+ ),
+ naiveGasPricingCalculatorConfig = null,
+ legacyGasPricingCalculatorBounds = BoundableFeeCalculator.Config(
+ feeUpperBound = configs.l2NetworkGasPricing.flatRateGasPricing.gasPriceUpperBound.toDouble(),
+ feeLowerBound = configs.l2NetworkGasPricing.flatRateGasPricing.gasPriceLowerBound.toDouble(),
+ feeMargin = 0.0,
+ ),
+ )
+
+ val config = L2NetworkGasPricingService.Config(
+ feeHistoryFetcherConfig = FeeHistoryFetcherImpl.Config(
+ feeHistoryBlockCount = configs.l2NetworkGasPricing.feeHistoryBlockCount,
+ feeHistoryRewardPercentile = configs.l2NetworkGasPricing.feeHistoryRewardPercentile.toDouble(),
+ ),
+ legacy = legacyConfig,
+ jsonRpcGasPriceUpdaterConfig = null,
+ // we do not use miner_setGasPrice RPC method, so we set it to infinite
+ jsonRpcPriceUpdateInterval = Duration.INFINITE,
+ // there no other way to work now without setting extra data into sequencer node
+ extraDataPricingPropagationEnabled = true,
+ extraDataUpdateInterval = configs.l2NetworkGasPricing.priceUpdateInterval,
+ variableFeesCalculatorConfig = VariableFeesCalculator.Config(
+ blobSubmissionExpectedExecutionGas = configs.l2NetworkGasPricing.dynamicGasPricing
+ .blobSubmissionExpectedExecutionGas.toUInt(),
+ bytesPerDataSubmission = configs.l2NetworkGasPricing.dynamicGasPricing.l1BlobGas.toUInt(),
+ expectedBlobGas = configs.l2NetworkGasPricing.dynamicGasPricing.l1BlobGas.toUInt(),
+ margin = configs.l2NetworkGasPricing.dynamicGasPricing.margin,
+ ),
+ variableFeesCalculatorBounds = BoundableFeeCalculator.Config(
+ feeUpperBound = configs.l2NetworkGasPricing.dynamicGasPricing.variableCostUpperBound.toDouble(),
+ feeLowerBound = configs.l2NetworkGasPricing.dynamicGasPricing.variableCostLowerBound.toDouble(),
+ feeMargin = 0.0,
+ ),
+ extraDataCalculatorConfig = MinerExtraDataV1CalculatorImpl.Config(
+ fixedCostInKWei = configs.l2NetworkGasPricing.gasPriceFixedCost.toKWeiUInt(),
+ ethGasPriceMultiplier = 1.0,
+ ),
+ extraDataUpdaterConfig = ExtraDataV1UpdaterImpl.Config(
+ sequencerEndpoint = configs.l2NetworkGasPricing.extraDataUpdateEndpoint,
+ retryConfig = configs.l2NetworkGasPricing.extraDataUpdateRequestRetries.toJsonRpcRetry(),
+ ),
+ )
+ val l1Web3jClient = createWeb3jHttpClient(
+ rpcUrl = configs.l2NetworkGasPricing.l1Endpoint.toString(),
+ log = LogManager.getLogger("clients.l1.eth.l2pricing"),
+ )
L2NetworkGasPricingService(
vertx = vertx,
httpJsonRpcClientFactory = httpJsonRpcClientFactory,
l1Web3jClient = l1Web3jClient,
- l1Web3jService = l1Web3jService,
- config = configs.l2NetworkGasPricingService,
+ l1Web3jService = Web3jBlobExtended(
+ createWeb3jHttpService(
+ rpcUrl = configs.l2NetworkGasPricing.l1Endpoint.toString(),
+ log = LogManager.getLogger("clients.l1.eth.l2pricing"),
+ ),
+ ),
+ config = config,
)
} else {
null
}
private val l1FeeHistoryCachingService: LongRunningService =
- if (configs.l1DynamicGasPriceCapService.enabled) {
+ if (configs.l1Submission.isEnabled() && configs.l1Submission!!.dynamicGasPriceCap.isEnabled()) {
val feeHistoryPercentileWindowInBlocks =
- configs.l1DynamicGasPriceCapService.gasPriceCapCalculation.gasFeePercentileWindow
- .toKotlinDuration().inWholeSeconds.div(configs.l1.blockTime.seconds).toUInt()
-
+ configs.l1Submission.dynamicGasPriceCap.gasPriceCapCalculation.baseFeePerGasPercentileWindow
+ .div(configs.protocol.l1.blockTime).toUInt()
val feeHistoryStoragePeriodInBlocks =
- configs.l1DynamicGasPriceCapService.feeHistoryStorage.storagePeriod
- .toKotlinDuration().inWholeSeconds.div(configs.l1.blockTime.seconds).toUInt()
+ configs.l1Submission.dynamicGasPriceCap.feeHistoryFetcher.storagePeriod
+ .div(configs.protocol.l1.blockTime).toUInt()
val l1FeeHistoryWeb3jBlobExtClient = Web3jBlobExtended(
- HttpService(
- configs.l1DynamicGasPriceCapService.feeHistoryFetcher.endpoint?.toString()
- ?: configs.l1.ethFeeHistoryEndpoint.toString(),
+ createWeb3jHttpService(
+ rpcUrl = configs.l1Submission.aggregation.l1Endpoint.toString(),
+ log = LogManager.getLogger("clients.l1.eth.feehistory-cache"),
),
)
val l1FeeHistoryFetcher: GasPriceCapFeeHistoryFetcher = GasPriceCapFeeHistoryFetcherImpl(
web3jService = l1FeeHistoryWeb3jBlobExtClient,
config = GasPriceCapFeeHistoryFetcherImpl.Config(
- maxBlockCount = configs.l1DynamicGasPriceCapService.feeHistoryFetcher.maxBlockCount,
- rewardPercentiles = configs.l1DynamicGasPriceCapService.feeHistoryFetcher.rewardPercentiles,
+ maxBlockCount = configs.l1Submission.dynamicGasPriceCap.feeHistoryFetcher.maxBlockCount,
+ rewardPercentiles = configs.l1Submission.dynamicGasPriceCap.feeHistoryFetcher.rewardPercentiles
+ .map { it.toDouble() },
),
)
+ val l1Web3jClient = createWeb3jHttpClient(
+ rpcUrl = configs.l1Submission.dynamicGasPriceCap.feeHistoryFetcher.l1Endpoint.toString(),
+ log = LogManager.getLogger("clients.l1.eth.feehistory-cache"),
+ )
+
FeeHistoryCachingService(
config = FeeHistoryCachingService.Config(
pollingInterval =
- configs.l1DynamicGasPriceCapService.feeHistoryFetcher.fetchInterval.toKotlinDuration(),
+ configs.l1Submission.dynamicGasPriceCap.feeHistoryFetcher.fetchInterval,
feeHistoryMaxBlockCount =
- configs.l1DynamicGasPriceCapService.feeHistoryFetcher.maxBlockCount,
+ configs.l1Submission.dynamicGasPriceCap.feeHistoryFetcher.maxBlockCount,
gasFeePercentile =
- configs.l1DynamicGasPriceCapService.gasPriceCapCalculation.gasFeePercentile,
+ configs.l1Submission.dynamicGasPriceCap.gasPriceCapCalculation.baseFeePerGasPercentile.toDouble(),
feeHistoryStoragePeriodInBlocks = feeHistoryStoragePeriodInBlocks,
feeHistoryWindowInBlocks = feeHistoryPercentileWindowInBlocks,
numOfBlocksBeforeLatest =
- configs.l1DynamicGasPriceCapService.feeHistoryFetcher.numOfBlocksBeforeLatest,
+ configs.l1Submission.dynamicGasPriceCap.feeHistoryFetcher.numOfBlocksBeforeLatest,
),
vertx = vertx,
web3jClient = l1Web3jClient,
@@ -994,8 +1134,7 @@ class L1DependentApp(
.thenCompose { messageAnchoringApp.start() }
.thenCompose { l2NetworkGasPricingService?.start() ?: SafeFuture.completedFuture(Unit) }
.thenCompose { l1FeeHistoryCachingService.start() }
- .thenCompose { deadlineConflationCalculatorRunnerOld.start() }
- .thenCompose { deadlineConflationCalculatorRunnerNew.start() }
+ .thenCompose { deadlineConflationCalculatorRunner?.start() ?: SafeFuture.completedFuture(Unit) }
.thenCompose { blockCreationMonitor.start() }
.thenCompose { blobCompressionProofCoordinator.start() }
.thenPeek {
@@ -1014,11 +1153,9 @@ class L1DependentApp(
l2NetworkGasPricingService?.stop() ?: SafeFuture.completedFuture(Unit),
l1FeeHistoryCachingService.stop(),
blockCreationMonitor.stop(),
- deadlineConflationCalculatorRunnerOld.stop(),
- deadlineConflationCalculatorRunnerNew.stop(),
+ deadlineConflationCalculatorRunner?.stop() ?: SafeFuture.completedFuture(Unit),
blobCompressionProofCoordinator.stop(),
)
- .thenCompose { SafeFuture.fromRunnable { l1Web3jClient.shutdown() } }
.thenApply { log.info("L1App Stopped") }
}
@@ -1071,16 +1208,13 @@ class L1DependentApp(
}
fun setupL1FinalizationMonitorForShomeiFrontend(
- type2StateProofProviderConfig: Type2StateProofProviderConfig?,
+ type2StateProofProviderConfig: linea.coordinator.config.v2.Type2StateProofManagerConfig,
httpJsonRpcClientFactory: VertxHttpJsonRpcClientFactory,
lineaRollupClient: LineaRollupSmartContractClientReadOnly,
l2Web3jClient: Web3j,
vertx: Vertx,
): LongRunningService {
- if (type2StateProofProviderConfig == null ||
- type2StateProofProviderConfig.disabled ||
- type2StateProofProviderConfig.endpoints.isEmpty()
- ) {
+ if (type2StateProofProviderConfig.isDisabled()) {
return DisabledLongRunningService
}
@@ -1090,7 +1224,7 @@ class L1DependentApp(
ShomeiClient(
vertx = vertx,
rpcClient = httpJsonRpcClientFactory.create(it, log = log),
- retryConfig = type2StateProofProviderConfig.requestRetryConfig,
+ retryConfig = type2StateProofProviderConfig.requestRetries.toJsonRpcRetry(),
log = log,
)
}
@@ -1102,7 +1236,7 @@ class L1DependentApp(
FinalizationMonitorImpl(
config =
FinalizationMonitorImpl.Config(
- pollingInterval = type2StateProofProviderConfig.l1PollingInterval.toKotlinDuration(),
+ pollingInterval = type2StateProofProviderConfig.l1PollingInterval,
l1QueryBlockTag = type2StateProofProviderConfig.l1QueryBlockTag,
),
contract = lineaRollupClient,
@@ -1120,13 +1254,3 @@ class L1DependentApp(
}
}
}
-
-private object DisabledLongRunningService : LongRunningService {
- override fun start(): CompletableFuture {
- return SafeFuture.completedFuture(Unit)
- }
-
- override fun stop(): CompletableFuture {
- return SafeFuture.completedFuture(Unit)
- }
-}
diff --git a/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/config/CoordinatorConfig.kt b/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/config/CoordinatorConfig.kt
deleted file mode 100644
index 5de66e143c..0000000000
--- a/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/config/CoordinatorConfig.kt
+++ /dev/null
@@ -1,597 +0,0 @@
-package net.consensys.zkevm.coordinator.app.config
-
-import com.sksamuel.hoplite.ConfigAlias
-import com.sksamuel.hoplite.Masked
-import linea.blob.BlobCompressorVersion
-import linea.domain.BlockParameter
-import linea.domain.assertIsValidAddress
-import linea.kotlin.assertIs32Bytes
-import linea.kotlin.decodeHex
-import linea.web3j.SmartContractErrors
-import net.consensys.linea.ethereum.gaspricing.dynamiccap.MAX_FEE_HISTORIES_STORAGE_PERIOD
-import net.consensys.linea.ethereum.gaspricing.dynamiccap.MAX_FEE_HISTORY_BLOCK_COUNT
-import net.consensys.linea.ethereum.gaspricing.dynamiccap.MAX_REWARD_PERCENTILES_SIZE
-import net.consensys.linea.ethereum.gaspricing.dynamiccap.TimeOfDayMultipliers
-import net.consensys.linea.ethereum.gaspricing.dynamiccap.getAllTimeOfDayKeys
-import net.consensys.linea.jsonrpc.client.RequestRetryConfig
-import net.consensys.linea.traces.TracesCounters
-import net.consensys.linea.traces.TracesCountersV2
-import net.consensys.linea.traces.TracingModuleV2
-import net.consensys.zkevm.coordinator.app.L2NetworkGasPricingService
-import net.consensys.zkevm.coordinator.clients.prover.ProversConfig
-import java.math.BigInteger
-import java.net.URL
-import java.time.Duration
-import kotlin.time.Duration.Companion.milliseconds
-import kotlin.time.Duration.Companion.minutes
-import kotlin.time.Duration.Companion.seconds
-import kotlin.time.toJavaDuration
-import kotlin.time.toKotlinDuration
-
-data class ApiConfig(
- val observabilityPort: UInt,
-)
-
-data class ConflationConfig(
- val consistentNumberOfBlocksOnL1ToWait: Int,
- val conflationDeadline: Duration,
- val conflationDeadlineCheckInterval: Duration,
- val conflationDeadlineLastBlockConfirmationDelay: Duration,
- val blocksLimit: Long? = null,
- private var _tracesLimitsV2: TracesCountersV2?,
- private var _smartContractErrors: SmartContractErrors?,
- val fetchBlocksLimit: Int,
- @ConfigAlias("conflation-target-end-block-numbers")
- private val _conflationTargetEndBlockNumbers: List = emptyList(),
-) {
-
- init {
- require(conflationDeadlineCheckInterval <= conflationDeadline) {
- "Clock ticker interval must be smaller than conflation deadline"
- }
- consistentNumberOfBlocksOnL1ToWait.let {
- require(it > 0) { "consistentNumberOfBlocksOnL1ToWait must be grater than 0" }
- }
- blocksLimit?.let { require(it > 0) { "blocksLimit must be greater than 0" } }
-
- _smartContractErrors = _smartContractErrors
- ?.let { it.mapKeys { it.key.lowercase() } }
- ?: emptyMap()
- }
-
- val tracesLimitsV2: TracesCounters
- get() = _tracesLimitsV2 ?: throw IllegalStateException("Traces limits not defined!")
- val smartContractErrors: SmartContractErrors = _smartContractErrors!!
-
- val conflationTargetEndBlockNumbers: Set = _conflationTargetEndBlockNumbers.map { it.toULong() }.toSet()
-}
-
-interface RetryConfig {
- val maxRetries: Int?
- val timeout: Duration?
- val backoffDelay: Duration
-}
-
-/**
- * This class is used to parse the requestRetry config from the toml file.
- * If we use UInt toml parser throws an exception because it does not support SOMETIMES UInt values: ¯\_(ツ)_/¯
- *
- * kotlin.reflect.jvm.internal.KotlinReflectionInternalError:
- * This callable does not support a default call: public constructor
- * RequestRetryConfigTomlFriendly(maxRetries: kotlin.UInt? = ...
- */
-data class RequestRetryConfigTomlFriendly(
- override val maxRetries: Int? = null,
- override val timeout: Duration? = null,
- override val backoffDelay: Duration = 1.milliseconds.toJavaDuration(),
- val failuresWarningThreshold: Int = 0,
-) : RetryConfig {
- init {
- maxRetries?.also {
- require(maxRetries >= 1) { "maxRetries must be >=1. value=$maxRetries" }
- }
- timeout?.also {
- require(timeout.toKotlinDuration() >= 1.milliseconds) { "timeout must be >= 1ms. value=$timeout" }
- }
- require(backoffDelay.toKotlinDuration() >= 1.milliseconds) {
- "backoffDelay must be >= 1ms. value=$backoffDelay"
- }
- require(failuresWarningThreshold >= 0) {
- "failuresWarningThreshold must be greater than or equal to 0. value=$failuresWarningThreshold"
- }
- }
-
- internal val asJsonRpcRetryConfig = RequestRetryConfig(
- maxRetries = maxRetries?.toUInt(),
- timeout = timeout?.toKotlinDuration(),
- backoffDelay = backoffDelay.toKotlinDuration(),
- failuresWarningThreshold = failuresWarningThreshold.toUInt(),
- )
-
- internal val asDomain: linea.domain.RetryConfig = linea.domain.RetryConfig(
- maxRetries = maxRetries?.toUInt(),
- timeout = timeout?.toKotlinDuration(),
- backoffDelay = backoffDelay.toKotlinDuration(),
- failuresWarningThreshold = failuresWarningThreshold.toUInt(),
- )
-
- companion object {
- fun endlessRetry(
- backoffDelay: Duration,
- failuresWarningThreshold: Int,
- ) = RequestRetryConfigTomlFriendly(
- maxRetries = null,
- timeout = null,
- backoffDelay = backoffDelay,
- failuresWarningThreshold = failuresWarningThreshold,
- )
- }
-}
-
-data class PersistenceRetryConfig(
- override val maxRetries: Int? = null,
- override val backoffDelay: Duration = 1.seconds.toJavaDuration(),
- override val timeout: Duration? = 10.minutes.toJavaDuration(),
-) : RetryConfig
-
-internal interface RequestRetryConfigurable {
- val requestRetry: RequestRetryConfigTomlFriendly
- val requestRetryConfig: RequestRetryConfig
- get() = requestRetry.asJsonRpcRetryConfig
-}
-
-data class BlobCompressionConfig(
- val blobSizeLimit: Int,
- @ConfigAlias("batches-limit")
- private val _batchesLimit: Int? = null,
- val handlerPollingInterval: Duration,
-) {
- init {
- _batchesLimit?.also {
- require(it > 0) { "batchesLimit=$_batchesLimit must be greater than 0" }
- }
- }
-
- val batchesLimit: UInt?
- get() = _batchesLimit?.toUInt()
-}
-
-data class AggregationConfig(
- val aggregationProofsLimit: Int,
- val aggregationDeadline: Duration,
- val aggregationCoordinatorPollingInterval: Duration,
- val deadlineCheckInterval: Duration,
- val aggregationSizeMultipleOf: Int = 1,
- @ConfigAlias("target-end-blocks")
- private val _targetEndBlocks: List = emptyList(),
-) {
- val targetEndBlocks: List = _targetEndBlocks.map { it.toULong() }
-
- init {
- require(aggregationSizeMultipleOf > 0) { "aggregationSizeMultipleOf should be greater than 0" }
- }
-}
-
-data class TracesConfig(
- val rawExecutionTracesVersion: String,
- val blobCompressorVersion: BlobCompressorVersion,
- val expectedTracesApiVersionV2: String,
- val countersV2: FunctionalityEndpoint,
- val conflationV2: FunctionalityEndpoint,
-) {
- data class FunctionalityEndpoint(
- val endpoints: List,
- val requestLimitPerEndpoint: UInt,
- override val requestRetry: RequestRetryConfigTomlFriendly,
- ) : RequestRetryConfigurable {
- init {
- require(requestLimitPerEndpoint > 0u) { "requestLimitPerEndpoint must be greater than 0" }
- }
- }
-}
-
-data class StateManagerClientConfig(
- val version: String,
- val endpoints: List,
- val requestLimitPerEndpoint: UInt,
- override val requestRetry: RequestRetryConfigTomlFriendly,
-) : RequestRetryConfigurable {
- init {
- require(requestLimitPerEndpoint > 0u) { "requestLimitPerEndpoint must be greater than 0" }
- }
-}
-
-data class BlobSubmissionConfig(
- val dbPollingInterval: Duration,
- val maxBlobsToReturn: Int,
- val proofSubmissionDelay: Duration,
- val priorityFeePerGasUpperBound: ULong,
- val priorityFeePerGasLowerBound: ULong,
- val maxBlobsToSubmitPerTick: Int = maxBlobsToReturn,
- val targetBlobsToSendPerTransaction: Int = 9,
- val useEthEstimateGas: Boolean = false,
- override var disabled: Boolean = false,
-) : FeatureToggleable {
- init {
- require(maxBlobsToReturn > 0) { "maxBlobsToReturn must be greater than 0" }
- require(maxBlobsToSubmitPerTick >= 0) { "submissionLimit must be greater or equal to 0" }
- require(targetBlobsToSendPerTransaction in 1..9) {
- "targetBlobsToSendPerTransaction must be between 1 and 9, value=$targetBlobsToSendPerTransaction"
- }
- }
-}
-
-data class AggregationFinalizationConfig(
- val dbPollingInterval: Duration,
- val maxAggregationsToFinalizePerTick: Int,
- val proofSubmissionDelay: Duration,
- val useEthEstimateGas: Boolean = false,
- override var disabled: Boolean = false,
-) : FeatureToggleable {
- init {
- require(maxAggregationsToFinalizePerTick > 0) {
- "maxAggregationsToFinalizePerIteration must be greater than 0"
- }
- }
-}
-
-data class DatabaseConfig(
- val host: String,
- val port: Int,
- val username: String,
- val password: Masked,
- val schema: String,
- val readPoolSize: Int,
- val readPipeliningLimit: Int,
- val transactionalPoolSize: Int,
-)
-
-data class L1Config(
- val zkEvmContractAddress: String,
- val rpcEndpoint: URL,
- val finalizationPollingInterval: Duration,
- @ConfigAlias("l1-query-block-tag")
- private val _l1QueryBlockTag: String = BlockParameter.Tag.FINALIZED.name,
- val gasLimit: ULong,
- val feeHistoryBlockCount: Int,
- val feeHistoryRewardPercentile: Double,
- val maxFeePerGasCap: ULong,
- val maxFeePerBlobGasCap: ULong,
- val maxPriorityFeePerGasCap: ULong,
- val gasPriceCapMultiplierForFinalization: Double,
- val earliestBlock: BigInteger,
- val sendMessageEventPollingInterval: Duration,
- val maxEventScrapingTime: Duration,
- val maxMessagesToCollect: UInt,
- val finalizedBlockTag: String,
- val blockRangeLoopLimit: UInt = 0U,
- val blockTime: Duration = Duration.parse("PT12S"),
- @ConfigAlias("eth-fee-history-endpoint") private val _ethFeeHistoryEndpoint: URL?,
- @ConfigAlias("genesis-state-root-hash") private val _genesisStateRootHash: String,
- @ConfigAlias("genesis-shnarf-v6") private val _genesisShnarfV6: String,
-) {
- val ethFeeHistoryEndpoint: URL
- get() = _ethFeeHistoryEndpoint ?: rpcEndpoint
-
- val genesisStateRootHash: ByteArray
- get() = _genesisStateRootHash.decodeHex().assertIs32Bytes("genesisStateRootHash")
- val genesisShnarfV6: ByteArray
- get() = _genesisShnarfV6.decodeHex().assertIs32Bytes("genesisShnarfV6")
-
- val l1QueryBlockTag: BlockParameter.Tag
- get() = BlockParameter.Tag.fromString(_l1QueryBlockTag)
-
- init {
- require(gasPriceCapMultiplierForFinalization > 0.0) {
- "gas price cap multiplier for finalization must be greater than 0.0." +
- " Value=$gasPriceCapMultiplierForFinalization"
- }
- // just to ensure that the tag is valid right at config parsing time
- BlockParameter.Tag.fromString(_l1QueryBlockTag)
- }
-}
-
-data class L2Config(
- val messageServiceAddress: String,
- val messageServiceDeploymentBlockNumber: ULong? = null,
- val rpcEndpoint: URL,
- val gasLimit: ULong,
- val maxFeePerGasCap: ULong,
- val feeHistoryBlockCount: UInt,
- val feeHistoryRewardPercentile: Double,
- val blocksToFinalization: UInt,
- val lastHashSearchWindow: UInt,
- val anchoringReceiptPollingInterval: Duration,
- val maxReceiptRetries: UInt,
- val newBlockPollingInterval: Duration,
-) {
- init {
- messageServiceAddress.assertIsValidAddress("messageServiceAddress")
- }
-}
-
-data class SignerConfig(
- val type: Type,
- val web3signer: Web3SignerConfig?,
- val web3j: Web3jConfig?,
-) {
- enum class Type {
- Web3j,
- Web3Signer,
- }
-
- init {
- when (type) {
- Type.Web3Signer -> {
- if (web3signer == null) throw IllegalStateException("Signer $type configuration is null.")
- }
-
- Type.Web3j -> {
- if (web3j == null) throw IllegalStateException("Signer $type configuration is null.")
- }
- }
- }
-}
-
-data class Web3jConfig(
- val privateKey: Masked,
-)
-
-data class Web3SignerConfig(
- val endpoint: String,
- val maxPoolSize: UInt,
- val keepAlive: Boolean,
- val publicKey: String,
-)
-
-interface FeatureToggleable {
- val disabled: Boolean
- val enabled: Boolean
- get() = !disabled
-}
-
-data class L1DynamicGasPriceCapServiceConfig(
- val gasPriceCapCalculation: GasPriceCapCalculation,
- val feeHistoryFetcher: FeeHistoryFetcher,
- val feeHistoryStorage: FeeHistoryStorage,
- override var disabled: Boolean = false,
-) : FeatureToggleable {
- data class GasPriceCapCalculation(
- val adjustmentConstant: UInt,
- val blobAdjustmentConstant: UInt,
- val finalizationTargetMaxDelay: Duration,
- val gasFeePercentileWindow: Duration,
- val gasFeePercentileWindowLeeway: Duration,
- val gasFeePercentile: Double,
- val gasPriceCapsCheckCoefficient: Double,
- val historicBaseFeePerBlobGasLowerBound: ULong,
- val historicAvgRewardConstant: ULong?,
- val timeOfDayMultipliers: TimeOfDayMultipliers?,
- ) {
- init {
- timeOfDayMultipliers?.also {
- val allTimeOfDayKeys = getAllTimeOfDayKeys()
- if (allTimeOfDayKeys != it.keys) {
- val missingKeys = allTimeOfDayKeys - it.keys
- val extraKeys = it.keys - allTimeOfDayKeys
- val errorMessage =
- "Invalid time of day multipliers: missing keys: " +
- "${missingKeys.joinToString(",", "[", "]")}, " +
- "unsupported keys=${extraKeys.joinToString(",", "[", "]")}"
- throw IllegalStateException(errorMessage)
- }
-
- it.entries.forEach { timeOfDayMultiplier ->
- require(timeOfDayMultiplier.value > 0.0) {
- throw IllegalStateException(
- "Each multiplier in timeOfDayMultipliers must be greater than 0.0." +
- " Key=${timeOfDayMultiplier.key} Value=${timeOfDayMultiplier.value}",
- )
- }
- }
-
- require(gasFeePercentile in 0.0..100.0) {
- "gasFeePercentile must be within 0.0 and 100.0." +
- " Value=$gasFeePercentile"
- }
-
- require(gasPriceCapsCheckCoefficient > 0.0) {
- "gasPriceCapsCheckCoefficient must be greater than 0.0." +
- " Value=$gasPriceCapsCheckCoefficient"
- }
- }
- }
- }
-
- data class FeeHistoryStorage(
- val storagePeriod: Duration,
- ) {
- init {
- require(storagePeriod <= MAX_FEE_HISTORIES_STORAGE_PERIOD.toJavaDuration()) {
- "storagePeriod must be at most $MAX_FEE_HISTORIES_STORAGE_PERIOD days"
- }
- }
- }
-
- data class FeeHistoryFetcher(
- val fetchInterval: Duration,
- val maxBlockCount: UInt,
- val rewardPercentiles: List,
- val numOfBlocksBeforeLatest: UInt = 4U,
- val endpoint: URL?,
- ) {
- init {
- require(
- maxBlockCount > 0U &&
- maxBlockCount <= MAX_FEE_HISTORY_BLOCK_COUNT,
- ) {
- "maxBlockCount must be greater than 0 and " +
- "less than or equal to $MAX_FEE_HISTORY_BLOCK_COUNT"
- }
-
- require(
- rewardPercentiles.isNotEmpty() &&
- rewardPercentiles.size <= MAX_REWARD_PERCENTILES_SIZE,
- ) {
- "rewardPercentiles must be a non-empty list with " +
- "maximum length as $MAX_REWARD_PERCENTILES_SIZE."
- }
-
- require(rewardPercentiles.zipWithNext().all { it.first < it.second }) {
- "rewardPercentiles must contain monotonically-increasing values"
- }
-
- rewardPercentiles.forEach { percentile ->
- require(percentile in 0.0..100.0) {
- "Each percentile in rewardPercentiles must be within 0.0 and 100.0." +
- " Value=$percentile"
- }
- }
- }
- }
-
- init {
- require(feeHistoryStorage.storagePeriod >= gasPriceCapCalculation.gasFeePercentileWindow) {
- "storagePeriod must be at least same length as" +
- " gasFeePercentileWindow=${gasPriceCapCalculation.gasFeePercentileWindow}." +
- " Value=${feeHistoryStorage.storagePeriod}"
- }
-
- require(
- gasPriceCapCalculation.gasFeePercentileWindow
- >= gasPriceCapCalculation.gasFeePercentileWindowLeeway,
- ) {
- "gasFeePercentileWindow must be at least same length as" +
- " gasFeePercentileWindowLeeway=${gasPriceCapCalculation.gasFeePercentileWindowLeeway}." +
- " Value=${gasPriceCapCalculation.gasFeePercentileWindow}"
- }
-
- require(feeHistoryFetcher.rewardPercentiles.contains(gasPriceCapCalculation.gasFeePercentile)) {
- "rewardPercentiles must contain the given" +
- " gasFeePercentile=${gasPriceCapCalculation.gasFeePercentile}." +
- " Value=${feeHistoryFetcher.rewardPercentiles}"
- }
- }
-}
-
-data class SmartContractErrorCodesConfig(val smartContractErrors: SmartContractErrors)
-
-data class GasPriceCapTimeOfDayMultipliersConfig(val gasPriceCapTimeOfDayMultipliers: TimeOfDayMultipliers)
-
-data class Type2StateProofProviderConfig(
- override var disabled: Boolean = false,
- val endpoints: List,
- val l1QueryBlockTag: BlockParameter.Tag = BlockParameter.Tag.LATEST,
- val l1PollingInterval: Duration = Duration.ofSeconds(12),
- override val requestRetry: RequestRetryConfigTomlFriendly,
-) : FeatureToggleable, RequestRetryConfigurable
-
-data class TracesLimitsV2ConfigFile(val tracesLimits: Map)
-
-//
-// CoordinatorConfigTomlDto class to parse from toml
-// CoordinatorConfig class with reified configs
-// separation between Toml representation and domain representation
-// otherwise it's hard to test the configuration is loaded properly
-data class CoordinatorConfigTomlDto(
- val l2InclusiveBlockNumberToStopAndFlushAggregation: ULong? = null,
- val blobCompression: BlobCompressionConfig,
- val proofAggregation: AggregationConfig,
- val traces: TracesConfig,
- val type2StateProofProvider: Type2StateProofProviderConfig,
- val l1: L1Config,
- val l2: L2Config,
- val finalizationSigner: SignerConfig,
- val dataSubmissionSigner: SignerConfig,
- val blobSubmission: BlobSubmissionConfig,
- val aggregationFinalization: AggregationFinalizationConfig,
- val database: DatabaseConfig,
- val persistenceRetry: PersistenceRetryConfig,
- val stateManager: StateManagerClientConfig,
- val conflation: ConflationConfig,
- val api: ApiConfig,
- val l2Signer: SignerConfig,
- val messageAnchoring: MessageAnchoringConfigTomlDto = MessageAnchoringConfigTomlDto(),
- val l2NetworkGasPricing: L2NetworkGasPricingTomlDto,
- val l1DynamicGasPriceCapService: L1DynamicGasPriceCapServiceConfig,
- val testL1Disabled: Boolean = false,
- val prover: ProverConfigTomlDto,
-) {
- fun reified(): CoordinatorConfig = CoordinatorConfig(
- l2InclusiveBlockNumberToStopAndFlushAggregation = l2InclusiveBlockNumberToStopAndFlushAggregation,
- blobCompression = blobCompression,
- proofAggregation = proofAggregation,
- traces = traces,
- type2StateProofProvider = type2StateProofProvider,
- l1 = l1,
- l2 = l2,
- finalizationSigner = finalizationSigner,
- dataSubmissionSigner = dataSubmissionSigner,
- blobSubmission = blobSubmission,
- aggregationFinalization = aggregationFinalization,
- database = database,
- persistenceRetry = persistenceRetry,
- stateManager = stateManager,
- conflation = conflation,
- api = api,
- l2Signer = l2Signer,
- messageAnchoring = messageAnchoring.reified(
- l1DefaultEndpoint = l1.rpcEndpoint,
- l2DefaultEndpoint = l2.rpcEndpoint,
- ),
- l2NetworkGasPricingService =
- if (testL1Disabled || l2NetworkGasPricing.disabled) null else l2NetworkGasPricing.reified(),
- l1DynamicGasPriceCapService = l1DynamicGasPriceCapService,
- testL1Disabled = testL1Disabled,
- proversConfig = prover.reified(),
- )
-}
-
-data class CoordinatorConfig(
- val l2InclusiveBlockNumberToStopAndFlushAggregation: ULong? = null,
- val blobCompression: BlobCompressionConfig,
- val proofAggregation: AggregationConfig,
- val traces: TracesConfig,
- val type2StateProofProvider: Type2StateProofProviderConfig,
- val l1: L1Config,
- val l2: L2Config,
- val finalizationSigner: SignerConfig,
- val dataSubmissionSigner: SignerConfig,
- val blobSubmission: BlobSubmissionConfig,
- val aggregationFinalization: AggregationFinalizationConfig,
- val database: DatabaseConfig,
- val persistenceRetry: PersistenceRetryConfig,
- val stateManager: StateManagerClientConfig,
- val conflation: ConflationConfig,
- val api: ApiConfig,
- val l2Signer: SignerConfig,
- val messageAnchoring: MessageAnchoringConfig,
- val l2NetworkGasPricingService: L2NetworkGasPricingService.Config?,
- val l1DynamicGasPriceCapService: L1DynamicGasPriceCapServiceConfig,
- val testL1Disabled: Boolean = false,
- val proversConfig: ProversConfig,
-) {
- init {
- if (l2InclusiveBlockNumberToStopAndFlushAggregation != null) {
- require(proofAggregation.targetEndBlocks.contains(l2InclusiveBlockNumberToStopAndFlushAggregation)) {
- "proofAggregation.targetEndBlocks should contain the l2InclusiveBlockNumberToStopAndFlushAggregation"
- }
- require(conflation.conflationTargetEndBlockNumbers.contains(l2InclusiveBlockNumberToStopAndFlushAggregation)) {
- "conflation.conflationTargetEndBlockNumbers should contain the l2InclusiveBlockNumberToStopAndFlushAggregation"
- }
- }
-
- require(
- blobCompression.batchesLimit == null ||
- blobCompression.batchesLimit!! < proofAggregation.aggregationProofsLimit.toUInt(),
- ) {
- "[blob-compression].batchesLimit=${blobCompression.batchesLimit} must be less than " +
- "[proof-aggregation].aggregationProofsLimit=${proofAggregation.aggregationProofsLimit}"
- }
-
- if (testL1Disabled) {
- messageAnchoring.disabled = true
- l1DynamicGasPriceCapService.disabled = true
- }
- }
-}
diff --git a/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/config/L2NetworkGasPricingConfig.kt b/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/config/L2NetworkGasPricingConfig.kt
deleted file mode 100644
index ae27dd1077..0000000000
--- a/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/config/L2NetworkGasPricingConfig.kt
+++ /dev/null
@@ -1,194 +0,0 @@
-package net.consensys.zkevm.coordinator.app.config
-
-import com.sksamuel.hoplite.ConfigAlias
-import linea.kotlin.toKWeiUInt
-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.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,
- val besuGasPriceUpdateRecipients: List,
-) : 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 == true || 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,
- ),
- )
- }
- }
- val gasPriceUpdaterConfig = if (jsonRpcPricingPropagation?.enabled == true) {
- GasPriceUpdaterImpl.Config(
- gethEndpoints = jsonRpcPricingPropagation.gethGasPriceUpdateRecipients,
- besuEndPoints = jsonRpcPricingPropagation.besuGasPriceUpdateRecipients,
- retryConfig = requestRetryConfig,
- )
- } else {
- null
- }
- return L2NetworkGasPricingService.Config(
- feeHistoryFetcherConfig = FeeHistoryFetcherImpl.Config(
- feeHistoryBlockCount = feeHistoryBlockCount.toUInt(),
- feeHistoryRewardPercentile = feeHistoryRewardPercentile,
- ),
- legacy = legacyGasPricingConfig,
- jsonRpcGasPriceUpdaterConfig = gasPriceUpdaterConfig,
- 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,
- ),
- )
- }
-}
diff --git a/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/config/MessageAnchoringConfig.kt b/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/config/MessageAnchoringConfig.kt
deleted file mode 100644
index 3cb8ad1bed..0000000000
--- a/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/config/MessageAnchoringConfig.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-package net.consensys.zkevm.coordinator.app.config
-
-import linea.domain.BlockParameter
-import java.net.URL
-import java.time.Duration
-import kotlin.time.Duration.Companion.milliseconds
-import kotlin.time.Duration.Companion.seconds
-import kotlin.time.toJavaDuration
-import kotlin.time.toKotlinDuration
-
-data class MessageAnchoringConfigTomlDto(
- var disabled: Boolean = false,
- val l1Endpoint: URL? = null, // shall default to L1 endpoint
- val l1HighestBlockTag: BlockParameter = BlockParameter.Tag.FINALIZED,
- val l1RequestRetries: RequestRetryConfigTomlFriendly = RequestRetryConfigTomlFriendly.endlessRetry(
- backoffDelay = 1.seconds.toJavaDuration(),
- failuresWarningThreshold = 3,
- ),
- val l1EventPollingInterval: Duration = 12.seconds.toJavaDuration(),
- val l1EventPollingTimeout: Duration = 6.seconds.toJavaDuration(),
- val l1SuccessBackoffDelay: Duration = 1.milliseconds.toJavaDuration(), // is configurable mostly for testing purposes
- val l1EventSearchBlockChunk: Int = 1000,
- val l2Endpoint: URL? = null,
- val l2HighestBlockTag: BlockParameter = BlockParameter.Tag.LATEST,
- val l2RequestRetries: RequestRetryConfigTomlFriendly = RequestRetryConfigTomlFriendly.endlessRetry(
- backoffDelay = 1.seconds.toJavaDuration(),
- failuresWarningThreshold = 3,
- ),
- val anchoringTickInterval: Duration = 2.seconds.toJavaDuration(),
- val messageQueueCapacity: Int = 10_000,
- val maxMessagesToAnchorPerL2Transaction: Int = 100,
-) {
- init {
- require(messageQueueCapacity > 0) {
- "messageQueueCapacity must be greater than 0"
- }
- require(maxMessagesToAnchorPerL2Transaction >= 1) {
- "maxMessagesToAnchorPerL2Transaction=$maxMessagesToAnchorPerL2Transaction be equal or greater than 1"
- }
- require(l1EventPollingInterval.toMillis() >= 1) {
- "l1EventPollingInterval=$l1EventPollingInterval must be equal or greater than 1ms"
- }
- require(l1EventPollingTimeout.toMillis() >= 1) {
- "l1EventPollingTimeout=$l1EventPollingTimeout must be equal or greater than 1ms"
- }
- require(l1SuccessBackoffDelay.toMillis() >= 1) {
- "l1SuccessBackoffDelay=$l1SuccessBackoffDelay must be equal or greater than 1ms"
- }
- require(l1EventSearchBlockChunk >= 1) {
- "l1EventSearchBlockChunk=$l1EventSearchBlockChunk must be equal or greater than 1"
- }
- require(anchoringTickInterval.toMillis() >= 1) {
- "anchoringTickInterval must be equal or greater than 1ms"
- }
- }
-
- fun reified(
- l1DefaultEndpoint: URL,
- l2DefaultEndpoint: URL,
- ): MessageAnchoringConfig {
- return MessageAnchoringConfig(
- disabled = disabled,
- l1Endpoint = l1Endpoint ?: l1DefaultEndpoint,
- l2Endpoint = l2Endpoint ?: l2DefaultEndpoint,
- l1HighestBlockTag = l1HighestBlockTag,
- l2HighestBlockTag = l2HighestBlockTag,
- l1RequestRetryConfig = l1RequestRetries.asDomain,
- l2RequestRetryConfig = l2RequestRetries.asDomain,
- l1EventPollingInterval = l1EventPollingInterval.toKotlinDuration(),
- l1EventPollingTimeout = l1EventPollingTimeout.toKotlinDuration(),
- l1SuccessBackoffDelay = l1SuccessBackoffDelay.toKotlinDuration(),
- l1EventSearchBlockChunk = l1EventSearchBlockChunk.toUInt(),
- anchoringTickInterval = anchoringTickInterval.toKotlinDuration(),
- messageQueueCapacity = messageQueueCapacity.toUInt(),
- maxMessagesToAnchorPerL2Transaction = maxMessagesToAnchorPerL2Transaction.toUInt(),
- )
- }
-}
-
-data class MessageAnchoringConfig(
- override var disabled: Boolean,
- val l1Endpoint: URL,
- val l2Endpoint: URL,
- val l1HighestBlockTag: BlockParameter,
- val l2HighestBlockTag: BlockParameter,
- val l1RequestRetryConfig: linea.domain.RetryConfig,
- val l2RequestRetryConfig: linea.domain.RetryConfig,
- val l1EventPollingInterval: kotlin.time.Duration,
- val l1EventPollingTimeout: kotlin.time.Duration,
- val l1SuccessBackoffDelay: kotlin.time.Duration,
- val l1EventSearchBlockChunk: UInt,
- val anchoringTickInterval: kotlin.time.Duration,
- val messageQueueCapacity: UInt,
- val maxMessagesToAnchorPerL2Transaction: UInt,
-) : FeatureToggleable
diff --git a/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/config/ProverConfig.kt b/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/config/ProverConfig.kt
deleted file mode 100644
index 128e17e02f..0000000000
--- a/coordinator/app/src/main/kotlin/net/consensys/zkevm/coordinator/app/config/ProverConfig.kt
+++ /dev/null
@@ -1,91 +0,0 @@
-package net.consensys.zkevm.coordinator.app.config
-
-import net.consensys.zkevm.coordinator.clients.prover.FileBasedProverConfig
-import net.consensys.zkevm.coordinator.clients.prover.ProverConfig
-import net.consensys.zkevm.coordinator.clients.prover.ProversConfig
-import java.nio.file.Path
-import java.time.Duration
-import kotlin.time.Duration.Companion.hours
-import kotlin.time.Duration.Companion.seconds
-import kotlin.time.toJavaDuration
-import kotlin.time.toKotlinDuration
-
-data class ProverConfigTomlDto(
- val switchBlockNumberInclusive: Long? = null,
- var fsInprogressRequestWritingSuffix: String? = null,
- var fsInprogressProvingSuffixPattern: String? = null,
- var fsPollingInterval: Duration? = null,
- var fsPollingTimeout: Duration? = null,
- val execution: FileSystemTomlDto,
- val blobCompression: FileSystemTomlDto,
- val proofAggregation: FileSystemTomlDto,
- val new: ProverConfigTomlDto? = null,
-) {
- private fun asProverConfig(): ProverConfig {
- return ProverConfig(
- execution = execution.toDomain(),
- blobCompression = blobCompression.toDomain(),
- proofAggregation = proofAggregation.toDomain(),
- )
- }
-
- fun reified(): ProversConfig {
- fsInprogressRequestWritingSuffix = fsInprogressRequestWritingSuffix ?: ".inprogress_coordinator_writing"
- fsInprogressProvingSuffixPattern = fsInprogressProvingSuffixPattern ?: "\\.inprogress\\.prover.*"
- fsPollingInterval = fsPollingInterval ?: 1.seconds.toJavaDuration()
- fsPollingTimeout = fsPollingTimeout ?: 3.hours.toJavaDuration()
- execution.reifyWithRootDefaults(this)
- blobCompression.reifyWithRootDefaults(this)
- proofAggregation.reifyWithRootDefaults(this)
-
- if (new != null) {
- if (new.switchBlockNumberInclusive == null) {
- throw IllegalArgumentException("switchBlockNumberInclusive must be set when new prover is configured")
- }
- new.fsInprogressProvingSuffixPattern = new.fsInprogressProvingSuffixPattern
- ?: fsInprogressProvingSuffixPattern
- new.fsInprogressRequestWritingSuffix = new.fsInprogressRequestWritingSuffix
- ?: fsInprogressRequestWritingSuffix
- new.fsPollingInterval = new.fsPollingInterval ?: fsPollingInterval
- new.fsPollingTimeout = new.fsPollingTimeout ?: fsPollingTimeout
- new.execution.reifyWithRootDefaults(new)
- new.blobCompression.reifyWithRootDefaults(new)
- new.proofAggregation.reifyWithRootDefaults(new)
- }
-
- return ProversConfig(
- proverA = this.asProverConfig(),
- switchBlockNumberInclusive = new?.switchBlockNumberInclusive?.toULong(),
- proverB = new?.asProverConfig(),
- )
- }
-}
-
-data class FileSystemTomlDto(
- internal val fsRequestsDirectory: Path,
- internal val fsResponsesDirectory: Path,
- internal var fsInprogressRequestWritingSuffix: String?,
- internal var fsInprogressProvingSuffixPattern: String?,
- internal var fsPollingInterval: Duration?,
- internal var fsPollingTimeout: Duration?,
-) {
- internal fun reifyWithRootDefaults(rootConfig: ProverConfigTomlDto) {
- fsInprogressRequestWritingSuffix = fsInprogressRequestWritingSuffix
- ?: rootConfig.fsInprogressRequestWritingSuffix
- fsInprogressProvingSuffixPattern = fsInprogressProvingSuffixPattern
- ?: rootConfig.fsInprogressProvingSuffixPattern
- fsPollingInterval = fsPollingInterval ?: rootConfig.fsPollingInterval
- fsPollingTimeout = fsPollingTimeout ?: rootConfig.fsPollingTimeout
- }
-
- fun toDomain(): FileBasedProverConfig {
- return FileBasedProverConfig(
- requestsDirectory = fsRequestsDirectory,
- responsesDirectory = fsResponsesDirectory,
- inprogressRequestWritingSuffix = fsInprogressRequestWritingSuffix!!,
- inprogressProvingSuffixPattern = fsInprogressProvingSuffixPattern!!,
- pollingInterval = fsPollingInterval!!.toKotlinDuration(),
- pollingTimeout = fsPollingTimeout!!.toKotlinDuration(),
- )
- }
-}
diff --git a/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/ApiConfigParsingTest.kt b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/ApiConfigParsingTest.kt
new file mode 100644
index 0000000000..b29dc66bb1
--- /dev/null
+++ b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/ApiConfigParsingTest.kt
@@ -0,0 +1,39 @@
+package linea.coordinator.config.v2
+
+import linea.coordinator.config.v2.toml.ApiConfigToml
+import linea.coordinator.config.v2.toml.parseConfig
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+
+class ApiConfigParsingTest {
+ companion object {
+ val toml = """
+ [api]
+ observability-port = 9546
+ """.trimIndent()
+
+ val config = ApiConfigToml(
+ observabilityPort = 9546u,
+ )
+
+ val tomlMinimal = ""
+
+ val configMinimal = ApiConfigToml(
+ observabilityPort = 9545u,
+ )
+ }
+
+ data class WrapperConfig(
+ val api: ApiConfigToml = ApiConfigToml(),
+ )
+
+ @Test
+ fun `should parse api full config`() {
+ assertThat(parseConfig(toml).api).isEqualTo(config)
+ }
+
+ @Test
+ fun `should parse api minimal config`() {
+ assertThat(parseConfig(tomlMinimal).api).isEqualTo(configMinimal)
+ }
+}
diff --git a/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/ConflationParsingTest.kt b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/ConflationParsingTest.kt
new file mode 100644
index 0000000000..9f6ee34bdd
--- /dev/null
+++ b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/ConflationParsingTest.kt
@@ -0,0 +1,107 @@
+package linea.coordinator.config.v2
+
+import linea.coordinator.config.v2.toml.ConflationToml
+import linea.coordinator.config.v2.toml.parseConfig
+import linea.kotlin.toURL
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+import kotlin.time.Duration.Companion.seconds
+
+class ConflationParsingTest {
+ companion object {
+ val toml = """
+ [conflation]
+ disabled = true
+ blocks-limit = 2
+ conflation-deadline = "PT6S"
+ conflation-deadline-check-interval = "PT3S"
+ conflation-deadline-last-block-confirmation-delay = "PT2S" # recommended: at least 2 * blockInterval
+ l2-fetch-blocks-limit = 4_000
+ l2-endpoint = "http://l2-node-1:8545"
+ l2-logs-endpoint = "http://l2-node-2:8545"
+ consistent-number-of-blocks-on-l1-to-wait = 1
+
+ [conflation.blob-compression]
+ blob-size-limit = 102_400 # 100KB
+ handler-polling-interval = "PT1S"
+ # default batches limit is aggregation-proofs-limit -1
+ # batches-limit must be less than or equal to aggregation-proofs-limit-1
+ batches-limit = 1
+
+ [conflation.proof-aggregation]
+ proofs-limit = 3
+ deadline = "PT1M"
+ coordinator-polling-interval = "PT2S"
+ deadline-check-interval = "PT8S"
+ target-end-blocks = [10, 20, 30_000]
+ """.trimIndent()
+ val config = ConflationToml(
+ disabled = true,
+ blocksLimit = 2u,
+ conflationDeadline = 6.seconds,
+ conflationDeadlineCheckInterval = 3.seconds,
+ conflationDeadlineLastBlockConfirmationDelay = 2.seconds,
+ l2FetchBlocksLimit = 4000u,
+ l2Endpoint = "http://l2-node-1:8545".toURL(),
+ l2LogsEndpoint = "http://l2-node-2:8545".toURL(),
+ consistentNumberOfBlocksOnL1ToWait = 1u,
+ blobCompression = ConflationToml.BlobCompressionToml(
+ blobSizeLimit = 102_400U,
+ handlerPollingInterval = 1.seconds,
+ batchesLimit = 1u,
+ ),
+ proofAggregation = ConflationToml.ProofAggregationToml(
+ proofsLimit = 3u,
+ deadline = 60.seconds,
+ coordinatorPollingInterval = 2.seconds,
+ deadlineCheckInterval = 8.seconds,
+ targetEndBlocks = listOf(10uL, 20uL, 30_000uL),
+ ),
+ )
+
+ val tomlMinimal = """
+ # all fields are optional, defaults will be used if not specified
+ """.trimIndent()
+ val configMinimal = ConflationToml(
+ disabled = false,
+ blocksLimit = null,
+ conflationDeadline = null,
+ l2FetchBlocksLimit = null,
+ l2Endpoint = null,
+ l2LogsEndpoint = null,
+ consistentNumberOfBlocksOnL1ToWait = 32u,
+ blobCompression = ConflationToml.BlobCompressionToml(
+ blobSizeLimit = 102_400U,
+ handlerPollingInterval = 1.seconds,
+ batchesLimit = null,
+ ),
+ proofAggregation = ConflationToml.ProofAggregationToml(
+ proofsLimit = 300u,
+ deadline = null,
+ deadlineCheckInterval = 30.seconds,
+ coordinatorPollingInterval = 3.seconds,
+ targetEndBlocks = null,
+ ),
+ )
+ }
+
+ data class WrapperConfig(
+ val conflation: ConflationToml = ConflationToml(),
+ )
+
+ @Test
+ fun `should parse conflation toml configs - full`() {
+ assertThat(
+ parseConfig(toml).conflation,
+ )
+ .isEqualTo(config)
+ }
+
+ @Test
+ fun `should parse conflation toml configs - minimal`() {
+ assertThat(
+ parseConfig(tomlMinimal).conflation,
+ )
+ .isEqualTo(configMinimal)
+ }
+}
diff --git a/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/CoordinatorConfigTest.kt b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/CoordinatorConfigTest.kt
new file mode 100644
index 0000000000..b17e19d1d4
--- /dev/null
+++ b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/CoordinatorConfigTest.kt
@@ -0,0 +1,79 @@
+package linea.coordinator.config.v2
+
+import linea.coordinator.config.v2.toml.CoordinatorConfigFileToml
+import linea.coordinator.config.v2.toml.parseConfig
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+
+class CoordinatorConfigTest {
+ @Test
+ fun `should parse full configs`() {
+ val toml = """
+ ${DefaultsParsingTest.toml}
+ ${ProtocolParsingTest.toml}
+ ${ConflationParsingTest.toml}
+ ${ProverParsingTest.toml}
+ ${TracesParsingTest.toml}
+ ${StateManagerParsingTest.toml}
+ ${Type2StateProofProviderParsingTest.toml}
+ ${L1FinalizationMonitorParsingTest.toml}
+ ${L1SubmissionConfigParsingTest.toml}
+ ${MessageAnchoringConfigParsingTest.toml}
+ ${L2NetWorkingGasPricingConfigParsingTest.toml}
+ ${DataBaseConfigParsingTest.toml}
+ ${ApiConfigParsingTest.toml}
+ """.trimIndent()
+ val config = CoordinatorConfigFileToml(
+ defaults = DefaultsParsingTest.config,
+ protocol = ProtocolParsingTest.config,
+ conflation = ConflationParsingTest.config,
+ prover = ProverParsingTest.config,
+ traces = TracesParsingTest.config,
+ stateManager = StateManagerParsingTest.config,
+ type2StateProofProvider = Type2StateProofProviderParsingTest.config,
+ l1FinalizationMonitor = L1FinalizationMonitorParsingTest.config,
+ l1Submission = L1SubmissionConfigParsingTest.config,
+ messageAnchoring = MessageAnchoringConfigParsingTest.config,
+ l2NetworkGasPricing = L2NetWorkingGasPricingConfigParsingTest.config,
+ database = DataBaseConfigParsingTest.config,
+ api = ApiConfigParsingTest.config,
+ )
+ assertThat(parseConfig(toml)).isEqualTo(config)
+ }
+
+ @Test
+ fun `should parse minimal configs`() {
+ val toml = """
+ ${DefaultsParsingTest.tomlMinimal}
+ ${ProtocolParsingTest.tomlMinimal}
+ ${ConflationParsingTest.tomlMinimal}
+ ${ProverParsingTest.tomlMinimal}
+ ${TracesParsingTest.tomlMinimal}
+ ${StateManagerParsingTest.tomlMinimal}
+ ${Type2StateProofProviderParsingTest.tomlMinimal}
+ ${L1FinalizationMonitorParsingTest.tomlMinimal}
+ ${L1SubmissionConfigParsingTest.tomlMinimal}
+ ${MessageAnchoringConfigParsingTest.tomlMinimal}
+ ${L2NetWorkingGasPricingConfigParsingTest.tomlMinimal}
+ ${DataBaseConfigParsingTest.tomlMinimal}
+ ${ApiConfigParsingTest.tomlMinimal}
+ """.trimIndent()
+ val config = CoordinatorConfigFileToml(
+ defaults = DefaultsParsingTest.configMinimal,
+ protocol = ProtocolParsingTest.configMinimal,
+ conflation = ConflationParsingTest.configMinimal,
+ prover = ProverParsingTest.configMinimal,
+ traces = TracesParsingTest.configMinimal,
+ stateManager = StateManagerParsingTest.configMinimal,
+ type2StateProofProvider = Type2StateProofProviderParsingTest.configMinimal,
+ l1FinalizationMonitor = L1FinalizationMonitorParsingTest.configMinimal,
+ l1Submission = L1SubmissionConfigParsingTest.configMinimal,
+ messageAnchoring = MessageAnchoringConfigParsingTest.configMinimal,
+ l2NetworkGasPricing = L2NetWorkingGasPricingConfigParsingTest.configMinimal,
+ database = DataBaseConfigParsingTest.configMinimal,
+ api = ApiConfigParsingTest.configMinimal,
+ )
+
+ assertThat(parseConfig(toml)).isEqualTo(config)
+ }
+}
diff --git a/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/DataBaseConfigParsingTest.kt b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/DataBaseConfigParsingTest.kt
new file mode 100644
index 0000000000..3ebb323ce4
--- /dev/null
+++ b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/DataBaseConfigParsingTest.kt
@@ -0,0 +1,86 @@
+package linea.coordinator.config.v2
+
+import com.sksamuel.hoplite.Masked
+import linea.coordinator.config.v2.toml.DatabaseToml
+import linea.coordinator.config.v2.toml.RequestRetriesToml
+import linea.coordinator.config.v2.toml.parseConfig
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+import kotlin.time.Duration.Companion.minutes
+import kotlin.time.Duration.Companion.seconds
+
+class DataBaseConfigParsingTest {
+ companion object {
+ val toml = """
+ [database]
+ hostname = "localhost"
+ port = "5432"
+ username = "someuser"
+ password = "somepassword"
+ schema = "linea_coordinator"
+ read_pool_size = 10
+ read_pipelining_limit = 11
+ transactional_pool_size = 12
+ [database.persistence-retries]
+ max-retries = 3
+ backoff-delay = "PT1S"
+ timeout = "PT40S"
+ failures-warning-threshold = 2
+ """.trimIndent()
+
+ val config = DatabaseToml(
+ hostname = "localhost",
+ username = "someuser",
+ password = Masked("somepassword"),
+ schema = "linea_coordinator",
+ readPoolSize = 10,
+ readPipeliningLimit = 11,
+ transactionalPoolSize = 12,
+ port = 5432u,
+ persistenceRetries = RequestRetriesToml(
+ maxRetries = 3u,
+ backoffDelay = 1.seconds,
+ timeout = 40.seconds,
+ failuresWarningThreshold = 2u,
+ ),
+ )
+
+ val tomlMinimal = """
+ [database]
+ hostname = "localhost"
+ username = "someuser"
+ password = "somepassword"
+ """.trimIndent()
+
+ val configMinimal = DatabaseToml(
+ hostname = "localhost",
+ username = "someuser",
+ password = Masked("somepassword"),
+ schema = "linea_coordinator",
+ readPoolSize = 10,
+ readPipeliningLimit = 10,
+ transactionalPoolSize = 10,
+ port = 5432u,
+ persistenceRetries = RequestRetriesToml(
+ maxRetries = null,
+ backoffDelay = 1.seconds,
+ timeout = 10.minutes,
+ failuresWarningThreshold = 3u,
+ ),
+ )
+ }
+
+ data class WrapperConfig(
+ val database: DatabaseToml,
+ )
+
+ @Test
+ fun `should parse database full config`() {
+ assertThat(parseConfig(toml).database).isEqualTo(config)
+ }
+
+ @Test
+ fun `should parse database minimal config`() {
+ assertThat(parseConfig(tomlMinimal).database).isEqualTo(configMinimal)
+ }
+}
diff --git a/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/DefaultsParsingTest.kt b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/DefaultsParsingTest.kt
new file mode 100644
index 0000000000..ed964fbf56
--- /dev/null
+++ b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/DefaultsParsingTest.kt
@@ -0,0 +1,69 @@
+package linea.coordinator.config.v2
+
+import linea.coordinator.config.v2.toml.DefaultsToml
+import linea.coordinator.config.v2.toml.RequestRetriesToml
+import linea.coordinator.config.v2.toml.parseConfig
+import linea.kotlin.toURL
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+import kotlin.time.Duration.Companion.seconds
+
+class DefaultsParsingTest {
+ companion object {
+ val toml = """
+ [defaults]
+ l1-endpoint = "http://l1-el-node:8545"
+ l2-endpoint = "http://sequencer:8545"
+ [defaults.l1-request-retries]
+ backoff-delay = "PT2S"
+ failures-warning-threshold = 2
+ timeout = "PT20S"
+ [defaults.l2-request-retries]
+ backoff-delay = "PT3S"
+ failures-warning-threshold = 3
+ timeout = "PT30S"
+ """.trimIndent()
+
+ val config = DefaultsToml(
+ l1Endpoint = "http://l1-el-node:8545".toURL(),
+ l2Endpoint = "http://sequencer:8545".toURL(),
+ l1RequestRetries = RequestRetriesToml(
+ backoffDelay = 2.seconds,
+ failuresWarningThreshold = 2u,
+ timeout = 20.seconds,
+ ),
+ l2RequestRetries = RequestRetriesToml(
+ backoffDelay = 3.seconds,
+ failuresWarningThreshold = 3u,
+ timeout = 30.seconds,
+ ),
+ )
+
+ val tomlMinimal = """
+ """.trimIndent()
+
+ val configMinimal = DefaultsToml(
+ l1Endpoint = null,
+ l2Endpoint = null,
+ l1RequestRetries = RequestRetriesToml.endlessRetry(
+ backoffDelay = 1.seconds,
+ failuresWarningThreshold = 3u,
+ ),
+ l2RequestRetries = RequestRetriesToml.endlessRetry(
+ backoffDelay = 1.seconds,
+ failuresWarningThreshold = 3u,
+ ),
+ )
+ }
+ internal data class WrapperConfig(val defaults: DefaultsToml = DefaultsToml())
+
+ @Test
+ fun `should parse defaults full configs`() {
+ assertThat(parseConfig(toml).defaults).isEqualTo(config)
+ }
+
+ @Test
+ fun `should parse defaults minimal configs`() {
+ assertThat(parseConfig(tomlMinimal).defaults).isEqualTo(configMinimal)
+ }
+}
diff --git a/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/L1FinalizationMonitorParsingTest.kt b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/L1FinalizationMonitorParsingTest.kt
new file mode 100644
index 0000000000..581af1020c
--- /dev/null
+++ b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/L1FinalizationMonitorParsingTest.kt
@@ -0,0 +1,57 @@
+package linea.coordinator.config.v2
+
+import linea.coordinator.config.v2.toml.L1FinalizationMonitorConfigToml
+import linea.coordinator.config.v2.toml.parseConfig
+import linea.domain.BlockParameter
+import linea.kotlin.toURL
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+import kotlin.time.Duration.Companion.seconds
+
+class L1FinalizationMonitorParsingTest {
+ companion object {
+ val toml = """
+ [l1-finalization-monitor]
+ l1-endpoint = "http://l1-el-node:8545"
+ l2-endpoint = "http://sequencer:8545"
+ l1-polling-interval = "PT1S"
+ l1-query-block-tag="FINALIZED"
+ """.trimIndent()
+
+ val config = L1FinalizationMonitorConfigToml(
+ l1Endpoint = "http://l1-el-node:8545".toURL(),
+ l2Endpoint = "http://sequencer:8545".toURL(),
+ l1PollingInterval = 1.seconds,
+ l1QueryBlockTag = BlockParameter.Tag.FINALIZED,
+ )
+
+ val tomlMinimal = """
+ [l1-finalization-monitor]
+ l1-endpoint = "http://l1-el-node:8545"
+ l2-endpoint = "http://sequencer:8545"
+ """.trimIndent()
+
+ val configMinimal = L1FinalizationMonitorConfigToml(
+ l1Endpoint = "http://l1-el-node:8545".toURL(),
+ l2Endpoint = "http://sequencer:8545".toURL(),
+ l1PollingInterval = 6.seconds,
+ l1QueryBlockTag = BlockParameter.Tag.FINALIZED,
+ )
+ }
+
+ data class WrapperConfig(
+ val l1FinalizationMonitor: L1FinalizationMonitorConfigToml,
+ )
+
+ @Test
+ fun `should parse finalization monitor full config`() {
+ assertThat(parseConfig(toml).l1FinalizationMonitor)
+ .isEqualTo(config)
+ }
+
+ @Test
+ fun `should parse finalization monitor minimal config`() {
+ assertThat(parseConfig(tomlMinimal).l1FinalizationMonitor)
+ .isEqualTo(configMinimal)
+ }
+}
diff --git a/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/L1SubmissionConfigParsingTest.kt b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/L1SubmissionConfigParsingTest.kt
new file mode 100644
index 0000000000..d125988596
--- /dev/null
+++ b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/L1SubmissionConfigParsingTest.kt
@@ -0,0 +1,358 @@
+package linea.coordinator.config.v2
+
+import com.sksamuel.hoplite.Masked
+import linea.coordinator.config.v2.toml.L1SubmissionConfigToml
+import linea.coordinator.config.v2.toml.SignerConfigToml
+import linea.coordinator.config.v2.toml.parseConfig
+import linea.kotlin.decodeHex
+import linea.kotlin.toURL
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+import kotlin.time.Duration.Companion.days
+import kotlin.time.Duration.Companion.hours
+import kotlin.time.Duration.Companion.minutes
+import kotlin.time.Duration.Companion.seconds
+
+class L1SubmissionConfigParsingTest {
+ companion object {
+ val toml = """
+ [l1-submission]
+ disabled = true
+ [l1-submission.dynamic-gas-price-cap]
+ disabled = true
+ [l1-submission.dynamic-gas-price-cap.gas-price-cap-calculation]
+ adjustment-constant = 25
+ blob-adjustment-constant = 25
+ finalization-target-max-delay = "PT32H"
+ base-fee-per-gas-percentile-window = "P7D"
+ base-fee-per-gas-percentile-window-leeway = "PT10M"
+ base-fee-per-gas-percentile = 10
+ gas-price-caps-check-coefficient = 0.9
+ historic-base-fee-per-blob-gas-lower-bound=100000011 # 0.1 GWEI
+ historic-avg-reward-constant=100000012 # 0.1 GWEI
+ [l1-submission.dynamic-gas-price-cap.fee-history-fetcher]
+ l1-endpoint = "http://l1-node:8545"
+ fetch-interval = "PT1S"
+ max-block-count = 1000
+ reward-percentiles = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
+ num-of-blocks-before-latest = 2
+ storage-period = "P10D"
+
+ [l1-submission.fallback-gas-price]
+ fee-history-block-count = 10
+ fee-history-reward-percentile = 15
+
+ [l1-submission.blob]
+ disabled = false
+ l1-endpoint = "http://l1-el-node:8545"
+ submission-delay = "PT1S"
+ submission-tick-interval = "PT10S"
+ max-submission-transactions-per-tick = 10
+ target-blobs-per-transaction=9
+ db-max-blobs-to-return = 100
+ [l1-submission.blob.gas]
+ gas-limit = 10_000_000
+ max-fee-per-gas-cap = 100_000_000_000
+ max-fee-per-blob-gas-cap = 100_000_000
+ max-priority-fee-per-gas-cap=10_000_000_000
+ # Note: prefixed with "fallback-", used when dynamic gas price is disabled or DB is not populated yet
+ [l1-submission.blob.gas.fallback]
+ priority-fee-per-gas-upper-bound = 20_000_000_000 # 20 GWEI
+ priority-fee-per-gas-lower-bound = 2_000_000_000 # 2 GWEI
+
+ [l1-submission.blob.signer]
+ # Web3j/Web3signer
+ type = "Web3j"
+
+ # The account with this private key is in genesis file
+ [l1-submission.blob.signer.web3j]
+ private-key = "0x0000000000000000000000000000000000000000000000000000000000000001"
+
+ [l1-submission.blob.signer.web3signer]
+ endpoint = "http://web3signer:9000"
+ max-pool-size = 10
+ keep-alive = true
+ public-key = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"
+
+ [l1-submission.aggregation]
+ disabled = false
+ l1-endpoint = "http://l1-el-node:8545"
+ submission-delay = "PT2S"
+ submission-tick-interval = "PT12S"
+ max-submissions-per-tick = 10
+ [l1-submission.aggregation.gas]
+ gas-limit = 10_000_001
+ max-fee-per-gas-cap = 100_000_000_001
+ max-priority-fee-per-gas-cap = 10_000_000_001
+
+ [l1-submission.aggregation.gas.fallback]
+ # Note: prefixed with "fallback-", used when dynamic gas price is disabled or DB is not populated yet
+ priority-fee-per-gas-upper-bound = 20_000_000_001 # 20 GWEI
+ priority-fee-per-gas-lower-bound = 2_000_000_001 # 2 GWEI
+
+ [l1-submission.aggregation.signer]
+ # Web3j/Web3signer
+ type = "Web3signer"
+
+ [l1-submission.aggregation.signer.web3j]
+ private-key = "0x0000000000000000000000000000000000000000000000000000000000000002"
+
+ [l1-submission.aggregation.signer.web3signer]
+ endpoint = "http://web3signer:9000"
+ max-pool-size = 10
+ keep-alive = true
+ public-key = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"
+ """.trimIndent()
+
+ val config =
+ L1SubmissionConfigToml(
+ disabled = true,
+ dynamicGasPriceCap = L1SubmissionConfigToml.DynamicGasPriceCapToml(
+ disabled = true,
+ gasPriceCapCalculation = L1SubmissionConfigToml.DynamicGasPriceCapToml.GasPriceCapCalculationToml(
+ adjustmentConstant = 25u,
+ blobAdjustmentConstant = 25u,
+ finalizationTargetMaxDelay = 32.hours,
+ baseFeePerGasPercentileWindow = 7.days,
+ baseFeePerGasPercentileWindowLeeway = 10.minutes,
+ baseFeePerGasPercentile = 10u,
+ gasPriceCapsCheckCoefficient = 0.9,
+ historicBaseFeePerBlobGasLowerBound = 100000011UL,
+ historicAvgRewardConstant = 100000012UL,
+ ),
+ feeHistoryFetcher = L1SubmissionConfigToml.DynamicGasPriceCapToml.FeeHistoryFetcherConfig(
+ l1Endpoint = "http://l1-node:8545".toURL(),
+ fetchInterval = 1.seconds,
+ maxBlockCount = 1000u,
+ rewardPercentiles = listOf(10, 20, 30, 40, 50, 60, 70, 80, 90, 100).map { it.toUInt() },
+ numOfBlocksBeforeLatest = 2u,
+ storagePeriod = 10.days,
+ ),
+ ),
+ fallbackGasPrice = L1SubmissionConfigToml.FallbackGasPriceToml(
+ feeHistoryBlockCount = 10u,
+ feeHistoryRewardPercentile = 15u,
+ ),
+ blob = L1SubmissionConfigToml.BlobSubmissionConfigToml(
+ disabled = false,
+ l1Endpoint = "http://l1-el-node:8545".toURL(),
+ submissionDelay = 1.seconds,
+ submissionTickInterval = 10.seconds,
+ maxSubmissionTransactionsPerTick = 10u,
+ targetBlobsPerTransaction = 9u,
+ dbMaxBlobsToReturn = 100u,
+ gas = L1SubmissionConfigToml.GasConfigToml(
+ gasLimit = 10_000_000u,
+ maxFeePerGasCap = 100_000_000_000u,
+ maxFeePerBlobGasCap = 100_000_000u,
+ maxPriorityFeePerGasCap = 10_000_000_000u,
+ fallback = L1SubmissionConfigToml.GasConfigToml.FallbackGasConfig(
+ priorityFeePerGasUpperBound = 20_000_000_000u,
+ priorityFeePerGasLowerBound = 2_000_000_000u,
+ ),
+ ),
+ signer = SignerConfigToml(
+ type = SignerConfigToml.SignerType.WEB3J,
+ web3j = SignerConfigToml.Web3jConfig(
+ privateKey = Masked("0x0000000000000000000000000000000000000000000000000000000000000001"),
+ ),
+ web3signer = SignerConfigToml.Web3SignerConfig(
+ endpoint = "http://web3signer:9000".toURL(),
+ publicKey = (
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000001"
+ ).decodeHex(),
+ maxPoolSize = 10,
+ keepAlive = true,
+ ),
+ ),
+ ),
+ aggregation = L1SubmissionConfigToml.AggregationSubmissionToml(
+ disabled = false,
+ l1Endpoint = "http://l1-el-node:8545".toURL(),
+ submissionDelay = 2.seconds,
+ submissionTickInterval = 12.seconds,
+ maxSubmissionsPerTick = 10u,
+ gas = L1SubmissionConfigToml.GasConfigToml(
+ gasLimit = 10_000_001u,
+ maxFeePerGasCap = 100_000_000_001u,
+ maxFeePerBlobGasCap = null,
+ maxPriorityFeePerGasCap = 10_000_000_001u,
+ fallback = L1SubmissionConfigToml.GasConfigToml.FallbackGasConfig(
+ priorityFeePerGasUpperBound = 20_000_000_001u,
+ priorityFeePerGasLowerBound = 2_000_000_001u,
+ ),
+ ),
+ signer = SignerConfigToml(
+ type = SignerConfigToml.SignerType.WEB3SIGNER,
+ web3j = SignerConfigToml.Web3jConfig(
+ privateKey = Masked("0x0000000000000000000000000000000000000000000000000000000000000002"),
+ ),
+ web3signer = SignerConfigToml.Web3SignerConfig(
+ endpoint = "http://web3signer:9000".toURL(),
+ publicKey = (
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000002"
+ ).decodeHex(),
+ maxPoolSize = 10,
+ keepAlive = true,
+ ),
+ ),
+ ),
+ )
+
+ val tomlMinimal = """
+ [l1-submission]
+ [l1-submission.dynamic-gas-price-cap]
+ [l1-submission.dynamic-gas-price-cap.gas-price-cap-calculation]
+ adjustment-constant = 25
+ blob-adjustment-constant = 25
+ finalization-target-max-delay = "PT32H"
+ base-fee-per-gas-percentile-window = "P7D"
+ base-fee-per-gas-percentile-window-leeway = "PT10M"
+ base-fee-per-gas-percentile = 10
+ gas-price-caps-check-coefficient = 0.9
+ historic-base-fee-per-blob-gas-lower-bound=100000011 # 0.1 GWEI
+ historic-avg-reward-constant=100000012 # 0.1 GWEI
+ [l1-submission.dynamic-gas-price-cap.gas-price-cap-calculation]
+
+ [l1-submission.fallback-gas-price]
+ fee-history-block-count = 10
+ fee-history-reward-percentile = 15
+
+ [l1-submission.blob]
+ [l1-submission.blob.gas]
+ gas-limit = 10_000_000
+ max-fee-per-gas-cap = 100_000_000_001
+ max-fee-per-blob-gas-cap = 100_000_000
+ max-priority-fee-per-gas-cap=10_000_000_000
+ [l1-submission.blob.gas.fallback]
+ priority-fee-per-gas-upper-bound = 20_000_000_000 # 20 GWEI
+ priority-fee-per-gas-lower-bound = 2_000_000_000 # 2 GWEI
+
+ [l1-submission.blob.signer]
+ type = "Web3j"
+ [l1-submission.blob.signer.web3j]
+ private-key = "0x0000000000000000000000000000000000000000000000000000000000000001"
+
+ [l1-submission.aggregation]
+ [l1-submission.aggregation.gas]
+ gas-limit = 10_000_001
+ max-fee-per-gas-cap = 100_000_000_011
+ max-priority-fee-per-gas-cap = 10_000_000_010
+
+ [l1-submission.aggregation.gas.fallback]
+ priority-fee-per-gas-upper-bound = 20_000_000_001 # 20 GWEI
+ priority-fee-per-gas-lower-bound = 2_000_000_001 # 2 GWEI
+
+ [l1-submission.aggregation.signer]
+ type = "Web3signer"
+ [l1-submission.aggregation.signer.web3signer]
+ endpoint = "http://web3signer:9000"
+ max-pool-size = 10
+ keep-alive = true
+ public-key = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"
+ """.trimIndent()
+
+ val configMinimal =
+ L1SubmissionConfigToml(
+ disabled = false,
+ dynamicGasPriceCap = L1SubmissionConfigToml.DynamicGasPriceCapToml(
+ disabled = false,
+ gasPriceCapCalculation = L1SubmissionConfigToml.DynamicGasPriceCapToml.GasPriceCapCalculationToml(
+ adjustmentConstant = 25u,
+ blobAdjustmentConstant = 25u,
+ finalizationTargetMaxDelay = 32.hours,
+ baseFeePerGasPercentileWindow = 7.days,
+ baseFeePerGasPercentileWindowLeeway = 10.minutes,
+ baseFeePerGasPercentile = 10u,
+ gasPriceCapsCheckCoefficient = 0.9,
+ historicBaseFeePerBlobGasLowerBound = 100000011UL,
+ historicAvgRewardConstant = 100000012UL,
+ ),
+ feeHistoryFetcher = L1SubmissionConfigToml.DynamicGasPriceCapToml.FeeHistoryFetcherConfig(
+ fetchInterval = 3.seconds,
+ maxBlockCount = 1000u,
+ rewardPercentiles = listOf(10, 20, 30, 40, 50, 60, 70, 80, 90, 100).map { it.toUInt() },
+ numOfBlocksBeforeLatest = 4u,
+ storagePeriod = 10.days,
+ ),
+ ),
+ fallbackGasPrice = L1SubmissionConfigToml.FallbackGasPriceToml(
+ feeHistoryBlockCount = 10u,
+ feeHistoryRewardPercentile = 15u,
+ ),
+ blob = L1SubmissionConfigToml.BlobSubmissionConfigToml(
+ disabled = false,
+ l1Endpoint = null,
+ submissionDelay = 0.seconds,
+ submissionTickInterval = 12.seconds,
+ maxSubmissionTransactionsPerTick = 2u,
+ targetBlobsPerTransaction = 7u,
+ dbMaxBlobsToReturn = 100u,
+ gas = L1SubmissionConfigToml.GasConfigToml(
+ gasLimit = 10_000_000u,
+ maxFeePerGasCap = 100_000_000_001u,
+ maxFeePerBlobGasCap = 100_000_000u,
+ maxPriorityFeePerGasCap = 10_000_000_000u,
+ fallback = L1SubmissionConfigToml.GasConfigToml.FallbackGasConfig(
+ priorityFeePerGasUpperBound = 20_000_000_000u,
+ priorityFeePerGasLowerBound = 2_000_000_000u,
+ ),
+ ),
+ signer = SignerConfigToml(
+ type = SignerConfigToml.SignerType.WEB3J,
+ web3j = SignerConfigToml.Web3jConfig(
+ privateKey = Masked("0x0000000000000000000000000000000000000000000000000000000000000001"),
+ ),
+ web3signer = null,
+ ),
+ ),
+ aggregation = L1SubmissionConfigToml.AggregationSubmissionToml(
+ disabled = false,
+ l1Endpoint = null,
+ submissionDelay = 0.seconds,
+ submissionTickInterval = 24.seconds,
+ maxSubmissionsPerTick = 1u,
+ gas = L1SubmissionConfigToml.GasConfigToml(
+ gasLimit = 10_000_001u,
+ maxFeePerGasCap = 100_000_000_011u,
+ maxFeePerBlobGasCap = null,
+ maxPriorityFeePerGasCap = 10_000_000_010u,
+ fallback = L1SubmissionConfigToml.GasConfigToml.FallbackGasConfig(
+ priorityFeePerGasUpperBound = 20_000_000_001u,
+ priorityFeePerGasLowerBound = 2_000_000_001u,
+ ),
+ ),
+ signer = SignerConfigToml(
+ type = SignerConfigToml.SignerType.WEB3SIGNER,
+ web3j = null,
+ web3signer = SignerConfigToml.Web3SignerConfig(
+ endpoint = "http://web3signer:9000".toURL(),
+ publicKey = (
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000002"
+ ).decodeHex(),
+ maxPoolSize = 10,
+ keepAlive = true,
+ ),
+ ),
+ ),
+ )
+ }
+
+ data class WrapperConfig(
+ val l1Submission: L1SubmissionConfigToml,
+ )
+
+ @Test
+ fun `should parse l1 submission full config`() {
+ assertThat(parseConfig(toml).l1Submission)
+ .isEqualTo(config)
+ }
+
+ @Test
+ fun `should parse l1 submission minimal config`() {
+ assertThat(parseConfig(tomlMinimal).l1Submission).isEqualTo(configMinimal)
+ }
+}
diff --git a/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/L2NetWorkingGasPricingConfigParsingTest.kt b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/L2NetWorkingGasPricingConfigParsingTest.kt
new file mode 100644
index 0000000000..d34bff139b
--- /dev/null
+++ b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/L2NetWorkingGasPricingConfigParsingTest.kt
@@ -0,0 +1,135 @@
+package linea.coordinator.config.v2
+
+import linea.coordinator.config.v2.toml.L2NetworkGasPricingConfigToml
+import linea.coordinator.config.v2.toml.RequestRetriesToml
+import linea.coordinator.config.v2.toml.parseConfig
+import linea.kotlin.toURL
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+import kotlin.time.Duration.Companion.seconds
+
+class L2NetWorkingGasPricingConfigParsingTest {
+ companion object {
+ val toml = """
+ [l2-network-gas-pricing]
+ disabled = false
+ price-update-interval = "PT12S"
+ fee-history-block-count = 50
+ fee-history-reward-percentile = 15
+ gas-price-fixed-cost = 3000000
+ l1-endpoint="http://l1-el-node:8545/"
+ extra-data-update-endpoint = "http://sequencer:8545/"
+ [l2-network-gas-pricing.extra-data-update-request-retries]
+ max-retries = 3
+ timeout = "PT6S"
+ backoff-delay = "PT1S"
+ failures-warning-threshold = 2
+
+ [l2-network-gas-pricing.flat-rate-gas-pricing]
+ gas-price-upper-bound = 10000000000 # 10 GWEI
+ gas-price-lower-bound = 90000000 # 0.09 GWEI
+ compressed-tx-size = 125
+ expected-gas = 21000
+
+ [l2-network-gas-pricing.dynamic-gas-pricing]
+ l1-blob-gas = 131072 # 2^17
+ blob-submission-expected-execution-gas = 213000 # Lower to 120k as we improve efficiency
+ variable-cost-upper-bound = 10000000001 # ~10 GWEI
+ variable-cost-lower-bound = 90000001 # ~0.09 GWEI
+ margin = 4.0
+ """.trimIndent()
+
+ val config = L2NetworkGasPricingConfigToml(
+ disabled = false,
+ priceUpdateInterval = 12.seconds,
+ feeHistoryBlockCount = 50u,
+ feeHistoryRewardPercentile = 15u,
+ gasPriceFixedCost = 3_000_000UL,
+ l1Endpoint = "http://l1-el-node:8545/".toURL(),
+ extraDataUpdateEndpoint = "http://sequencer:8545/".toURL(),
+ extraDataUpdateRequestRetries = RequestRetriesToml(
+ maxRetries = 3u,
+ timeout = 6.seconds,
+ backoffDelay = 1.seconds,
+ failuresWarningThreshold = 2u,
+ ),
+ dynamicGasPricing = L2NetworkGasPricingConfigToml.DynamicGasPricingToml(
+ l1BlobGas = 131072UL, // 2^17
+ blobSubmissionExpectedExecutionGas = 213000UL, // Lower to 120k as we improve efficiency
+ variableCostUpperBound = 10_000_000_001UL, // ~10 GWEI
+ variableCostLowerBound = 90_000_001UL, // ~0.09 GWEI
+ margin = 4.0,
+ ),
+ flatRateGasPricing = L2NetworkGasPricingConfigToml.FlatRateGasPricingToml(
+ gasPriceUpperBound = 10_000_000_000UL, // 10 GWEI
+ gasPriceLowerBound = 90_000_000UL, // 0.09 GWEI
+ compressedTxSize = 125u,
+ expectedGas = 21000u,
+ ),
+ )
+
+ val tomlMinimal = """
+ [l2-network-gas-pricing]
+ gas-price-fixed-cost = 3000000
+ extra-data-update-endpoint = "http://sequencer:8545/"
+
+ [l2-network-gas-pricing.flat-rate-gas-pricing]
+ gas-price-upper-bound = 10000000000 # 10 GWEI
+ gas-price-lower-bound = 90000000 # 0.09 GWEI
+ compressed-tx-size = 125
+ expected-gas = 21000
+
+ [l2-network-gas-pricing.dynamic-gas-pricing]
+ l1-blob-gas = 131072 # 2^17
+ blob-submission-expected-execution-gas = 213000 # Lower to 120k as we improve efficiency
+ variable-cost-upper-bound = 10000000001 # ~10 GWEI
+ variable-cost-lower-bound = 90000001 # ~0.09 GWEI
+ margin = 4.0
+ """.trimIndent()
+
+ val configMinimal = L2NetworkGasPricingConfigToml(
+ disabled = false,
+ priceUpdateInterval = 12.seconds,
+ feeHistoryBlockCount = 1000u,
+ feeHistoryRewardPercentile = 15u,
+ gasPriceFixedCost = 3_000_000UL,
+ l1Endpoint = null,
+ extraDataUpdateEndpoint = "http://sequencer:8545/".toURL(),
+ extraDataUpdateRequestRetries = RequestRetriesToml(
+ maxRetries = null,
+ timeout = 8.seconds,
+ backoffDelay = 1.seconds,
+ failuresWarningThreshold = 3u,
+ ),
+ dynamicGasPricing = L2NetworkGasPricingConfigToml.DynamicGasPricingToml(
+ l1BlobGas = 131072UL, // 2^17
+ blobSubmissionExpectedExecutionGas = 213000UL, // Lower to 120k as we improve efficiency
+ variableCostUpperBound = 10_000_000_001UL, // ~10 GWEI
+ variableCostLowerBound = 90_000_001UL, // ~0.09 GWEI
+ margin = 4.0,
+ ),
+ flatRateGasPricing = L2NetworkGasPricingConfigToml.FlatRateGasPricingToml(
+ gasPriceUpperBound = 10_000_000_000UL, // 10 GWEI
+ gasPriceLowerBound = 90_000_000UL, // 0.09 GWEI
+ compressedTxSize = 125u,
+ expectedGas = 21000u,
+ ),
+ )
+ }
+
+ data class WrapperConfig(
+ val l2NetworkGasPricing: L2NetworkGasPricingConfigToml,
+ )
+
+ @Test
+ fun `should parse l2 network gaspricing full config`() {
+ assertThat(parseConfig(toml).l2NetworkGasPricing)
+ .isEqualTo(config)
+ }
+
+ @Test
+ fun `should parse l2 network gaspricing minimal config`() {
+ assertThat(parseConfig(tomlMinimal).l2NetworkGasPricing)
+ .isEqualTo(configMinimal)
+ }
+}
diff --git a/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/LocalStackConfigsParsingTest.kt b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/LocalStackConfigsParsingTest.kt
new file mode 100644
index 0000000000..97ef585f36
--- /dev/null
+++ b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/LocalStackConfigsParsingTest.kt
@@ -0,0 +1,27 @@
+package linea.coordinator.config.v2
+
+import linea.coordinator.config.v2.toml.loadConfigs
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+import java.nio.file.Path
+
+class LocalStackConfigsParsingTest {
+ @Test
+ fun `should keep local stack testing configs updated with the code`() {
+ // Just assert that Files have been loaded and parsed correctly
+ // This is to prevent Code changes in coordinator and forgetting to update config files used in the local stack
+ loadConfigs(
+ coordinatorConfigFiles = listOf(
+ Path.of("../../config/coordinator/coordinator-config-v2.toml"),
+ Path.of("../../config/coordinator/coordinator-config-v2-override-local-dev.toml"),
+ ),
+ tracesLimitsFileV2 = Path.of("../../config/common/traces-limits-v2.toml"),
+ gasPriceCapTimeOfDayMultipliersFile = Path.of("../../config/common/gas-price-cap-time-of-day-multipliers.toml"),
+ smartContractErrorsFile = Path.of("../../config/common/smart-contract-errors.toml"),
+ enforceStrict = true,
+ ).also { configs ->
+ // just small assertion to ensure that the configs are loaded and overridden correctly
+ assertThat(configs.database.host).isEqualTo("127.0.0.1")
+ }
+ }
+}
diff --git a/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/MessageAnchoringConfigParsingTest.kt b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/MessageAnchoringConfigParsingTest.kt
new file mode 100644
index 0000000000..4e3bca86c8
--- /dev/null
+++ b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/MessageAnchoringConfigParsingTest.kt
@@ -0,0 +1,185 @@
+package linea.coordinator.config.v2
+
+import com.sksamuel.hoplite.Masked
+import linea.coordinator.config.v2.toml.MessageAnchoringConfigToml
+import linea.coordinator.config.v2.toml.RequestRetriesToml
+import linea.coordinator.config.v2.toml.SignerConfigToml
+import linea.coordinator.config.v2.toml.parseConfig
+import linea.domain.BlockParameter
+import linea.kotlin.decodeHex
+import linea.kotlin.toURL
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.Duration.Companion.seconds
+
+class MessageAnchoringConfigParsingTest {
+ companion object {
+ val toml = """
+ [message-anchoring]
+ disabled = false
+ anchoring-tick-interval = "PT13S"
+ message-queue-capacity = 12_300
+ max-messages-to-anchor-per-l2-transaction = 86
+ l1-endpoint = "http://l1-el-node:8545"
+ l2-endpoint = "http://sequencer:8545"
+ l1-highest-block-tag="FINALIZED"
+ l2-highest-block-tag="LATEST" # optional, default to LATEST it shall not be necessary as Linea has instant finality
+
+ [message-anchoring.l1-request-retries]
+ max-retries = 4
+ backoff-delay = "PT1S"
+ timeout = "PT6S"
+ failures-warning-threshold = 2
+
+ [message-anchoring.l2-request-retries]
+ max-retries = 5
+ backoff-delay = "PT0.1S"
+ timeout = "PT10S"
+ failures-warning-threshold = 3
+
+ [message-anchoring.l1-event-scraping]
+ polling-interval = "PT1S"
+ polling-timeout = "PT50S"
+ eth-logs-search-success-backoff-delay = "PT0.1S"
+ eth-logs-search-block-chunk-size = 123
+
+ [message-anchoring.gas]
+ max-fee-per-gas-cap = 100000000000
+ gas-limit = 10000000
+ fee-history-block-count = 4
+ fee-history-reward-percentile = 15
+
+ [message-anchoring.signer]
+ # Web3j/Web3signer
+ type = "Web3j"
+
+ [message-anchoring.signer.web3j]
+ private-key = "0x0000000000000000000000000000000000000000000000000000000000000001"
+
+ [message-anchoring.signer.web3signer]
+ endpoint = "http://web3signer:9000"
+ max-pool-size = 11
+ keep-alive = true
+ public-key = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"
+ """.trimIndent()
+
+ val config =
+ MessageAnchoringConfigToml(
+ disabled = false,
+ l1Endpoint = "http://l1-el-node:8545".toURL(),
+ l2Endpoint = "http://sequencer:8545".toURL(),
+ l1HighestBlockTag = BlockParameter.Tag.FINALIZED,
+ l2HighestBlockTag = BlockParameter.Tag.LATEST,
+ anchoringTickInterval = 13.seconds,
+ messageQueueCapacity = 12_300u,
+ maxMessagesToAnchorPerL2Transaction = 86u,
+ l1EventScraping = MessageAnchoringConfigToml.L1EventScrapping(
+ pollingInterval = 1.seconds,
+ pollingTimeout = 50.seconds,
+ ethLogsSearchSuccessBackoffDelay = 100.milliseconds,
+ ethLogsSearchBlockChunkSize = 123u,
+ ),
+ l1RequestRetries = RequestRetriesToml(
+ maxRetries = 4u,
+ backoffDelay = 1.seconds,
+ timeout = 6.seconds,
+ failuresWarningThreshold = 2u,
+ ),
+ l2RequestRetries = RequestRetriesToml(
+ maxRetries = 5u,
+ backoffDelay = 100.milliseconds,
+ timeout = 10.seconds,
+ failuresWarningThreshold = 3u,
+ ),
+ gas = MessageAnchoringConfigToml.GasConfig(
+ maxFeePerGasCap = 100_000_000_000u,
+ gasLimit = 10_000_000u,
+ feeHistoryBlockCount = 4u,
+ feeHistoryRewardPercentile = 15u,
+ ),
+ signer = SignerConfigToml(
+ type = SignerConfigToml.SignerType.WEB3J,
+ web3j = SignerConfigToml.Web3jConfig(
+ privateKey = Masked("0x0000000000000000000000000000000000000000000000000000000000000001"),
+ ),
+ web3signer = SignerConfigToml.Web3SignerConfig(
+ endpoint = "http://web3signer:9000".toURL(),
+ maxPoolSize = 11,
+ keepAlive = true,
+ publicKey = (
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000001"
+ ).decodeHex(),
+ ),
+ ),
+ )
+
+ val tomlMinimal = """
+ [message-anchoring]
+ [message-anchoring.signer]
+ type = "Web3j"
+ [message-anchoring.signer.web3j]
+ private-key = "0x0000000000000000000000000000000000000000000000000000000000000001"
+ """.trimIndent()
+
+ val configMinimal =
+ MessageAnchoringConfigToml(
+ disabled = false,
+ anchoringTickInterval = 10.seconds,
+ messageQueueCapacity = 10_000u,
+ maxMessagesToAnchorPerL2Transaction = 100u,
+ l1HighestBlockTag = BlockParameter.Tag.FINALIZED,
+ l2HighestBlockTag = BlockParameter.Tag.LATEST,
+ l1Endpoint = null,
+ l2Endpoint = null,
+ l1EventScraping = MessageAnchoringConfigToml.L1EventScrapping(
+ pollingInterval = 6.seconds,
+ pollingTimeout = 5.seconds,
+ ethLogsSearchSuccessBackoffDelay = 1.milliseconds,
+ ethLogsSearchBlockChunkSize = 1000u,
+ ),
+ l1RequestRetries = RequestRetriesToml(
+ maxRetries = null,
+ backoffDelay = 1.seconds,
+ timeout = null,
+ failuresWarningThreshold = 3u,
+ ),
+ l2RequestRetries = RequestRetriesToml(
+ maxRetries = null,
+ backoffDelay = 1.seconds,
+ timeout = 8.seconds,
+ failuresWarningThreshold = 3u,
+ ),
+ gas = MessageAnchoringConfigToml.GasConfig(
+ maxFeePerGasCap = 100_000_000_000u,
+ gasLimit = 2_500_000u,
+ feeHistoryBlockCount = 4u,
+ feeHistoryRewardPercentile = 15u,
+ ),
+ signer = SignerConfigToml(
+ type = SignerConfigToml.SignerType.WEB3J,
+ web3j = SignerConfigToml.Web3jConfig(
+ privateKey = Masked("0x0000000000000000000000000000000000000000000000000000000000000001"),
+ ),
+ web3signer = null,
+ ),
+ )
+ }
+
+ data class WrapperConfig(
+ val messageAnchoring: MessageAnchoringConfigToml,
+ )
+
+ @Test
+ fun `should parse message anchoring full config`() {
+ assertThat(parseConfig(toml).messageAnchoring)
+ .isEqualTo(config)
+ }
+
+ @Test
+ fun `should parse message anchoring minimal config`() {
+ assertThat(parseConfig(tomlMinimal).messageAnchoring)
+ .isEqualTo(configMinimal)
+ }
+}
diff --git a/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/ProtocolParsingTest.kt b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/ProtocolParsingTest.kt
new file mode 100644
index 0000000000..0d088fa75b
--- /dev/null
+++ b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/ProtocolParsingTest.kt
@@ -0,0 +1,78 @@
+package linea.coordinator.config.v2
+
+import linea.coordinator.config.v2.toml.ProtocolToml
+import linea.coordinator.config.v2.toml.parseConfig
+import linea.kotlin.decodeHex
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+import kotlin.time.Duration.Companion.seconds
+
+class ProtocolParsingTest {
+ companion object {
+ val toml = """
+ [protocol.genesis]
+ genesis-state-root-hash = "0x0000000000000000000000000000000000000000000000000000000000000001"
+ genesis-shnarf = "0x0000000000000000000000000000000000000000000000000000000000000002"
+ [protocol.l1]
+ contract-address = "0x0000000000000000000000000000000000000001"
+ block-time = "PT2S"
+ [protocol.l2]
+ contract-address = "0x0000000000000000000000000000000000000002"
+ contract-deployment-block-number = 1
+ """.trimIndent()
+
+ val config = ProtocolToml(
+ genesis = ProtocolToml.Genesis(
+ genesisStateRootHash = "0x0000000000000000000000000000000000000000000000000000000000000001".decodeHex(),
+ genesisShnarf = "0x0000000000000000000000000000000000000000000000000000000000000002".decodeHex(),
+ ),
+ l1 = ProtocolToml.Layer1Config(
+ contractAddress = "0x0000000000000000000000000000000000000001",
+ blockTime = 2.seconds,
+ ),
+ l2 = ProtocolToml.Layer2Config(
+ contractAddress = "0x0000000000000000000000000000000000000002",
+ contractDeploymentBlockNumber = 1UL,
+ ),
+ )
+
+ val tomlMinimal = """
+ [protocol.genesis]
+ genesis-state-root-hash = "0x0000000000000000000000000000000000000000000000000000000000000001"
+ genesis-shnarf = "0x0000000000000000000000000000000000000000000000000000000000000002"
+ [protocol.l1]
+ contract-address = "0x0000000000000000000000000000000000000001"
+ [protocol.l2]
+ contract-address = "0x0000000000000000000000000000000000000002"
+ """.trimIndent()
+
+ val configMinimal = ProtocolToml(
+ genesis = ProtocolToml.Genesis(
+ genesisStateRootHash = "0x0000000000000000000000000000000000000000000000000000000000000001".decodeHex(),
+ genesisShnarf = "0x0000000000000000000000000000000000000000000000000000000000000002".decodeHex(),
+ ),
+ l1 = ProtocolToml.Layer1Config(
+ contractAddress = "0x0000000000000000000000000000000000000001",
+ blockTime = 12.seconds,
+ ),
+ l2 = ProtocolToml.Layer2Config(
+ contractAddress = "0x0000000000000000000000000000000000000002",
+ contractDeploymentBlockNumber = null,
+ ),
+ )
+ }
+
+ internal data class WrapperConfig(val protocol: ProtocolToml)
+
+ @Test
+ fun `should parse protocol full configs`() {
+ assertThat(parseConfig(toml).protocol)
+ .isEqualTo(config)
+ }
+
+ @Test
+ fun `should parse protocol minimal configs`() {
+ assertThat(parseConfig(tomlMinimal).protocol)
+ .isEqualTo(configMinimal)
+ }
+}
diff --git a/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/ProverParsingTest.kt b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/ProverParsingTest.kt
new file mode 100644
index 0000000000..5f55db917c
--- /dev/null
+++ b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/ProverParsingTest.kt
@@ -0,0 +1,129 @@
+package linea.coordinator.config.v2
+
+import linea.coordinator.config.v2.toml.ProverToml
+import linea.coordinator.config.v2.toml.parseConfig
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+import kotlin.time.Duration.Companion.minutes
+import kotlin.time.Duration.Companion.seconds
+
+class ProverParsingTest {
+ companion object {
+ val toml = """
+ [prover]
+ version = "v2.0.0"
+ fs-inprogress-request-writing-suffix = ".coordinator_writing_request"
+ fs-inprogress-proving-suffix-pattern = "\\.inprogress\\.prover_is_proving.*"
+ fs-polling-interval = "PT1S"
+ fs-polling-timeout = "PT10M"
+ [prover.execution]
+ fs-requests-directory = "/data/prover/v2/execution/requests"
+ fs-responses-directory = "/data/prover/v2/execution/responses"
+ [prover.blob-compression]
+ fs-requests-directory = "/data/prover/v2/compression/requests"
+ fs-responses-directory = "/data/prover/v2/compression/responses"
+ [prover.proof-aggregation]
+ fs-requests-directory = "/data/prover/v2/aggregation/requests"
+ fs-responses-directory = "/data/prover/v2/aggregation/responses"
+
+ [prover.new]
+ version = "v3.0.0"
+ switch-block-number-inclusive=1000
+ [prover.new.execution]
+ fs-requests-directory = "/data/prover/v3/execution/requests"
+ fs-responses-directory = "/data/prover/v3/execution/responses"
+ [prover.new.blob-compression]
+ fs-requests-directory = "/data/prover/v3/compression/requests"
+ fs-responses-directory = "/data/prover/v3/compression/responses"
+ [prover.new.proof-aggregation]
+ fs-requests-directory = "/data/prover/v3/aggregation/requests"
+ fs-responses-directory = "/data/prover/v3/aggregation/responses"
+ """.trimIndent()
+ val config = ProverToml(
+ version = "v2.0.0",
+ fsInprogressRequestWritingSuffix = ".coordinator_writing_request",
+ fsInprogressProvingSuffixPattern = "\\.inprogress\\.prover_is_proving.*",
+ fsPollingInterval = 1.seconds,
+ fsPollingTimeout = 10.minutes,
+ execution = ProverToml.ProverDirectoriesToml(
+ fsRequestsDirectory = "/data/prover/v2/execution/requests",
+ fsResponsesDirectory = "/data/prover/v2/execution/responses",
+ ),
+ blobCompression = ProverToml.ProverDirectoriesToml(
+ fsRequestsDirectory = "/data/prover/v2/compression/requests",
+ fsResponsesDirectory = "/data/prover/v2/compression/responses",
+ ),
+ proofAggregation = ProverToml.ProverDirectoriesToml(
+ fsRequestsDirectory = "/data/prover/v2/aggregation/requests",
+ fsResponsesDirectory = "/data/prover/v2/aggregation/responses",
+ ),
+ new = ProverToml(
+ switchBlockNumberInclusive = 1_000u,
+ version = "v3.0.0",
+ execution = ProverToml.ProverDirectoriesToml(
+ fsRequestsDirectory = "/data/prover/v3/execution/requests",
+ fsResponsesDirectory = "/data/prover/v3/execution/responses",
+ ),
+ blobCompression = ProverToml.ProverDirectoriesToml(
+ fsRequestsDirectory = "/data/prover/v3/compression/requests",
+ fsResponsesDirectory = "/data/prover/v3/compression/responses",
+ ),
+ proofAggregation = ProverToml.ProverDirectoriesToml(
+ fsRequestsDirectory = "/data/prover/v3/aggregation/requests",
+ fsResponsesDirectory = "/data/prover/v3/aggregation/responses",
+ ),
+ ),
+ )
+
+ val tomlMinimal = """
+ [prover]
+ version = "v2.0.0"
+ [prover.execution]
+ fs-requests-directory = "/data/prover/v2/execution/requests"
+ fs-responses-directory = "/data/prover/v2/execution/responses"
+ [prover.blob-compression]
+ fs-requests-directory = "/data/prover/v2/compression/requests"
+ fs-responses-directory = "/data/prover/v2/compression/responses"
+ [prover.proof-aggregation]
+ fs-requests-directory = "/data/prover/v2/aggregation/requests"
+ fs-responses-directory = "/data/prover/v2/aggregation/responses"
+ """.trimIndent()
+ val configMinimal = ProverToml(
+ version = "v2.0.0",
+ fsInprogressRequestWritingSuffix = ".inprogress_coordinator_writing",
+ fsInprogressProvingSuffixPattern = "\\.inprogress\\.prover.*",
+ execution = ProverToml.ProverDirectoriesToml(
+ fsRequestsDirectory = "/data/prover/v2/execution/requests",
+ fsResponsesDirectory = "/data/prover/v2/execution/responses",
+ ),
+ blobCompression = ProverToml.ProverDirectoriesToml(
+ fsRequestsDirectory = "/data/prover/v2/compression/requests",
+ fsResponsesDirectory = "/data/prover/v2/compression/responses",
+ ),
+ proofAggregation = ProverToml.ProverDirectoriesToml(
+ fsRequestsDirectory = "/data/prover/v2/aggregation/requests",
+ fsResponsesDirectory = "/data/prover/v2/aggregation/responses",
+ ),
+ switchBlockNumberInclusive = null,
+ new = null,
+ )
+ }
+
+ data class WrapperConfig(
+ val prover: ProverToml,
+ )
+
+ @Test
+ fun `should parse prover toml configs - full`() {
+ assertThat(
+ parseConfig(toml).prover,
+ ).isEqualTo(config)
+ }
+
+ @Test
+ fun `should parse conflation toml configs and provide defaults`() {
+ assertThat(
+ parseConfig(tomlMinimal).prover,
+ ).isEqualTo(configMinimal)
+ }
+}
diff --git a/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/SignerConfigParsingTest.kt b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/SignerConfigParsingTest.kt
new file mode 100644
index 0000000000..bc4d2b6ab9
--- /dev/null
+++ b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/SignerConfigParsingTest.kt
@@ -0,0 +1,61 @@
+package linea.coordinator.config.v2
+
+import com.sksamuel.hoplite.Masked
+import linea.coordinator.config.v2.toml.SignerConfigToml
+import linea.coordinator.config.v2.toml.parseConfig
+import linea.kotlin.decodeHex
+import linea.kotlin.toURL
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+
+class SignerConfigParsingTest {
+ companion object {
+ val toml = """
+ [web3jExample]
+ type = "wEb3j" # Shall be case insensitive
+ [web3jExample.web3j]
+ private-key = "0x0000000000000000000000000000000000000000000000000000000000000001"
+
+ [web3signerExample]
+ type = "Web3SiGner" # Shall be case insensitive
+ [web3signerExample.web3signer]
+ endpoint = "http://web3signer:9000"
+ max-pool-size = 10
+ keep-alive = true
+ public-key = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"
+ """.trimIndent()
+
+ val config = WrapperConfig(
+ web3jExample = SignerConfigToml(
+ type = SignerConfigToml.SignerType.WEB3J,
+ web3j = SignerConfigToml.Web3jConfig(
+ privateKey = Masked("0x0000000000000000000000000000000000000000000000000000000000000001"),
+ ),
+ web3signer = null,
+ ),
+ web3SignerExample = SignerConfigToml(
+ type = SignerConfigToml.SignerType.WEB3SIGNER,
+ web3j = null,
+ web3signer = SignerConfigToml.Web3SignerConfig(
+ endpoint = "http://web3signer:9000".toURL(),
+ publicKey = (
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000001"
+ ).decodeHex(),
+ maxPoolSize = 10,
+ keepAlive = true,
+ ),
+ ),
+ )
+ }
+
+ data class WrapperConfig(
+ val web3jExample: SignerConfigToml,
+ val web3SignerExample: SignerConfigToml,
+ )
+
+ @Test
+ fun `should parse full state manager config`() {
+ assertThat(parseConfig(toml)).isEqualTo(config)
+ }
+}
diff --git a/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/StateManagerParsingTest.kt b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/StateManagerParsingTest.kt
new file mode 100644
index 0000000000..0d314732d2
--- /dev/null
+++ b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/StateManagerParsingTest.kt
@@ -0,0 +1,68 @@
+package linea.coordinator.config.v2
+
+import linea.coordinator.config.v2.toml.RequestRetriesToml
+import linea.coordinator.config.v2.toml.StateManagerToml
+import linea.coordinator.config.v2.toml.parseConfig
+import linea.kotlin.toURL
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+import kotlin.time.Duration.Companion.seconds
+
+class StateManagerParsingTest {
+ companion object {
+ val toml = """
+ [state-manager]
+ version = "2.2.0"
+ endpoints = ["http://shomei:8888/"]
+ request-limit-per-endpoint = 3
+ [state-manager.request-retries]
+ max-retries = 5
+ backoff-delay = "PT2S"
+ failures-warning-threshold = 2
+ """.trimIndent()
+
+ val config = StateManagerToml(
+ version = "2.2.0",
+ endpoints = listOf("http://shomei:8888/".toURL()),
+ requestLimitPerEndpoint = 3u,
+ requestRetries = RequestRetriesToml(
+ maxRetries = 5u,
+ backoffDelay = 2.seconds,
+ failuresWarningThreshold = 2u,
+ ),
+ )
+
+ val tomlMinimal = """
+ [state-manager]
+ version = "2.2.0"
+ endpoints = ["http://shomei:8888/"]
+ """.trimIndent()
+
+ val configMinimal = StateManagerToml(
+ version = "2.2.0",
+ endpoints = listOf("http://shomei:8888/".toURL()),
+ requestLimitPerEndpoint = UInt.MAX_VALUE,
+ requestRetries = RequestRetriesToml(
+ maxRetries = null,
+ backoffDelay = 1.seconds,
+ failuresWarningThreshold = 3u,
+ ),
+ )
+ }
+
+ data class WrapperConfig(
+ val stateManager: StateManagerToml,
+ )
+
+ @Test
+ fun `should parse state manager full config`() {
+ assertThat(parseConfig(toml).stateManager)
+ .isEqualTo(config)
+ }
+
+ @Test
+ fun `should parse state manager minimal config`() {
+ assertThat(parseConfig(tomlMinimal).stateManager)
+ .isEqualTo(configMinimal)
+ }
+}
diff --git a/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/TestHelper.kt b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/TestHelper.kt
new file mode 100644
index 0000000000..878f7ac883
--- /dev/null
+++ b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/TestHelper.kt
@@ -0,0 +1,15 @@
+package linea.coordinator.config.v2
+
+import java.nio.file.Path
+import java.nio.file.Paths
+
+class TestHelper {
+ companion object {
+ fun pathToResource(resource: String): Path {
+ return Paths.get(
+ this::class.java.classLoader.getResource(resource)?.toURI()
+ ?: error("Resource not found: $resource"),
+ )
+ }
+ }
+}
diff --git a/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/TracesParsingTest.kt b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/TracesParsingTest.kt
new file mode 100644
index 0000000000..3e9266cbc7
--- /dev/null
+++ b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/TracesParsingTest.kt
@@ -0,0 +1,174 @@
+package linea.coordinator.config.v2
+
+import linea.coordinator.config.v2.toml.RequestRetriesToml
+import linea.coordinator.config.v2.toml.TracesToml
+import linea.coordinator.config.v2.toml.parseConfig
+import linea.kotlin.toURL
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+import java.net.URI
+import kotlin.time.Duration.Companion.seconds
+
+class TracesParsingTest {
+ companion object {
+ val toml = """
+ [traces]
+ expected-traces-api-version = "1.2.0"
+ [traces.counters]
+ endpoints = ["http://traces-api-1:8080/"]
+ request-limit-per-endpoint = 20
+ [traces.counters.request-retries]
+ max-retries = 40
+ backoff-delay = "PT1S"
+ failures-warning-threshold = 2
+
+ [traces.conflation]
+ endpoints = ["http://traces-api-2:8080/"]
+ request-limit-per-endpoint = 2
+ [traces.conflation.request-retries]
+ max-retries = 30
+ backoff-delay = "PT3S"
+ failures-warning-threshold = 4
+
+ [traces.new]
+ switch-block-number-inclusive=1_000
+ expected-traces-api-version = "2.0.0"
+ [traces.new.counters]
+ endpoints = ["http://traces-api-v2-11:8080/", "http://traces-api-v2-12:8080/"]
+ request-limit-per-endpoint = 200
+ [traces.new.counters.request-retries]
+ max-retries = 4
+ backoff-delay = "PT1S"
+ failures-warning-threshold = 2
+
+ [traces.new.conflation]
+ endpoints = ["http://traces-api-v2-21:8080/", "http://traces-api-v2-22:8080/"]
+ request-limit-per-endpoint = 5
+ [traces.new.conflation.request-retries]
+ max-retries = 55
+ backoff-delay = "PT50S"
+ failures-warning-threshold = 50
+ """.trimIndent()
+
+ val config = TracesToml(
+ expectedTracesApiVersion = "1.2.0",
+ counters = TracesToml.ClientApiConfigToml(
+ endpoints = listOf(URI.create("http://traces-api-1:8080/").toURL()),
+ requestLimitPerEndpoint = 20u,
+ requestRetries = RequestRetriesToml(
+ maxRetries = 40u,
+ backoffDelay = 1.seconds,
+ failuresWarningThreshold = 2u,
+ ),
+ ),
+ conflation = TracesToml.ClientApiConfigToml(
+ endpoints = listOf(URI.create("http://traces-api-2:8080/").toURL()),
+ requestLimitPerEndpoint = 2u,
+ requestRetries = RequestRetriesToml(
+ maxRetries = 30u,
+ backoffDelay = 3.seconds,
+ failuresWarningThreshold = 4u,
+ ),
+ ),
+ new = TracesToml(
+ expectedTracesApiVersion = "2.0.0",
+ switchBlockNumberInclusive = 1_000u,
+ counters = TracesToml.ClientApiConfigToml(
+ endpoints = listOf("http://traces-api-v2-11:8080/", "http://traces-api-v2-12:8080/").map { it.toURL() },
+ requestLimitPerEndpoint = 200u,
+ requestRetries = RequestRetriesToml(
+ maxRetries = 4u,
+ backoffDelay = 1.seconds,
+ failuresWarningThreshold = 2u,
+ ),
+ ),
+ conflation = TracesToml.ClientApiConfigToml(
+ endpoints = listOf("http://traces-api-v2-21:8080/", "http://traces-api-v2-22:8080/").map { it.toURL() },
+ requestLimitPerEndpoint = 5u,
+ requestRetries = RequestRetriesToml(
+ maxRetries = 55u,
+ backoffDelay = 50.seconds,
+ failuresWarningThreshold = 50u,
+ ),
+ ),
+ ),
+ )
+
+ val tomlMinimal = """
+ [traces]
+ expected-traces-api-version = "1.2.0"
+ [traces.counters]
+ endpoints = ["http://traces-api-1:8080/"]
+ [traces.conflation]
+ endpoints = ["http://traces-api-2:8080/"]
+ """.trimIndent()
+
+ val configMinimal = TracesToml(
+ expectedTracesApiVersion = "1.2.0",
+ counters = TracesToml.ClientApiConfigToml(
+ endpoints = listOf(URI.create("http://traces-api-1:8080/").toURL()),
+ requestLimitPerEndpoint = UInt.MAX_VALUE,
+ requestRetries = RequestRetriesToml(
+ maxRetries = null,
+ backoffDelay = 1.seconds,
+ failuresWarningThreshold = 3u,
+ ),
+ ),
+ conflation = TracesToml.ClientApiConfigToml(
+ endpoints = listOf(URI.create("http://traces-api-2:8080/").toURL()),
+ requestLimitPerEndpoint = UInt.MAX_VALUE,
+ requestRetries = RequestRetriesToml(
+ maxRetries = null,
+ backoffDelay = 1.seconds,
+ failuresWarningThreshold = 3u,
+ ),
+ ),
+ )
+ }
+
+ data class WrapperConfig(
+ val traces: TracesToml,
+ )
+
+ @Test
+ fun `should parse ClientApiConfigToml config`() {
+ assertThat(
+ parseConfig(
+ """
+ endpoints = ["http://traces-api-1:8080/", "http://traces-api-2:8080/"]
+ request-limit-per-endpoint = 2
+ [request-retries]
+ max-retries = 6
+ backoff-delay = "PT3S"
+ failures-warning-threshold = 4
+ """.trimIndent(),
+ ),
+ )
+ .isEqualTo(
+ TracesToml.ClientApiConfigToml(
+ endpoints = listOf(
+ "http://traces-api-1:8080/".toURL(),
+ "http://traces-api-2:8080/".toURL(),
+ ),
+ requestLimitPerEndpoint = 2u,
+ requestRetries = RequestRetriesToml(
+ maxRetries = 6u,
+ backoffDelay = 3.seconds,
+ failuresWarningThreshold = 4u,
+ ),
+ ),
+ )
+ }
+
+ @Test
+ fun `should parse traces full config`() {
+ assertThat(parseConfig(toml).traces)
+ .isEqualTo(config)
+ }
+
+ @Test
+ fun `should parse traces minimal config`() {
+ assertThat(parseConfig(tomlMinimal).traces)
+ .isEqualTo(configMinimal)
+ }
+}
diff --git a/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/Type2StateProofProviderParsingTest.kt b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/Type2StateProofProviderParsingTest.kt
new file mode 100644
index 0000000000..dce0ba1342
--- /dev/null
+++ b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/Type2StateProofProviderParsingTest.kt
@@ -0,0 +1,71 @@
+package linea.coordinator.config.v2
+
+import linea.coordinator.config.v2.toml.RequestRetriesToml
+import linea.coordinator.config.v2.toml.Type2StateProofManagerToml
+import linea.coordinator.config.v2.toml.parseConfig
+import linea.domain.BlockParameter
+import linea.kotlin.toURL
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+import kotlin.time.Duration.Companion.seconds
+
+class Type2StateProofProviderParsingTest {
+ companion object {
+ val toml = """
+ [type2-state-proof-provider]
+ disabled = false
+ endpoints = ["http://shomei-frontend-i1:8888/", "http://shomei-frontend-i2:8888/"]
+ l1-query-block-tag="SAFE"
+ l1-polling-interval="PT12S"
+ [type2-state-proof-provider.request-retries]
+ max-retries = 3
+ backoff-delay = "PT1S"
+ failures-warning-threshold = 2
+ """.trimIndent()
+
+ val config = Type2StateProofManagerToml(
+ disabled = false,
+ endpoints = listOf("http://shomei-frontend-i1:8888/".toURL(), "http://shomei-frontend-i2:8888/".toURL()),
+ l1QueryBlockTag = BlockParameter.Tag.SAFE,
+ l1PollingInterval = 12.seconds,
+ requestRetries = RequestRetriesToml(
+ maxRetries = 3u,
+ backoffDelay = 1.seconds,
+ failuresWarningThreshold = 2u,
+ ),
+ )
+
+ val tomlMinimal = """
+ [type2-state-proof-provider]
+ endpoints = ["http://shomei-frontend-i1:8888/", "http://shomei-frontend-i2:8888/"]
+ """.trimIndent()
+
+ val configMinimal = Type2StateProofManagerToml(
+ disabled = false,
+ endpoints = listOf("http://shomei-frontend-i1:8888/".toURL(), "http://shomei-frontend-i2:8888/".toURL()),
+ l1QueryBlockTag = BlockParameter.Tag.FINALIZED,
+ l1PollingInterval = 6.seconds,
+ requestRetries = RequestRetriesToml(
+ maxRetries = null,
+ backoffDelay = 1.seconds,
+ failuresWarningThreshold = 3u,
+ ),
+ )
+ }
+
+ data class WrapperConfig(
+ val type2StateProofProvider: Type2StateProofManagerToml,
+ )
+
+ @Test
+ fun `should parse type2-state-proof-provider full config`() {
+ assertThat(parseConfig(toml).type2StateProofProvider)
+ .isEqualTo(config)
+ }
+
+ @Test
+ fun `should parse type2-state-proof-provider minimal config`() {
+ assertThat(parseConfig(tomlMinimal).type2StateProofProvider)
+ .isEqualTo(configMinimal)
+ }
+}
diff --git a/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/toml/decoder/BlockParameterDecoderTest.kt b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/toml/decoder/BlockParameterDecoderTest.kt
new file mode 100644
index 0000000000..857bbbfed1
--- /dev/null
+++ b/coordinator/app/src/test/kotlin/linea/coordinator/config/v2/toml/decoder/BlockParameterDecoderTest.kt
@@ -0,0 +1,63 @@
+package linea.coordinator.config.v2.toml.decoder
+
+import linea.coordinator.config.v2.toml.parseConfig
+import linea.domain.BlockParameter
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Disabled
+import org.junit.jupiter.api.Test
+
+class BlockParameterDecoderTest {
+ @Test
+ fun `should decode block parameter tag`() {
+ data class ConfigTomTag(val blockParameter: BlockParameter.Tag)
+
+ assertThat(parseConfig("""block-parameter = "latest" """))
+ .isEqualTo(ConfigTomTag(BlockParameter.Tag.LATEST))
+ }
+
+ @Test
+ fun `should decode block parameter tag nullable`() {
+ data class ConfigTomTag(val blockParameter: BlockParameter.Tag? = null)
+
+ assertThat(parseConfig("""block-parameter = "latest" """))
+ .isEqualTo(ConfigTomTag(BlockParameter.Tag.LATEST))
+
+ assertThat(parseConfig(""" """))
+ .isEqualTo(ConfigTomTag(null))
+ }
+
+ @Test
+ fun `should decode block parameter number`() {
+ data class ConfigTomTag(val blockParameter: BlockParameter.BlockNumber)
+
+ assertThat(parseConfig("""block-parameter = 2_000 """))
+ .isEqualTo(ConfigTomTag(BlockParameter.BlockNumber(2_000UL)))
+ }
+
+ @Disabled("fails with Cannot cast java.lang.Long to linea.domain.BlockParameter.BlockNumber")
+ fun `should decode block parameter number nullable`() {
+ data class ConfigTomTag(val blockParameter: BlockParameter.BlockNumber? = null)
+
+ assertThat(parseConfig("""block-parameter = 2_000 """))
+ .isEqualTo(ConfigTomTag(BlockParameter.BlockNumber(2_000UL)))
+
+ assertThat(parseConfig(""" """))
+ .isEqualTo(ConfigTomTag(null))
+ }
+
+ @Test
+ fun `should decode block parameter generic type with tag set`() {
+ data class ConfigTomTag(val blockParameter: BlockParameter)
+
+ assertThat(parseConfig("""block-parameter = "latest" """))
+ .isEqualTo(ConfigTomTag(BlockParameter.Tag.LATEST))
+ }
+
+ @Test
+ fun `should decode block parameter generic type with number set`() {
+ data class ConfigTomTag(val blockParameter: BlockParameter)
+
+ assertThat(parseConfig("""block-parameter = 2_000 """))
+ .isEqualTo(ConfigTomTag(BlockParameter.BlockNumber(2_000UL)))
+ }
+}
diff --git a/coordinator/app/src/test/kotlin/net/consensys/zkevm/coordinator/app/config/CoordinatorConfigTest.kt b/coordinator/app/src/test/kotlin/net/consensys/zkevm/coordinator/app/config/CoordinatorConfigTest.kt
deleted file mode 100644
index f15f2d7b12..0000000000
--- a/coordinator/app/src/test/kotlin/net/consensys/zkevm/coordinator/app/config/CoordinatorConfigTest.kt
+++ /dev/null
@@ -1,558 +0,0 @@
-package net.consensys.zkevm.coordinator.app.config
-
-import com.github.michaelbull.result.getError
-import com.sksamuel.hoplite.Masked
-import linea.blob.BlobCompressorVersion
-import linea.coordinator.config.loadConfigs
-import linea.coordinator.config.loadConfigsOrError
-import linea.domain.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.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
-import org.assertj.core.api.Assertions.assertThat
-import org.junit.jupiter.api.Assertions.assertEquals
-import org.junit.jupiter.api.Test
-import org.junit.jupiter.api.assertDoesNotThrow
-import org.junit.jupiter.api.assertThrows
-import java.math.BigInteger
-import java.net.URI
-import java.nio.file.Path
-import java.nio.file.Paths
-import java.time.Duration
-import kotlin.time.Duration.Companion.minutes
-import kotlin.time.Duration.Companion.seconds
-import kotlin.time.toJavaDuration
-
-class CoordinatorConfigTest {
- companion object {
- private val apiConfig = ApiConfig(9545U)
- private val conflationConfig = ConflationConfig(
- consistentNumberOfBlocksOnL1ToWait = 1,
- conflationDeadline = Duration.parse("PT6S"),
- conflationDeadlineCheckInterval = Duration.parse("PT3S"),
- conflationDeadlineLastBlockConfirmationDelay = Duration.parse("PT2S"),
- blocksLimit = 2,
- _tracesLimitsV2 = expectedTracesLimitsV2,
- _smartContractErrors = mapOf(
- // L1 Linea Rollup
- "0f06cd15" to "DataAlreadySubmitted",
- "c01eab56" to "EmptySubmissionData",
- ),
- fetchBlocksLimit = 4000,
- )
-
- private val proversConfig = ProversConfig(
- proverA = ProverConfig(
- execution = FileBasedProverConfig(
- requestsDirectory = Path.of("/data/prover/v2/execution/requests"),
- responsesDirectory = Path.of("/data/prover/v2/execution/responses"),
- pollingInterval = 1.seconds,
- pollingTimeout = 10.minutes,
- inprogressProvingSuffixPattern = ".*\\.inprogress\\.prover.*",
- inprogressRequestWritingSuffix = ".inprogress_coordinator_writing",
- ),
- blobCompression = FileBasedProverConfig(
- requestsDirectory = Path.of("/data/prover/v2/compression/requests"),
- responsesDirectory = Path.of("/data/prover/v2/compression/responses"),
- pollingInterval = 1.seconds,
- pollingTimeout = 10.minutes,
- inprogressProvingSuffixPattern = ".*\\.inprogress\\.prover.*",
- inprogressRequestWritingSuffix = ".inprogress_coordinator_writing",
- ),
- proofAggregation = FileBasedProverConfig(
- requestsDirectory = Path.of("/data/prover/v2/aggregation/requests"),
- responsesDirectory = Path.of("/data/prover/v2/aggregation/responses"),
- pollingInterval = 1.seconds,
- pollingTimeout = 10.minutes,
- inprogressProvingSuffixPattern = ".*\\.inprogress\\.prover.*",
- inprogressRequestWritingSuffix = ".inprogress_coordinator_writing",
- ),
- ),
- switchBlockNumberInclusive = null,
- proverB = null,
- )
-
- private val blobCompressionConfig = BlobCompressionConfig(
- blobSizeLimit = 100 * 1024,
- handlerPollingInterval = Duration.parse("PT1S"),
- _batchesLimit = 1,
- )
-
- private val aggregationConfig = AggregationConfig(
- aggregationProofsLimit = 3,
- aggregationDeadline = Duration.parse("PT10S"),
- aggregationCoordinatorPollingInterval = Duration.parse("PT2S"),
- deadlineCheckInterval = Duration.parse("PT8S"),
- )
-
- private val tracesConfig = TracesConfig(
- blobCompressorVersion = BlobCompressorVersion.V1_2,
- rawExecutionTracesVersion = "0.2.0",
- expectedTracesApiVersionV2 = "v0.8.0-rc8",
- conflationV2 = TracesConfig.FunctionalityEndpoint(
- endpoints = listOf(URI("http://traces-node:8545/").toURL()),
- requestLimitPerEndpoint = 1U,
- requestRetry = RequestRetryConfigTomlFriendly(
- backoffDelay = Duration.parse("PT1S"),
- failuresWarningThreshold = 2,
- ),
- ),
- countersV2 = TracesConfig.FunctionalityEndpoint(
- endpoints = listOf(URI("http://traces-node:8545/").toURL()),
- requestLimitPerEndpoint = 1U,
- requestRetry = RequestRetryConfigTomlFriendly(
- backoffDelay = Duration.parse("PT1S"),
- failuresWarningThreshold = 2,
- ),
- ),
- )
-
- private val type2StateProofProviderConfig = Type2StateProofProviderConfig(
- endpoints = listOf(URI("http://shomei-frontend:8888/").toURL()),
- requestRetry = RequestRetryConfigTomlFriendly(
- backoffDelay = Duration.parse("PT1S"),
- failuresWarningThreshold = 2,
- ),
- l1QueryBlockTag = BlockParameter.Tag.SAFE,
- l1PollingInterval = Duration.parse("PT6S"),
- )
- private val stateManagerConfig = StateManagerClientConfig(
- version = "2.3.0",
- endpoints = listOf(
- URI("http://shomei:8888/").toURL(),
- ),
- requestLimitPerEndpoint = 2U,
- requestRetry = RequestRetryConfigTomlFriendly(
- backoffDelay = Duration.parse("PT2S"),
- failuresWarningThreshold = 2,
- ),
- )
-
- private val blobSubmissionConfig = BlobSubmissionConfig(
- dbPollingInterval = Duration.parse("PT1S"),
- maxBlobsToReturn = 100,
- maxBlobsToSubmitPerTick = 10,
- priorityFeePerGasUpperBound = 2000000000UL,
- priorityFeePerGasLowerBound = 200000000UL,
- proofSubmissionDelay = Duration.parse("PT1S"),
- targetBlobsToSendPerTransaction = 9,
- useEthEstimateGas = false,
- disabled = false,
- )
-
- private val aggregationFinalizationConfig = AggregationFinalizationConfig(
- dbPollingInterval = Duration.parse("PT1S"),
- maxAggregationsToFinalizePerTick = 1,
- proofSubmissionDelay = Duration.parse("PT1S"),
- useEthEstimateGas = true,
- disabled = false,
- )
-
- private val databaseConfig = DatabaseConfig(
- host = "postgres",
- port = 5432,
- username = "postgres",
- password = Masked("postgres"),
- schema = "linea_coordinator",
- readPoolSize = 10,
- readPipeliningLimit = 10,
- transactionalPoolSize = 10,
- )
-
- private val persistenceRetryConfig = PersistenceRetryConfig(
- maxRetries = null,
- backoffDelay = Duration.parse("PT1S"),
- )
-
- private val l1Config = L1Config(
- zkEvmContractAddress = "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9",
- rpcEndpoint = URI("http://l1-el-node:8545").toURL(),
- finalizationPollingInterval = Duration.parse("PT6S"),
- _l1QueryBlockTag = BlockParameter.Tag.LATEST.getTag(),
- gasLimit = 10000000UL,
- feeHistoryBlockCount = 10,
- feeHistoryRewardPercentile = 15.0,
- maxFeePerGasCap = 100000000000UL,
- maxFeePerBlobGasCap = 100000000000UL,
- maxPriorityFeePerGasCap = 20000000000UL,
- gasPriceCapMultiplierForFinalization = 2.0,
- earliestBlock = BigInteger.ZERO,
- sendMessageEventPollingInterval = Duration.parse("PT1S"),
- maxEventScrapingTime = Duration.parse("PT5S"),
- maxMessagesToCollect = 1000U,
- finalizedBlockTag = "latest",
- blockTime = Duration.parse("PT1S"),
- blockRangeLoopLimit = 500U,
- _ethFeeHistoryEndpoint = null,
- _genesisStateRootHash = "0x072ead6777750dc20232d1cee8dc9a395c2d350df4bbaa5096c6f59b214dcecd",
- _genesisShnarfV6 = "0x47452a1b9ebadfe02bdd02f580fa1eba17680d57eec968a591644d05d78ee84f",
- )
-
- private val l2Config = L2Config(
- messageServiceAddress = "0xe537D669CA013d86EBeF1D64e40fC74CADC91987",
- rpcEndpoint = URI("http://sequencer:8545").toURL(),
- gasLimit = 10000000u,
- maxFeePerGasCap = 100000000000u,
- feeHistoryBlockCount = 4U,
- feeHistoryRewardPercentile = 15.0,
- blocksToFinalization = 0U,
- lastHashSearchWindow = 25U,
- anchoringReceiptPollingInterval = Duration.parse("PT01S"),
- maxReceiptRetries = 120U,
- newBlockPollingInterval = Duration.parse("PT1S"),
- )
-
- private val finalizationSigner = SignerConfig(
- type = SignerConfig.Type.Web3j,
- web3signer = Web3SignerConfig(
- endpoint = "http://web3signer:9000",
- maxPoolSize = 10U,
- keepAlive = true,
- publicKey =
- "ba5734d8f7091719471e7f7ed6b9df170dc70cc661ca05e688601ad984f068b0d67351e5f06073092499336ab0839ef8a521afd334e5" +
- "3807205fa2f08eec74f4",
- ),
- web3j = Web3jConfig(Masked("0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d")),
- )
-
- private val dataSubmissionSigner = SignerConfig(
- type = SignerConfig.Type.Web3j,
- web3signer = Web3SignerConfig(
- endpoint = "http://web3signer:9000",
- maxPoolSize = 10U,
- keepAlive = true,
- publicKey =
- "9d9031e97dd78ff8c15aa86939de9b1e791066a0224e331bc962a2099a7b1f0464b8bbafe1535f2301c72c2cb3535b172da30b02686a" +
- "b0393d348614f157fbdb",
- ),
- web3j = Web3jConfig(Masked("0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a")),
- )
- private val l2SignerConfig = SignerConfig(
- type = SignerConfig.Type.Web3j,
- web3signer = Web3SignerConfig(
- endpoint = "http://web3signer:9000",
- maxPoolSize = 10U,
- keepAlive = true,
- publicKey =
- "4a788ad6fa008beed58de6418369717d7492f37d173d70e2c26d9737e2c6eeae929452ef8602a19410844db3e200a0e73f5208fd7625" +
- "9a8766b73953fc3e7023",
- ),
- web3j = Web3jConfig(Masked("0x4d01ae6487860981699236a58b68f807ee5f17b12df5740b85cf4c4653be0f55")),
- )
-
- 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,
- ),
- 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,
- ),
- jsonRpcGasPriceUpdaterConfig = GasPriceUpdaterImpl.Config(
- gethEndpoints = listOf(
- URI("http://l2-node:8545/").toURL(),
- ),
- besuEndPoints = listOf(),
- 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("http://sequencer:8545/").toURL(),
- retryConfig = l2NetworkGasPricingRequestRetryConfig,
- ),
- )
-
- private val l1DynamicGasPriceCapServiceConfig = L1DynamicGasPriceCapServiceConfig(
- disabled = false,
- gasPriceCapCalculation = L1DynamicGasPriceCapServiceConfig.GasPriceCapCalculation(
- adjustmentConstant = 25U,
- blobAdjustmentConstant = 25U,
- finalizationTargetMaxDelay = Duration.parse("PT30S"),
- gasFeePercentileWindow = Duration.parse("PT1M"),
- gasFeePercentileWindowLeeway = Duration.parse("PT10S"),
- gasFeePercentile = 10.0,
- gasPriceCapsCheckCoefficient = 0.9,
- historicBaseFeePerBlobGasLowerBound = 100_000_000u,
- historicAvgRewardConstant = 100_000_000u,
- timeOfDayMultipliers = expectedTimeOfDayMultipliers,
- ),
- feeHistoryFetcher = L1DynamicGasPriceCapServiceConfig.FeeHistoryFetcher(
- fetchInterval = Duration.parse("PT1S"),
- maxBlockCount = 1000U,
- rewardPercentiles = listOf(10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0),
- numOfBlocksBeforeLatest = 4U,
- endpoint = null,
- ),
- feeHistoryStorage = L1DynamicGasPriceCapServiceConfig.FeeHistoryStorage(
- storagePeriod = Duration.parse("PT2M"),
- ),
- )
-
- private val coordinatorConfig = CoordinatorConfig(
- blobCompression = blobCompressionConfig,
- proofAggregation = aggregationConfig,
- traces = tracesConfig,
- type2StateProofProvider = type2StateProofProviderConfig,
- l1 = l1Config,
- l2 = l2Config,
- finalizationSigner = finalizationSigner,
- dataSubmissionSigner = dataSubmissionSigner,
- blobSubmission = blobSubmissionConfig,
- aggregationFinalization = aggregationFinalizationConfig,
- database = databaseConfig,
- persistenceRetry = persistenceRetryConfig,
- stateManager = stateManagerConfig,
- conflation = conflationConfig,
- api = apiConfig,
- l2Signer = l2SignerConfig,
- messageAnchoring = MessageAnchoringConfigTomlDto().reified(
- l1DefaultEndpoint = l1Config.rpcEndpoint,
- l2DefaultEndpoint = l2Config.rpcEndpoint,
- ),
- l2NetworkGasPricingService = l2NetworkGasPricingServiceConfig,
- l1DynamicGasPriceCapService = l1DynamicGasPriceCapServiceConfig,
- proversConfig = proversConfig,
- )
- }
-
- private data class TestConfig(val extraField: String)
-
- @Test
- fun `should keep local stack testing configs uptodate with the code`() {
- // Just assert that Files have been loaded and parsed correctly
- // This is to prevent Code changes in coordinator and forgetting to update config files used in the local stack
- loadConfigs(
- coordinatorConfigFiles = listOf(
- Path.of("../../config/coordinator/coordinator-docker.config.toml"),
- Path.of("../../config/coordinator/coordinator-docker-traces-v2-override.config.toml"),
- Path.of("../../config/coordinator/coordinator-docker-web3signer-override.config.toml"),
- Path.of("../../config/coordinator/coordinator-local-dev.config.overrides.toml"),
- Path.of("../../config/coordinator/coordinator-local-dev.config-traces-v2.overrides.toml"),
- ),
- tracesLimitsFileV2 = Path.of("../../config/common/traces-limits-v2.toml"),
- gasPriceCapTimeOfDayMultipliersFile = Path.of("../../config/common/gas-price-cap-time-of-day-multipliers.toml"),
- smartContractErrorsFile = Path.of("../../config/common/smart-contract-errors.toml"),
- )
- }
-
- private fun pathToResource(resource: String): Path {
- return Paths.get(
- this::class.java.classLoader.getResource(resource)?.toURI()
- ?: error("Resource not found: $resource"),
- )
- }
-
- @Test
- fun `should parse and consolidate configs`() {
- val configs = loadConfigs(
- coordinatorConfigFiles = listOf(pathToResource("configs/coordinator.config.toml")),
- tracesLimitsFileV2 = pathToResource("configs/traces-limits-v2.toml"),
- gasPriceCapTimeOfDayMultipliersFile = pathToResource("configs/gas-price-cap-time-of-day-multipliers.toml"),
- smartContractErrorsFile = pathToResource("configs/smart-contract-errors.toml"),
- )
-
- assertEquals(coordinatorConfig, configs)
- assertEquals(coordinatorConfig.l1.rpcEndpoint, coordinatorConfig.l1.ethFeeHistoryEndpoint)
- }
-
- @Test
- fun parsesValidWeb3SignerConfigOverride() {
- val config = loadConfigs(
- coordinatorConfigFiles = listOf(
- pathToResource("configs/coordinator.config.toml"),
- pathToResource("configs/coordinator-web3signer-override.config.toml"),
- ),
- tracesLimitsFileV2 = pathToResource("configs/traces-limits-v2.toml"),
- gasPriceCapTimeOfDayMultipliersFile = pathToResource("configs/gas-price-cap-time-of-day-multipliers.toml"),
- smartContractErrorsFile = pathToResource("configs/smart-contract-errors.toml"),
- )
-
- val expectedConfig =
- coordinatorConfig.copy(
- finalizationSigner = finalizationSigner.copy(type = SignerConfig.Type.Web3Signer),
- dataSubmissionSigner = dataSubmissionSigner.copy(type = SignerConfig.Type.Web3Signer),
- l2Signer = l2SignerConfig.copy(type = SignerConfig.Type.Web3Signer),
- )
-
- assertThat(config).isEqualTo(expectedConfig)
- }
-
- @Test
- fun parsesValidTracesV2ConfigOverride() {
- val config = loadConfigs(
- coordinatorConfigFiles = listOf(
- pathToResource("configs/coordinator.config.toml"),
- pathToResource("configs/coordinator-traces-v2-override.config.toml"),
- ),
- tracesLimitsFileV2 = pathToResource("configs/traces-limits-v2.toml"),
- gasPriceCapTimeOfDayMultipliersFile = pathToResource("configs/gas-price-cap-time-of-day-multipliers.toml"),
- smartContractErrorsFile = pathToResource("configs/smart-contract-errors.toml"),
- )
-
- val expectedConfig =
- coordinatorConfig.copy(
- l2NetworkGasPricingService = l2NetworkGasPricingServiceConfig.copy(
- legacy =
- l2NetworkGasPricingServiceConfig.legacy.copy(
- transactionCostCalculatorConfig =
- l2NetworkGasPricingServiceConfig.legacy.transactionCostCalculatorConfig?.copy(
- compressedTxSize = 350,
- expectedGas = 29400,
- ),
- ),
- ),
- traces = tracesConfig.copy(
- blobCompressorVersion = BlobCompressorVersion.V1_2,
- expectedTracesApiVersionV2 = "v0.8.0-rc8",
- conflationV2 = tracesConfig.conflationV2,
- countersV2 = tracesConfig.countersV2,
- ),
- proversConfig = proversConfig.copy(
- proverA = proversConfig.proverA.copy(
- execution = proversConfig.proverA.execution.copy(
- requestsDirectory = Path.of("/data/prover/v3/execution/requests"),
- responsesDirectory = Path.of("/data/prover/v3/execution/responses"),
- ),
- blobCompression = proversConfig.proverA.blobCompression.copy(
- requestsDirectory = Path.of("/data/prover/v3/compression/requests"),
- responsesDirectory = Path.of("/data/prover/v3/compression/responses"),
- ),
- proofAggregation = proversConfig.proverA.proofAggregation.copy(
- requestsDirectory = Path.of("/data/prover/v3/aggregation/requests"),
- responsesDirectory = Path.of("/data/prover/v3/aggregation/responses"),
- ),
- ),
- ),
- messageAnchoring = MessageAnchoringConfigTomlDto().copy(
- l1Endpoint = URI("http://l1-endpoint-for-anchoring:8545").toURL(),
- l2Endpoint = URI("http://l2-endpoint-for-anchoring:8545").toURL(),
- l1HighestBlockTag = BlockParameter.Tag.LATEST,
- l1EventPollingInterval = 1.seconds.toJavaDuration(),
- anchoringTickInterval = 1.seconds.toJavaDuration(),
- l1RequestRetries = RequestRetryConfigTomlFriendly(
- maxRetries = 10,
- failuresWarningThreshold = 1,
- ),
- ).reified(
- l1DefaultEndpoint = l1Config.rpcEndpoint,
- l2DefaultEndpoint = l2Config.rpcEndpoint,
- ),
- )
-
- assertThat(config).isEqualTo(expectedConfig)
- }
-
- @Test
- fun invalidConfigReturnsErrorResult() {
- val configsResult = loadConfigsOrError(
- configFiles = listOf(pathToResource("configs/coordinator.config.toml")),
- )
-
- assertThat(configsResult.getError()).contains("'extraField': Missing String from config")
- }
-
- @Test
- fun testInvalidAggregationByTargetBlockNumberWhenL2InclusiveBlockNumberToStopAndFlushAggregationSpecified() {
- val aggregationConfigWithoutTargetBlockNumber = aggregationConfig.copy(
- _targetEndBlocks = emptyList(),
- )
- val conflationConfigWithTargetBlockNumber = conflationConfig.copy(
- _conflationTargetEndBlockNumbers = listOf(100L),
- )
-
- val exception = assertThrows {
- coordinatorConfig.copy(
- l2InclusiveBlockNumberToStopAndFlushAggregation = 100uL,
- proofAggregation = aggregationConfigWithoutTargetBlockNumber,
- conflation = conflationConfigWithTargetBlockNumber,
- )
- }
- assertThat(exception.message)
- .isEqualTo("proofAggregation.targetEndBlocks should contain the l2InclusiveBlockNumberToStopAndFlushAggregation")
- }
-
- @Test
- fun testInvalidConflationByTargetBlockNumberWhenL2InclusiveBlockNumberToStopAndFlushAggregationSpecified() {
- val aggregationConfigWithTargetBlockNumber = aggregationConfig.copy(
- _targetEndBlocks = listOf(100L),
- )
- val conflationConfigWithoutTargetBlockNumber = conflationConfig.copy(
- _conflationTargetEndBlockNumbers = emptyList(),
- )
-
- val exception = assertThrows {
- coordinatorConfig.copy(
- l2InclusiveBlockNumberToStopAndFlushAggregation = 100uL,
- proofAggregation = aggregationConfigWithTargetBlockNumber,
- conflation = conflationConfigWithoutTargetBlockNumber,
- )
- }
- assertThat(exception.message)
- .isEqualTo(
- "conflation.conflationTargetEndBlockNumbers should contain the " +
- "l2InclusiveBlockNumberToStopAndFlushAggregation",
- )
- }
-
- @Test
- fun testValidAggrAndConflationByTargetBlockNumberWhenL2InclusiveBlockNumberToStopAndFlushAggregationSpecified() {
- val aggregationConfigWithoutSwithBlockNumber = aggregationConfig.copy(
- _targetEndBlocks = listOf(10L, 100L),
- )
- val conflationConfigWithTargetBlockNumber = conflationConfig.copy(
- _conflationTargetEndBlockNumbers = listOf(100L),
- )
-
- assertDoesNotThrow {
- coordinatorConfig.copy(
- l2InclusiveBlockNumberToStopAndFlushAggregation = 100uL,
- proofAggregation = aggregationConfigWithoutSwithBlockNumber,
- conflation = conflationConfigWithTargetBlockNumber,
- )
- }
- }
-}
diff --git a/coordinator/app/src/test/kotlin/net/consensys/zkevm/coordinator/app/config/ExpectedGasPriceMultipliers.kt b/coordinator/app/src/test/kotlin/net/consensys/zkevm/coordinator/app/config/ExpectedGasPriceMultipliers.kt
deleted file mode 100644
index 5913056f9f..0000000000
--- a/coordinator/app/src/test/kotlin/net/consensys/zkevm/coordinator/app/config/ExpectedGasPriceMultipliers.kt
+++ /dev/null
@@ -1,172 +0,0 @@
-package net.consensys.zkevm.coordinator.app.config
-
-val expectedTimeOfDayMultipliers = mapOf(
- "SUNDAY_0" to 1.7489178377946066,
- "SUNDAY_1" to 1.7494632175198737,
- "SUNDAY_2" to 1.75,
- "SUNDAY_3" to 1.733166295438555,
- "SUNDAY_4" to 1.6993775444542885,
- "SUNDAY_5" to 1.6350086618091364,
- "SUNDAY_6" to 1.5627740860151331,
- "SUNDAY_7" to 1.4831149222064164,
- "SUNDAY_8" to 1.4101476768256929,
- "SUNDAY_9" to 1.370085278922007,
- "SUNDAY_10" to 1.3516015544068651,
- "SUNDAY_11" to 1.3482404546676368,
- "SUNDAY_12" to 1.3580905751578942,
- "SUNDAY_13" to 1.3775497419563296,
- "SUNDAY_14" to 1.3700255667542938,
- "SUNDAY_15" to 1.2642948506461285,
- "SUNDAY_16" to 1.2794806131912935,
- "SUNDAY_17" to 1.2750892256476676,
- "SUNDAY_18" to 1.2919720208955585,
- "SUNDAY_19" to 1.317984990098603,
- "SUNDAY_20" to 1.4433501639513178,
- "SUNDAY_21" to 1.4705921238901998,
- "SUNDAY_22" to 1.515043370430801,
- "SUNDAY_23" to 1.5556742617266397,
- "MONDAY_0" to 1.5381562278760164,
- "MONDAY_1" to 1.5423761828433993,
- "MONDAY_2" to 1.539015963719092,
- "MONDAY_3" to 1.487676153648977,
- "MONDAY_4" to 1.430973985132037,
- "MONDAY_5" to 1.4656765439056292,
- "MONDAY_6" to 1.4484298622828233,
- "MONDAY_7" to 1.4459076216659752,
- "MONDAY_8" to 1.4899061835032241,
- "MONDAY_9" to 1.5249733712852067,
- "MONDAY_10" to 1.511367489481033,
- "MONDAY_11" to 1.4225695658047797,
- "MONDAY_12" to 1.2887291896624584,
- "MONDAY_13" to 1.1460926897291355,
- "MONDAY_14" to 1.0004897955233254,
- "MONDAY_15" to 0.8694664537368378,
- "MONDAY_16" to 0.8270273375962802,
- "MONDAY_17" to 0.7868289022833883,
- "MONDAY_18" to 0.7780303121746551,
- "MONDAY_19" to 0.7756215256634205,
- "MONDAY_20" to 0.7984895728860915,
- "MONDAY_21" to 0.8918589268832423,
- "MONDAY_22" to 0.9967716668541272,
- "MONDAY_23" to 1.0973334887144106,
- "TUESDAY_0" to 1.2233064209957951,
- "TUESDAY_1" to 1.3238883432855082,
- "TUESDAY_2" to 1.3874518307497257,
- "TUESDAY_3" to 1.463621147171298,
- "TUESDAY_4" to 1.4975989065490154,
- "TUESDAY_5" to 1.481679186141442,
- "TUESDAY_6" to 1.452778387763161,
- "TUESDAY_7" to 1.3414858185569951,
- "TUESDAY_8" to 1.2869454637983988,
- "TUESDAY_9" to 1.249347290389873,
- "TUESDAY_10" to 1.196488297386161,
- "TUESDAY_11" to 1.1136140507034202,
- "TUESDAY_12" to 0.9867528660797885,
- "TUESDAY_13" to 0.8018989158195754,
- "TUESDAY_14" to 0.6173048748109258,
- "TUESDAY_15" to 0.46718586671750373,
- "TUESDAY_16" to 0.4103633833041902,
- "TUESDAY_17" to 0.4871260756989506,
- "TUESDAY_18" to 0.5667378483016126,
- "TUESDAY_19" to 0.6464203510900723,
- "TUESDAY_20" to 0.7780268325299871,
- "TUESDAY_21" to 0.8995921101255763,
- "TUESDAY_22" to 1.0077600114996088,
- "TUESDAY_23" to 1.1109769960680498,
- "WEDNESDAY_0" to 1.2097668746150059,
- "WEDNESDAY_1" to 1.2631002319009361,
- "WEDNESDAY_2" to 1.2912775191940549,
- "WEDNESDAY_3" to 1.3229785939630059,
- "WEDNESDAY_4" to 1.3428607301494424,
- "WEDNESDAY_5" to 1.3750788517823973,
- "WEDNESDAY_6" to 1.3752344527256497,
- "WEDNESDAY_7" to 1.3505490078766218,
- "WEDNESDAY_8" to 1.2598503219367945,
- "WEDNESDAY_9" to 1.2051668977452374,
- "WEDNESDAY_10" to 1.0320896222195326,
- "WEDNESDAY_11" to 0.8900138031631949,
- "WEDNESDAY_12" to 0.6341155208698448,
- "WEDNESDAY_13" to 0.48337590254714624,
- "WEDNESDAY_14" to 0.2903189399226416,
- "WEDNESDAY_15" to 0.25,
- "WEDNESDAY_16" to 0.25711039485046006,
- "WEDNESDAY_17" to 0.37307641907591793,
- "WEDNESDAY_18" to 0.45280799454961196,
- "WEDNESDAY_19" to 0.5631397823847637,
- "WEDNESDAY_20" to 0.6285005244224133,
- "WEDNESDAY_21" to 0.6671897537279405,
- "WEDNESDAY_22" to 0.7268406397452634,
- "WEDNESDAY_23" to 0.8068904097486369,
- "THURSDAY_0" to 0.9021601102971811,
- "THURSDAY_1" to 1.023741688964238,
- "THURSDAY_2" to 1.1340689935096755,
- "THURSDAY_3" to 1.2530130345819006,
- "THURSDAY_4" to 1.3163421664973542,
- "THURSDAY_5" to 1.3536343767230727,
- "THURSDAY_6" to 1.3432290485306728,
- "THURSDAY_7" to 1.2864983218982178,
- "THURSDAY_8" to 1.2320488534113174,
- "THURSDAY_9" to 1.1984530721079034,
- "THURSDAY_10" to 1.0877338251341975,
- "THURSDAY_11" to 0.9999324929016475,
- "THURSDAY_12" to 0.87536726762619,
- "THURSDAY_13" to 0.6560822412167919,
- "THURSDAY_14" to 0.44836474861432074,
- "THURSDAY_15" to 0.36145134935025247,
- "THURSDAY_16" to 0.2695997829759713,
- "THURSDAY_17" to 0.2898426312618241,
- "THURSDAY_18" to 0.3970093434340387,
- "THURSDAY_19" to 0.5193273246848977,
- "THURSDAY_20" to 0.6426415257034419,
- "THURSDAY_21" to 0.800685718218497,
- "THURSDAY_22" to 0.9215516833839711,
- "THURSDAY_23" to 1.053701659160912,
- "FRIDAY_0" to 1.149649788723893,
- "FRIDAY_1" to 1.2046315447861193,
- "FRIDAY_2" to 1.2724031281576726,
- "FRIDAY_3" to 1.3525693456352732,
- "FRIDAY_4" to 1.3746126314960814,
- "FRIDAY_5" to 1.3744591862592468,
- "FRIDAY_6" to 1.3297812543035683,
- "FRIDAY_7" to 1.2762064429631657,
- "FRIDAY_8" to 1.235662409263294,
- "FRIDAY_9" to 1.2171558028785991,
- "FRIDAY_10" to 1.182722399785398,
- "FRIDAY_11" to 1.137345538963285,
- "FRIDAY_12" to 0.9999308422620752,
- "FRIDAY_13" to 0.8055000309055653,
- "FRIDAY_14" to 0.5667135273493851,
- "FRIDAY_15" to 0.4081529603000651,
- "FRIDAY_16" to 0.3987031354907009,
- "FRIDAY_17" to 0.5030075499003412,
- "FRIDAY_18" to 0.6518159532641841,
- "FRIDAY_19" to 0.8733483414970974,
- "FRIDAY_20" to 1.0496224913080463,
- "FRIDAY_21" to 1.1820684558591705,
- "FRIDAY_22" to 1.2561688567574458,
- "FRIDAY_23" to 1.3204704912328773,
- "SATURDAY_0" to 1.3832230236620218,
- "SATURDAY_1" to 1.4632908341022142,
- "SATURDAY_2" to 1.5019230781315296,
- "SATURDAY_3" to 1.5437332506007084,
- "SATURDAY_4" to 1.5934153179751855,
- "SATURDAY_5" to 1.6245578072557723,
- "SATURDAY_6" to 1.6294919789890665,
- "SATURDAY_7" to 1.6027665451672717,
- "SATURDAY_8" to 1.6068061069158674,
- "SATURDAY_9" to 1.624257927970777,
- "SATURDAY_10" to 1.5996112411089,
- "SATURDAY_11" to 1.5659672993092648,
- "SATURDAY_12" to 1.5333537902522736,
- "SATURDAY_13" to 1.445292929996356,
- "SATURDAY_14" to 1.2966021477035259,
- "SATURDAY_15" to 1.250999408961155,
- "SATURDAY_16" to 1.2535364828163025,
- "SATURDAY_17" to 1.2736456128871074,
- "SATURDAY_18" to 1.3348268054897328,
- "SATURDAY_19" to 1.4571388900094875,
- "SATURDAY_20" to 1.5073787902995706,
- "SATURDAY_21" to 1.5605139580010123,
- "SATURDAY_22" to 1.5885303316932382,
- "SATURDAY_23" to 1.6169891066719597,
-)
diff --git a/coordinator/app/src/test/kotlin/net/consensys/zkevm/coordinator/app/config/ExpectedTracesLimitsV2.kt b/coordinator/app/src/test/kotlin/net/consensys/zkevm/coordinator/app/config/ExpectedTracesLimitsV2.kt
deleted file mode 100644
index f463c525f4..0000000000
--- a/coordinator/app/src/test/kotlin/net/consensys/zkevm/coordinator/app/config/ExpectedTracesLimitsV2.kt
+++ /dev/null
@@ -1,60 +0,0 @@
-package net.consensys.zkevm.coordinator.app.config
-
-import net.consensys.linea.traces.TracesCountersV2
-import net.consensys.linea.traces.TracingModuleV2
-
-val expectedTracesLimitsV2 = TracesCountersV2(
- mapOf(
- TracingModuleV2.ADD to 1u,
- TracingModuleV2.BIN to 2u,
- TracingModuleV2.BLAKE_MODEXP_DATA to 3u,
- TracingModuleV2.BLOCK_DATA to 4u,
- TracingModuleV2.BLOCK_HASH to 5u,
- TracingModuleV2.EC_DATA to 6u,
- TracingModuleV2.EUC to 7u,
- TracingModuleV2.EXP to 8u,
- TracingModuleV2.EXT to 9u,
- TracingModuleV2.GAS to 10u,
- TracingModuleV2.HUB to 11u,
- TracingModuleV2.LOG_DATA to 12u,
- TracingModuleV2.LOG_INFO to 13u,
- TracingModuleV2.MMIO to 14u,
- TracingModuleV2.MMU to 15u,
- TracingModuleV2.MOD to 16u,
- TracingModuleV2.MUL to 18u,
- TracingModuleV2.MXP to 19u,
- TracingModuleV2.OOB to 20u,
- TracingModuleV2.RLP_ADDR to 21u,
- TracingModuleV2.RLP_TXN to 22u,
- TracingModuleV2.RLP_TXN_RCPT to 23u,
- TracingModuleV2.ROM to 24u,
- TracingModuleV2.ROM_LEX to 25u,
- TracingModuleV2.SHAKIRA_DATA to 26u,
- TracingModuleV2.SHF to 27u,
- TracingModuleV2.STP to 28u,
- TracingModuleV2.TRM to 29u,
- TracingModuleV2.TXN_DATA to 30u,
- TracingModuleV2.WCP to 31u,
- // Reference table limits, set to UInt.MAX_VALUE
- TracingModuleV2.BIN_REFERENCE_TABLE to 32u,
- TracingModuleV2.INSTRUCTION_DECODER to 33u,
- TracingModuleV2.SHF_REFERENCE_TABLE to 34u,
- // Precompiles limits
- TracingModuleV2.PRECOMPILE_BLAKE_EFFECTIVE_CALLS to 35u,
- TracingModuleV2.PRECOMPILE_BLAKE_ROUNDS to 36u,
- TracingModuleV2.PRECOMPILE_ECADD_EFFECTIVE_CALLS to 37u,
- TracingModuleV2.PRECOMPILE_ECMUL_EFFECTIVE_CALLS to 38u,
- TracingModuleV2.PRECOMPILE_ECPAIRING_FINAL_EXPONENTIATIONS to 39u,
- TracingModuleV2.PRECOMPILE_ECPAIRING_G2_MEMBERSHIP_CALLS to 40u,
- TracingModuleV2.PRECOMPILE_ECPAIRING_MILLER_LOOPS to 41u,
- TracingModuleV2.PRECOMPILE_ECRECOVER_EFFECTIVE_CALLS to 42u,
- TracingModuleV2.PRECOMPILE_MODEXP_EFFECTIVE_CALLS to 43u,
- TracingModuleV2.PRECOMPILE_RIPEMD_BLOCKS to 44u,
- TracingModuleV2.PRECOMPILE_SHA2_BLOCKS to 45u,
- // Block limits
- TracingModuleV2.BLOCK_KECCAK to 46u,
- TracingModuleV2.BLOCK_L1_SIZE to 47u,
- TracingModuleV2.BLOCK_L2_L1_LOGS to 48u,
- TracingModuleV2.BLOCK_TRANSACTIONS to 49u,
- ),
-)
diff --git a/coordinator/app/src/test/kotlin/net/consensys/zkevm/coordinator/app/config/L2NetworkGasPricingConfigTest.kt b/coordinator/app/src/test/kotlin/net/consensys/zkevm/coordinator/app/config/L2NetworkGasPricingConfigTest.kt
deleted file mode 100644
index 08f87d42b1..0000000000
--- a/coordinator/app/src/test/kotlin/net/consensys/zkevm/coordinator/app/config/L2NetworkGasPricingConfigTest.kt
+++ /dev/null
@@ -1,548 +0,0 @@
-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().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,
- ),
- 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("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,
- ),
- 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("http://sequencer:8545/").toURL(),
- retryConfig = l2NetworkGasPricingRequestretryConfig,
- ),
- ),
- )
- }
-
- @Test
- fun `Undefined json rpc pricing propagation reification is correct`() {
- val undefinedJsonRpcPropagation = """
- [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
- """.trimIndent()
- val config = parseConfig(undefinedJsonRpcPropagation).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,
- ),
- 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 = 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("http://sequencer:8545/").toURL(),
- retryConfig = l2NetworkGasPricingRequestretryConfig,
- ),
- ),
- )
- }
-
- @Test
- fun `Json rpc pricing propagation can be disabled without complete removal`() {
- val disabledJsonRpcPricingPropagation = """
- [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]
- disabled = true
- geth-gas-price-update-recipients = []
- besu-gas-price-update-recipients = []
- """.trimIndent()
- val config = parseConfig(disabledJsonRpcPricingPropagation).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,
- ),
- 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 = 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("http://sequencer:8545/").toURL(),
- retryConfig = l2NetworkGasPricingRequestretryConfig,
- ),
- ),
- )
- }
-}
diff --git a/coordinator/app/src/test/kotlin/net/consensys/zkevm/coordinator/app/config/MessageAnchoringConfigTest.kt b/coordinator/app/src/test/kotlin/net/consensys/zkevm/coordinator/app/config/MessageAnchoringConfigTest.kt
deleted file mode 100644
index d87caa2a70..0000000000
--- a/coordinator/app/src/test/kotlin/net/consensys/zkevm/coordinator/app/config/MessageAnchoringConfigTest.kt
+++ /dev/null
@@ -1,132 +0,0 @@
-package net.consensys.zkevm.coordinator.app.config
-
-import com.sksamuel.hoplite.ConfigLoaderBuilder
-import com.sksamuel.hoplite.toml.TomlPropertySource
-import linea.domain.BlockParameter
-import linea.domain.RetryConfig
-import org.assertj.core.api.Assertions.assertThat
-import org.junit.jupiter.api.Test
-import java.net.URI
-import kotlin.time.Duration.Companion.milliseconds
-import kotlin.time.Duration.Companion.seconds
-
-class MessageAnchoringConfigTest {
- private val l1DefaultEndpoint = URI("http://l1-default-rpc-endpoint:8545").toURL()
- private val l2DefaultEndpoint = URI("http://l2-default-rpc-endpoint:8545").toURL()
- data class Config(
- val messageAnchoring: MessageAnchoringConfigTomlDto = MessageAnchoringConfigTomlDto(),
- )
-
- private fun parseConfig(toml: String): MessageAnchoringConfig {
- return ConfigLoaderBuilder
- .default()
- .addDecoder(BlockParameterDecoder())
- .addSource(TomlPropertySource(toml))
- .build()
- .loadConfigOrThrow()
- .let {
- it.messageAnchoring.reified(
- l1DefaultEndpoint = l1DefaultEndpoint,
- l2DefaultEndpoint = l2DefaultEndpoint,
- )
- }
- }
-
- @Test
- fun `should parse message anchoroing full config`() {
- val toml = """
- [message-anchoring]
- disabled = true
- l1-endpoint = "http://l1-rpc-endpoint:8545"
- l2-endpoint = "http://l2-rpc-endpoint:8545"
- l1-highest-block-tag="FINALIZED"
- l2-highest-block-tag="LATEST"
- l1-event-polling-interval="PT30S"
- l1-event-polling-timeout="PT6S"
- l1-success-backoff-delay="PT0.1s"
- l1-event-search-block-chunk=123
- message-anchoring-chunck-size=123
- anchoring-tick-interval="PT3S"
- message-queue-capacity=321
- maxMessagesToAnchorPerL2Transaction=54
-
- [message-anchoring.l1-request-retries]
- max-retries = 10
- timeout = "PT100S"
- backoff-delay = "PT11S"
- failures-warning-threshold = 1
- [message-anchoring.l2-request-retries]
- max-retries = 20
- timeout = "PT200S"
- backoff-delay = "PT21S"
- failures-warning-threshold = 2
- """.trimIndent()
-
- assertThat(parseConfig(toml))
- .isEqualTo(
- MessageAnchoringConfig(
- disabled = true,
- l1Endpoint = URI("http://l1-rpc-endpoint:8545").toURL(),
- l2Endpoint = URI("http://l2-rpc-endpoint:8545").toURL(),
- l1HighestBlockTag = BlockParameter.Tag.FINALIZED,
- l2HighestBlockTag = BlockParameter.Tag.LATEST,
- l1RequestRetryConfig = RetryConfig(
- maxRetries = 10u,
- timeout = 100.seconds,
- backoffDelay = 11.seconds,
- failuresWarningThreshold = 1u,
- ),
- l2RequestRetryConfig = RetryConfig(
- maxRetries = 20u,
- timeout = 200.seconds,
- backoffDelay = 21.seconds,
- failuresWarningThreshold = 2u,
- ),
- l1EventPollingInterval = 30.seconds,
- l1EventPollingTimeout = 6.seconds,
- l1SuccessBackoffDelay = 100.milliseconds,
- l1EventSearchBlockChunk = 123u,
- anchoringTickInterval = 3.seconds,
- messageQueueCapacity = 321u,
- maxMessagesToAnchorPerL2Transaction = 54u,
- ),
- )
- }
-
- @Test
- fun `should parse message anchoroing with defaults`() {
- val toml = """
- # Nothing configured to return defaults
- """.trimIndent()
-
- assertThat(parseConfig(toml))
- .isEqualTo(
- MessageAnchoringConfig(
- disabled = false,
- l1Endpoint = URI("http://l1-default-rpc-endpoint:8545").toURL(),
- l2Endpoint = URI("http://l2-default-rpc-endpoint:8545").toURL(),
- l1HighestBlockTag = BlockParameter.Tag.FINALIZED,
- l2HighestBlockTag = BlockParameter.Tag.LATEST,
- l1RequestRetryConfig = RetryConfig(
- maxRetries = null,
- timeout = null,
- backoffDelay = 1.seconds,
- failuresWarningThreshold = 3u,
- ),
- l2RequestRetryConfig = RetryConfig(
- maxRetries = null,
- timeout = null,
- backoffDelay = 1.seconds,
- failuresWarningThreshold = 3u,
- ),
- l1EventPollingInterval = 12.seconds,
- l1EventPollingTimeout = 6.seconds,
- l1SuccessBackoffDelay = 1.milliseconds,
- l1EventSearchBlockChunk = 1000u,
- anchoringTickInterval = 2.seconds,
- messageQueueCapacity = 10_000u,
- maxMessagesToAnchorPerL2Transaction = 100u,
- ),
- )
- }
-}
diff --git a/coordinator/app/src/test/kotlin/net/consensys/zkevm/coordinator/app/config/ProverConfigTest.kt b/coordinator/app/src/test/kotlin/net/consensys/zkevm/coordinator/app/config/ProverConfigTest.kt
deleted file mode 100644
index 2bb13d883c..0000000000
--- a/coordinator/app/src/test/kotlin/net/consensys/zkevm/coordinator/app/config/ProverConfigTest.kt
+++ /dev/null
@@ -1,149 +0,0 @@
-package net.consensys.zkevm.coordinator.app.config
-
-import com.sksamuel.hoplite.ConfigLoaderBuilder
-import com.sksamuel.hoplite.toml.TomlPropertySource
-import net.consensys.zkevm.coordinator.clients.prover.FileBasedProverConfig
-import net.consensys.zkevm.coordinator.clients.prover.ProverConfig
-import net.consensys.zkevm.coordinator.clients.prover.ProversConfig
-import org.assertj.core.api.Assertions.assertThat
-import org.junit.jupiter.api.Test
-import java.nio.file.Path
-import kotlin.time.Duration.Companion.minutes
-import kotlin.time.Duration.Companion.seconds
-
-class ProverConfigTest {
- data class Config(
- val prover: ProverConfigTomlDto,
- )
-
- private fun parseConfig(toml: String): ProversConfig {
- return ConfigLoaderBuilder
- .default()
- .addSource(TomlPropertySource(toml))
- .build()
- .loadConfigOrThrow()
- .let { it.prover.reified() }
- }
-
- val proverAConfigToml = """
- [prover]
- fs-inprogress-request-writing-suffix = ".inprogress_coordinator_writing"
- fs-inprogress-proving-suffix-pattern = "\\.inprogress\\.prover.*"
- fs-polling-interval = "PT10S"
- fs-polling-timeout = "PT10M"
- [prover.execution]
- fs-requests-directory = "/data/prover/execution/requests"
- fs-responses-directory = "/data/prover/execution/responses"
- fs-inprogress-request-writing-suffix = ".OVERRIDE_inprogress_coordinator_writing"
- fs-inprogress-proving-suffix-pattern = "OVERRIDE_\\.inprogress\\.prover.*"
- [prover.blob-compression]
- fs-requests-directory = "/data/prover/compression/requests"
- fs-responses-directory = "/data/prover/compression/responses"
- fs-polling-interval = "PT20S"
- fs-polling-timeout = "PT20M"
- [prover.proof-aggregation]
- fs-requests-directory = "/data/prover/aggregation/requests"
- fs-responses-directory = "/data/prover/aggregation/responses"
- """.trimIndent()
-
- @Test
- fun `should load configs with single prover and overrids`() {
- val config = parseConfig(proverAConfigToml)
- assertThat(config.switchBlockNumberInclusive).isNull()
- assertProverAConfig(config.proverA)
- assertThat(config.proverB).isNull()
- }
-
- fun assertProverAConfig(config: ProverConfig) {
- assertThat(config.execution).isEqualTo(
- FileBasedProverConfig(
- requestsDirectory = Path.of("/data/prover/execution/requests"),
- responsesDirectory = Path.of("/data/prover/execution/responses"),
- inprogressRequestWritingSuffix = ".OVERRIDE_inprogress_coordinator_writing",
- inprogressProvingSuffixPattern = "OVERRIDE_\\.inprogress\\.prover.*",
- pollingInterval = 10.seconds,
- pollingTimeout = 10.minutes,
- ),
- )
- assertThat(config.blobCompression).isEqualTo(
- FileBasedProverConfig(
- requestsDirectory = Path.of("/data/prover/compression/requests"),
- responsesDirectory = Path.of("/data/prover/compression/responses"),
- inprogressRequestWritingSuffix = ".inprogress_coordinator_writing",
- inprogressProvingSuffixPattern = "\\.inprogress\\.prover.*",
- pollingInterval = 20.seconds,
- pollingTimeout = 20.minutes,
- ),
- )
- assertThat(config.proofAggregation).isEqualTo(
- FileBasedProverConfig(
- requestsDirectory = Path.of("/data/prover/aggregation/requests"),
- responsesDirectory = Path.of("/data/prover/aggregation/responses"),
- inprogressRequestWritingSuffix = ".inprogress_coordinator_writing",
- inprogressProvingSuffixPattern = "\\.inprogress\\.prover.*",
- pollingInterval = 10.seconds,
- pollingTimeout = 10.minutes,
- ),
- )
- }
-
- @Test
- fun `should load configs with 2 provers and overrides`() {
- val toml = """
- $proverAConfigToml
- [prover.new]
- switch-block-number-inclusive=200
- fs-inprogress-request-writing-suffix = ".NEW_OVERRIDE_inprogress_coordinator_writing"
- fs-polling-timeout = "PT5M"
- [prover.new.execution]
- fs-requests-directory = "/data/prover-new/execution/requests"
- fs-responses-directory = "/data/prover-new/execution/responses"
- fs-inprogress-request-writing-suffix = ".NEW_OVERRIDE_2_inprogress_coordinator_writing"
- fs-inprogress-proving-suffix-pattern = "NEW_OVERRIDE_2\\.inprogress\\.prover.*"
- [prover.new.blob-compression]
- fs-requests-directory = "/data/prover-new/compression/requests"
- fs-responses-directory = "/data/prover-new/compression/responses"
- fs-polling-interval = "PT12S"
- fs-polling-timeout = "PT12M"
- [prover.new.proof-aggregation]
- fs-requests-directory = "/data/prover-new/aggregation/requests"
- fs-responses-directory = "/data/prover-new/aggregation/responses
- "
- """.trimIndent()
- val config = parseConfig(toml)
-
- assertProverAConfig(config.proverA)
- assertThat(config.switchBlockNumberInclusive).isEqualTo(200uL)
- assertThat(config.proverB).isNotNull
- assertThat(config.proverB!!.execution).isEqualTo(
- FileBasedProverConfig(
- requestsDirectory = Path.of("/data/prover-new/execution/requests"),
- responsesDirectory = Path.of("/data/prover-new/execution/responses"),
- inprogressRequestWritingSuffix = ".NEW_OVERRIDE_2_inprogress_coordinator_writing",
- inprogressProvingSuffixPattern = "NEW_OVERRIDE_2\\.inprogress\\.prover.*",
- pollingInterval = 10.seconds,
- pollingTimeout = 5.minutes,
- ),
- )
- assertThat(config.proverB!!.blobCompression).isEqualTo(
- FileBasedProverConfig(
- requestsDirectory = Path.of("/data/prover-new/compression/requests"),
- responsesDirectory = Path.of("/data/prover-new/compression/responses"),
- inprogressRequestWritingSuffix = ".NEW_OVERRIDE_inprogress_coordinator_writing",
- inprogressProvingSuffixPattern = "\\.inprogress\\.prover.*",
- pollingInterval = 12.seconds,
- pollingTimeout = 12.minutes,
- ),
- )
- assertThat(config.proverB!!.proofAggregation).isEqualTo(
- FileBasedProverConfig(
- requestsDirectory = Path.of("/data/prover-new/aggregation/requests"),
- responsesDirectory = Path.of("/data/prover-new/aggregation/responses"),
- inprogressRequestWritingSuffix = ".NEW_OVERRIDE_inprogress_coordinator_writing",
- inprogressProvingSuffixPattern = "\\.inprogress\\.prover.*",
- pollingInterval = 10.seconds,
- pollingTimeout = 5.minutes,
- ),
- )
- }
-}
diff --git a/coordinator/ethereum/gas-pricing/static-cap/src/main/kotlin/net/consensys/linea/ethereum/gaspricing/staticcap/FeeHistoryFetcherImpl.kt b/coordinator/ethereum/gas-pricing/static-cap/src/main/kotlin/net/consensys/linea/ethereum/gaspricing/staticcap/FeeHistoryFetcherImpl.kt
index 4c852b9988..8358567cde 100644
--- a/coordinator/ethereum/gas-pricing/static-cap/src/main/kotlin/net/consensys/linea/ethereum/gaspricing/staticcap/FeeHistoryFetcherImpl.kt
+++ b/coordinator/ethereum/gas-pricing/static-cap/src/main/kotlin/net/consensys/linea/ethereum/gaspricing/staticcap/FeeHistoryFetcherImpl.kt
@@ -23,6 +23,9 @@ class FeeHistoryFetcherImpl(
val feeHistoryRewardPercentile: Double,
) {
init {
+ require(feeHistoryBlockCount > 0u) {
+ "feeHistoryBlockCount=$feeHistoryBlockCount must be greater than 0."
+ }
require(feeHistoryRewardPercentile in 0.0..100.0) {
"feeHistoryRewardPercentile must be within 0.0 and 100.0." +
" Value=$feeHistoryRewardPercentile"
diff --git a/coordinator/ethereum/test-utils/src/main/kotlin/net/consensys/zkevm/ethereum/ContractsManager.kt b/coordinator/ethereum/test-utils/src/main/kotlin/net/consensys/zkevm/ethereum/ContractsManager.kt
index 0da049361f..ae84798581 100644
--- a/coordinator/ethereum/test-utils/src/main/kotlin/net/consensys/zkevm/ethereum/ContractsManager.kt
+++ b/coordinator/ethereum/test-utils/src/main/kotlin/net/consensys/zkevm/ethereum/ContractsManager.kt
@@ -1,6 +1,7 @@
package net.consensys.zkevm.ethereum
import com.sksamuel.hoplite.ConfigLoaderBuilder
+import com.sksamuel.hoplite.ExperimentalHoplite
import com.sksamuel.hoplite.addFileSource
import linea.contract.l1.LineaContractVersion
import linea.kotlin.gwei
@@ -74,11 +75,15 @@ interface ContractsManager {
object MakeFileDelegatedContractsManager : ContractsManager {
val log = LoggerFactory.getLogger(MakeFileDelegatedContractsManager::class.java)
+
+ @OptIn(ExperimentalHoplite::class)
val lineaRollupContractErrors = findPathTo("config")!!
.resolve("common/smart-contract-errors.toml")
.let { filePath ->
data class ErrorsFile(val smartContractErrors: Map)
- ConfigLoaderBuilder.default()
+ ConfigLoaderBuilder
+ .default()
+ .withExplicitSealedTypes()
.addFileSource(filePath.toAbsolutePath().toString())
.build()
.loadConfigOrThrow()
diff --git a/docker/compose-spec-l2-services.yml b/docker/compose-spec-l2-services.yml
index ff5d31bc2d..2cb026a60c 100644
--- a/docker/compose-spec-l2-services.yml
+++ b/docker/compose-spec-l2-services.yml
@@ -202,7 +202,6 @@ services:
hostname: coordinator
container_name: coordinator
image: consensys/linea-coordinator:${COORDINATOR_TAG:-7e306e2}
- platform: linux/amd64
profiles: [ "l2", "debug" ]
depends_on:
postgres:
@@ -219,13 +218,11 @@ services:
- "9545:9545"
restart: on-failure
environment:
- config__override__l2-network-gas-pricing__json-rpc-pricing-propagation__disabled: ${DISABLE_JSON_RPC_PRICING_PROPAGATION:-true}
config__override__type2-state-proof-provider__disabled: ${DISABLE_TYPE2_STATE_PROOF_PROVIDER:-true}
- command: [ 'java', '-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005','-Dvertx.configurationFile=/var/lib/coordinator/vertx-options.json', '-Dlog4j2.configurationFile=/var/lib/coordinator/log4j2-dev.xml', '-jar', 'libs/coordinator.jar', '--traces-limits-v2', 'config/traces-limits-v2.toml', '--smart-contract-errors', 'config/smart-contract-errors.toml', '--gas-price-cap-time-of-day-multipliers', 'config/gas-price-cap-time-of-day-multipliers.toml', 'config/coordinator-docker.config.toml', 'config/coordinator-docker-traces-v2-override.config.toml' ]
+ command: [ 'java', '-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005','-Dvertx.configurationFile=/var/lib/coordinator/vertx-options.json', '-Dlog4j2.configurationFile=/var/lib/coordinator/log4j2-dev.xml', '-jar', 'libs/coordinator.jar', '--traces-limits-v2', 'config/traces-limits-v2.toml', '--smart-contract-errors', 'config/smart-contract-errors.toml', '--gas-price-cap-time-of-day-multipliers', 'config/gas-price-cap-time-of-day-multipliers.toml', 'config/coordinator-config.toml']
+ #command: [ 'echo', 'forced exit' ]
volumes:
- - ../config/coordinator/coordinator-docker.config.toml:/opt/consensys/linea/coordinator/config/coordinator-docker.config.toml:ro
- - ../config/coordinator/coordinator-docker-web3signer-override.config.toml:/opt/consensys/linea/coordinator/config/coordinator-docker-web3signer-override.config.toml:ro
- - ../config/coordinator/coordinator-docker-traces-v2-override.config.toml:/opt/consensys/linea/coordinator/config/coordinator-docker-traces-v2-override.config.toml:ro
+ - ../config/coordinator/coordinator-config-v2.toml:/opt/consensys/linea/coordinator/config/coordinator-config.toml:ro
- ../config/common/traces-limits-v2.toml:/opt/consensys/linea/coordinator/config/traces-limits-v2.toml:ro
- ../config/common/smart-contract-errors.toml:/opt/consensys/linea/coordinator/config/smart-contract-errors.toml:ro
- ../config/common/gas-price-cap-time-of-day-multipliers.toml:/opt/consensys/linea/coordinator/config/gas-price-cap-time-of-day-multipliers.toml:ro
diff --git a/docker/compose-tracing-v2-ci-extension.yml b/docker/compose-tracing-v2-ci-extension.yml
index 2fdfc0c847..91bad89976 100644
--- a/docker/compose-tracing-v2-ci-extension.yml
+++ b/docker/compose-tracing-v2-ci-extension.yml
@@ -11,7 +11,7 @@ services:
extends:
file: compose-spec-l2-services.yml
service: shomei-frontend
-
+
postman:
extends:
file: compose-spec-l2-services.yml
diff --git a/jvm-libs/generic/extensions/kotlin/src/main/kotlin/linea/kotlin/StringExtensions.kt b/jvm-libs/generic/extensions/kotlin/src/main/kotlin/linea/kotlin/StringExtensions.kt
index e2228f2ff7..6eb35b8b00 100644
--- a/jvm-libs/generic/extensions/kotlin/src/main/kotlin/linea/kotlin/StringExtensions.kt
+++ b/jvm-libs/generic/extensions/kotlin/src/main/kotlin/linea/kotlin/StringExtensions.kt
@@ -16,3 +16,4 @@ fun String.toIntFromHex(): Int = removePrefix("0x").toInt(16)
fun String.toLongFromHex(): Long = removePrefix("0x").toLong(16)
fun String.toULongFromHex(): ULong = BigInteger(removePrefix("0x"), 16).toULong()
fun String.toBigIntegerFromHex(): BigInteger = BigInteger(removePrefix("0x"), 16)
+fun String.toURL(): java.net.URL = java.net.URI.create(this).toURL()
diff --git a/jvm-libs/linea/clients/interfaces/src/main/kotlin/linea/ethapi/EthApiClient.kt b/jvm-libs/linea/clients/interfaces/src/main/kotlin/linea/ethapi/EthApiClient.kt
index 1bddc01a86..d1efdfca17 100644
--- a/jvm-libs/linea/clients/interfaces/src/main/kotlin/linea/ethapi/EthApiClient.kt
+++ b/jvm-libs/linea/clients/interfaces/src/main/kotlin/linea/ethapi/EthApiClient.kt
@@ -6,6 +6,8 @@ import linea.domain.BlockWithTxHashes
import tech.pegasys.teku.infrastructure.async.SafeFuture
interface EthApiClient : EthLogsClient {
+ fun getChainId(): SafeFuture
+
fun findBlockByNumber(
blockParameter: BlockParameter,
): SafeFuture
diff --git a/jvm-libs/linea/clients/interfaces/src/testFixtures/kotlin/linea/ethapi/FakeEthApiClient.kt b/jvm-libs/linea/clients/interfaces/src/testFixtures/kotlin/linea/ethapi/FakeEthApiClient.kt
index d0577496a8..fca5527ea2 100644
--- a/jvm-libs/linea/clients/interfaces/src/testFixtures/kotlin/linea/ethapi/FakeEthApiClient.kt
+++ b/jvm-libs/linea/clients/interfaces/src/testFixtures/kotlin/linea/ethapi/FakeEthApiClient.kt
@@ -17,6 +17,7 @@ import kotlin.time.Duration.Companion.seconds
class FakeEthApiClient(
initialLogsDb: Set = emptySet(),
+ val chainId: ULong = 101UL,
val genesisTimestamp: Instant = Instant.parse("2025-04-01T00:00:00Z"),
val blockTime: Duration = 1.seconds,
initialTagsBlocks: Map = mapOf(
@@ -112,6 +113,10 @@ class FakeEthApiClient(
}
}
+ override fun getChainId(): SafeFuture {
+ return SafeFuture.completedFuture(chainId)
+ }
+
override fun findBlockByNumber(blockParameter: BlockParameter): SafeFuture {
val blockNumber = blockParameterToBlockNumber(blockParameter)
if (isAfterHead(blockNumber)) {
diff --git a/jvm-libs/linea/clients/linea-contract-clients/src/main/kotlin/linea/contract/l2/Web3JL2MessageServiceSmartContractClient.kt b/jvm-libs/linea/clients/linea-contract-clients/src/main/kotlin/linea/contract/l2/Web3JL2MessageServiceSmartContractClient.kt
index 9f05422f96..87d66bb75c 100644
--- a/jvm-libs/linea/clients/linea-contract-clients/src/main/kotlin/linea/contract/l2/Web3JL2MessageServiceSmartContractClient.kt
+++ b/jvm-libs/linea/clients/linea-contract-clients/src/main/kotlin/linea/contract/l2/Web3JL2MessageServiceSmartContractClient.kt
@@ -25,6 +25,7 @@ import org.web3j.tx.gas.StaticGasProvider
import tech.pegasys.teku.infrastructure.async.SafeFuture
import java.math.BigInteger
import java.util.concurrent.atomic.AtomicReference
+import kotlin.random.Random
class Web3JL2MessageServiceSmartContractClient(
private val web3j: Web3j,
@@ -77,6 +78,30 @@ class Web3JL2MessageServiceSmartContractClient(
deploymentBlockNumberProvider = deploymentBlockNumberProvider,
)
}
+
+ fun createReadOnly(
+ web3jClient: Web3j,
+ contractAddress: String,
+ smartContractErrors: SmartContractErrors,
+ smartContractDeploymentBlockNumber: ULong?,
+ ): L2MessageServiceSmartContractClientReadOnly {
+ val unUsedTxManager = AsyncFriendlyTransactionManager(
+ web3j = web3jClient,
+ credentials = Credentials.create(Random.nextBytes(64).encodeHex()),
+ chainId = 1L,
+ )
+ return create(
+ web3jClient = web3jClient,
+ contractAddress = contractAddress,
+ gasLimit = 0UL,
+ maxFeePerGasCap = 0UL,
+ feeHistoryBlockCount = 1u,
+ feeHistoryRewardPercentile = 100.0,
+ transactionManager = unUsedTxManager,
+ smartContractErrors = smartContractErrors,
+ smartContractDeploymentBlockNumber = smartContractDeploymentBlockNumber,
+ )
+ }
}
private val fakeCredentials = Credentials.create(ByteArray(32).encodeHex())
diff --git a/jvm-libs/linea/web3j-extensions/src/main/kotlin/linea/web3j/Web3JFactory.kt b/jvm-libs/linea/web3j-extensions/src/main/kotlin/linea/web3j/Web3JFactory.kt
index a382f650db..f0389e4a34 100644
--- a/jvm-libs/linea/web3j-extensions/src/main/kotlin/linea/web3j/Web3JFactory.kt
+++ b/jvm-libs/linea/web3j-extensions/src/main/kotlin/linea/web3j/Web3JFactory.kt
@@ -8,26 +8,54 @@ import org.web3j.protocol.http.HttpService
import org.web3j.utils.Async
import java.util.concurrent.ScheduledExecutorService
import kotlin.time.Duration
-import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.Duration.Companion.seconds
fun createWeb3jHttpClient(
rpcUrl: String,
log: Logger = org.apache.logging.log4j.LogManager.getLogger(Web3j::class.java),
- pollingInterval: Duration = 500.milliseconds,
+ pollingInterval: Duration = 1.seconds,
executorService: ScheduledExecutorService = Async.defaultExecutorService(),
requestResponseLogLevel: Level = Level.TRACE,
failuresLogLevel: Level = Level.DEBUG,
+): Web3j {
+ val httpService = createWeb3jHttpService(rpcUrl, log, requestResponseLogLevel, failuresLogLevel)
+ return createWeb3jHttpClient(
+ httpService,
+ pollingInterval,
+ executorService,
+ )
+}
+
+fun createWeb3jHttpClient(
+ httpService: HttpService,
+ pollingInterval: Duration = 1.seconds,
+ executorService: ScheduledExecutorService = Async.defaultExecutorService(),
): Web3j {
return Web3j.build(
- HttpService(
- rpcUrl,
- okHttpClientBuilder(
- logger = log,
- requestResponseLogLevel = requestResponseLogLevel,
- failuresLogLevel = failuresLogLevel,
- ).build(),
- ),
+ /* web3jService = */
+ httpService,
+ // used for Web3jRx to poll for new Blocks and TransactionsReceipts
+ /* pollingInterval = */
pollingInterval.inWholeMilliseconds,
+ /* scheduledExecutorService = */
executorService,
)
}
+
+fun createWeb3jHttpService(
+ rpcUrl: String,
+ log: Logger = org.apache.logging.log4j.LogManager.getLogger(Web3j::class.java),
+ requestResponseLogLevel: Level = Level.TRACE,
+ failuresLogLevel: Level = Level.DEBUG,
+): HttpService {
+ return HttpService(
+ /* url = */
+ rpcUrl,
+ /* httpClient = */
+ okHttpClientBuilder(
+ logger = log,
+ requestResponseLogLevel = requestResponseLogLevel,
+ failuresLogLevel = failuresLogLevel,
+ ).build(),
+ )
+}
diff --git a/jvm-libs/linea/web3j-extensions/src/main/kotlin/linea/web3j/ethapi/Web3jEthApiClient.kt b/jvm-libs/linea/web3j-extensions/src/main/kotlin/linea/web3j/ethapi/Web3jEthApiClient.kt
index 943efd9b18..e20262e706 100644
--- a/jvm-libs/linea/web3j-extensions/src/main/kotlin/linea/web3j/ethapi/Web3jEthApiClient.kt
+++ b/jvm-libs/linea/web3j-extensions/src/main/kotlin/linea/web3j/ethapi/Web3jEthApiClient.kt
@@ -5,6 +5,7 @@ import linea.domain.BlockParameter
import linea.domain.BlockWithTxHashes
import linea.domain.EthLog
import linea.ethapi.EthApiClient
+import linea.kotlin.toULong
import linea.web3j.domain.toDomain
import linea.web3j.domain.toWeb3j
import linea.web3j.mapToDomainWithTxHashes
@@ -23,6 +24,14 @@ class Web3jEthApiClient(
val web3jClient: Web3j,
) : EthApiClient {
+ override fun getChainId(): SafeFuture {
+ return web3jClient
+ .ethChainId()
+ .requestAsync { resp ->
+ resp.chainId?.toULong() ?: throw IllegalStateException("Chain ID not found in response")
+ }
+ }
+
override fun findBlockByNumber(blockParameter: BlockParameter): SafeFuture {
return web3jClient
.ethGetBlockByNumber(blockParameter.toWeb3j(), true)
diff --git a/jvm-libs/linea/web3j-extensions/src/main/kotlin/linea/web3j/ethapi/Web3jEthApiClientFactory.kt b/jvm-libs/linea/web3j-extensions/src/main/kotlin/linea/web3j/ethapi/Web3jEthApiClientFactory.kt
index 83365c5d1e..3d5baab1a2 100644
--- a/jvm-libs/linea/web3j-extensions/src/main/kotlin/linea/web3j/ethapi/Web3jEthApiClientFactory.kt
+++ b/jvm-libs/linea/web3j-extensions/src/main/kotlin/linea/web3j/ethapi/Web3jEthApiClientFactory.kt
@@ -65,7 +65,14 @@ fun createEthApiClient(
vertx: Vertx?,
): EthApiClient {
val web3jClient =
- createWeb3jHttpClient(rpcUrl, log, pollingInterval, executorService, requestResponseLogLevel, failuresLogLevel)
+ createWeb3jHttpClient(
+ rpcUrl,
+ log,
+ pollingInterval,
+ executorService,
+ requestResponseLogLevel,
+ failuresLogLevel,
+ )
return createEthApiClient(web3jClient, requestRetryConfig, vertx)
}
diff --git a/jvm-libs/linea/web3j-extensions/src/main/kotlin/linea/web3j/ethapi/Web3jEthApiClientWithRetries.kt b/jvm-libs/linea/web3j-extensions/src/main/kotlin/linea/web3j/ethapi/Web3jEthApiClientWithRetries.kt
index 5254b681e2..a0df70c7c2 100644
--- a/jvm-libs/linea/web3j-extensions/src/main/kotlin/linea/web3j/ethapi/Web3jEthApiClientWithRetries.kt
+++ b/jvm-libs/linea/web3j-extensions/src/main/kotlin/linea/web3j/ethapi/Web3jEthApiClientWithRetries.kt
@@ -28,6 +28,10 @@ class Web3jEthApiClientWithRetries(
)
}
+ override fun getChainId(): SafeFuture {
+ return retry { ethApiClient.getChainId() }
+ }
+
override fun findBlockByNumber(blockParameter: BlockParameter): SafeFuture {
return retry { ethApiClient.findBlockByNumber(blockParameter) }
}
diff --git a/jvm-libs/linea/web3j-extensions/src/main/kotlin/linea/web3j/gas/EIP1559GasProvider.kt b/jvm-libs/linea/web3j-extensions/src/main/kotlin/linea/web3j/gas/EIP1559GasProvider.kt
index dbfbbbe7c6..630a443c11 100644
--- a/jvm-libs/linea/web3j-extensions/src/main/kotlin/linea/web3j/gas/EIP1559GasProvider.kt
+++ b/jvm-libs/linea/web3j-extensions/src/main/kotlin/linea/web3j/gas/EIP1559GasProvider.kt
@@ -4,6 +4,7 @@ import linea.kotlin.toBigInteger
import linea.kotlin.toIntervalString
import linea.web3j.domain.blocksRange
import linea.web3j.domain.toLineaDomain
+import linea.web3j.requestAsync
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
import org.web3j.protocol.Web3j
@@ -20,7 +21,13 @@ class EIP1559GasProvider(private val web3jClient: Web3j, private val config: Con
val maxFeePerGasCap: ULong,
val feeHistoryBlockCount: UInt,
val feeHistoryRewardPercentile: Double,
- )
+ ) {
+ init {
+ require(feeHistoryBlockCount > 0u) {
+ "feeHistoryBlockCount=$feeHistoryBlockCount must be greater than 0."
+ }
+ }
+ }
private val chainId: Long = web3jClient.ethChainId().send().chainId.toLong()
private var cacheIsValidForBlockNumber: BigInteger = BigInteger.ZERO
@@ -36,9 +43,7 @@ class EIP1559GasProvider(private val web3jClient: Web3j, private val config: Con
DefaultBlockParameterName.LATEST,
listOf(config.feeHistoryRewardPercentile),
)
- .sendAsync()
- .thenApply {
- feeHistoryResponse ->
+ .requestAsync { feeHistoryResponse ->
val feeHistory = feeHistoryResponse.feeHistory.toLineaDomain()
var maxPriorityFeePerGas = feeHistory.reward.sumOf { it[0] } / feeHistory.reward.size.toUInt()
diff --git a/linea-besu-package/Makefile b/linea-besu-package/Makefile
index f68643c66a..5640d539f1 100644
--- a/linea-besu-package/Makefile
+++ b/linea-besu-package/Makefile
@@ -21,11 +21,9 @@ build:
run-e2e-test:
echo "EXPECTED_TRACES_API_VERSION=${LINEA_TRACER_PLUGIN_VERSION}"
if [ "$(shell uname)" = "Linux" ]; then \
- sed -i'' 's/^\(expected-traces-api-version-v2=\).*/\1"${LINEA_TRACER_PLUGIN_VERSION}"/' ../config/coordinator/coordinator-docker.config.toml; \
- sed -i'' 's/^\(expected-traces-api-version-v2=\).*/\1"${LINEA_TRACER_PLUGIN_VERSION}"/' ../config/coordinator/coordinator-docker-traces-v2-override.config.toml; \
+ sed -i'' 's/^\(expected-traces-api-version[ ]*=[ ]*\).*/\1"${LINEA_TRACER_PLUGIN_VERSION}"/' ../config/coordinator/coordinator-config-v2.toml; \
elif [ "$(shell uname)" = "Darwin" ]; then \
- sed -i '' 's/^\(expected-traces-api-version-v2=\).*/\1"${LINEA_TRACER_PLUGIN_VERSION}"/' ../config/coordinator/coordinator-docker.config.toml; \
- sed -i '' 's/^\(expected-traces-api-version-v2=\).*/\1"${LINEA_TRACER_PLUGIN_VERSION}"/' ../config/coordinator/coordinator-docker-traces-v2-override.config.toml; \
+ sed -i '' 's/^\(expected-traces-api-version[ ]*=[ ]*\).*/\1"${LINEA_TRACER_PLUGIN_VERSION}"/' ../config/coordinator/coordinator-config-v2.toml; \
fi
cd .. && BESU_PACKAGE_TAG=$(TAG) make start-env-with-tracing-v2-ci && pnpm run -F e2e test:e2e:local