mirror of
https://github.com/vacp2p/linea-monorepo.git
synced 2026-01-06 22:23:55 -05:00
coordinator: adds config v2 (#853)
* coordinator: adds config v2 sample * coordinator: update config V2 * coordinator: update config v2 * coordinator: update config v2 * coordinator: config v2 wip * spotless fix * coordinator: adds config v2 parsers * coordinator: adds config v2 classes and parsing tests * coordinator: adds config v2 classes and parsing tests * remove end2end changes from staterecovery test trigger * coordinator: config v2 - fix prover directory config * coordinator: add getChaindId to EthApiClient.kt * coordinator: improve EIP1559GasProvider validation * coordinator: add createReadOnly to Web3JL2MessageServiceSmartContractClient * coordinator: add validation to FeeHistoryFetcherImpl * coordinator: add more configs to v2 * coordinator: extend Web3JFactory * coordinator: addapt CoordinatorApp to new V2 configs * coordinator: adapt local stack coordinator configs * coordinator: log4j clients.l1 debug * coordinator: revert attempt to use web3signer on CI ONly :( * coordinator: fix test and configs * coordinator: fix test and configs * coordinator: fix traces node address * coordinator: remove unnecessary file * coordinator: hardcode tracesVersion to v2.1.0 to match prover regex * Update config/coordinator/coordinator-config-v2.toml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Fluent Crafter <205769460+fluentcrafter@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Roman Vaseev <4833306+Filter94@users.noreply.github.com> Signed-off-by: Fluent Crafter <205769460+fluentcrafter@users.noreply.github.com> * coordinator: default targetBlobsPerTransaction=7u * coordinator: hardcode tracesVersion to 2.1.0 to match prover regex * coordinator: fix agg configs * coordinator: strict configs log warning when config is not used * coordinator: add carved out config files * coordinator: add opt-in to avoid annoying warning log * feat: update Makefile for new coordinator config file and variable name * localstack: remove coordinator forced platform * coordinator: add missing config on gas-price-cap-calculation * coordinator: remove old configs --------- Signed-off-by: Fluent Crafter <205769460+fluentcrafter@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Roman Vaseev <4833306+Filter94@users.noreply.github.com> Co-authored-by: jonesho <81145364+jonesho@users.noreply.github.com> Co-authored-by: jonesho <jones.ho@consensys.net>
This commit is contained in:
1
.github/workflows/main.yml
vendored
1
.github/workflows/main.yml
vendored
@@ -65,7 +65,6 @@ jobs:
|
||||
- '.github/workflows/staterecovery-*.yml'
|
||||
- '.github/workflows/main.yml'
|
||||
- '.github/workflows/reuse-*.yml'
|
||||
- 'e2e/**'
|
||||
postman:
|
||||
- 'postman/**'
|
||||
- 'sdk/**'
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
|
||||
<option name="MAIN_CLASS_NAME" value="net.consensys.zkevm.coordinator.app.CoordinatorAppMain" />
|
||||
<module name="zkevm.coordinator.app.main" />
|
||||
<option name="PROGRAM_PARAMETERS" value="--traces-limits-v2=config/common/traces-limits-v2.toml --smart-contract-errors=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-docker-traces-v2-override.config.toml config/coordinator/coordinator-local-dev.config.overrides.toml config/coordinator/coordinator-local-dev.config-traces-v2.overrides.toml" />
|
||||
<option name="PROGRAM_PARAMETERS" value="--traces-limits-v2=config/common/traces-limits-v2.toml --smart-contract-errors=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-config-v2.toml config/coordinator/coordinator-config-v2-override-local-dev.toml" />
|
||||
<option name="VM_PARAMETERS" value="-Dvertx.configurationFile=config/coordinator/vertx-options.json -Dlog4j2.configurationFile=config/coordinator/log4j2-dev.xml" />
|
||||
<extension name="net.ashald.envfile">
|
||||
<option name="IS_ENABLED" value="false" />
|
||||
|
||||
2
Makefile
2
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
|
||||
|
||||
@@ -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
|
||||
287
config/coordinator/coordinator-config-v2.toml
Normal file
287
config/coordinator/coordinator-config-v2.toml
Normal file
@@ -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
|
||||
@@ -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
|
||||
@@ -1,13 +0,0 @@
|
||||
[finalization-signer]
|
||||
# Web3j/Web3signer
|
||||
type="Web3Signer"
|
||||
|
||||
[data-submission-signer]
|
||||
# Web3j/Web3signer
|
||||
type="Web3Signer"
|
||||
|
||||
[l2-signer]
|
||||
# Web3j/Web3signer
|
||||
type="Web3Signer"
|
||||
|
||||
|
||||
@@ -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"
|
||||
@@ -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=[]
|
||||
@@ -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
|
||||
@@ -116,6 +116,10 @@
|
||||
<DebouncingFilter/>
|
||||
<appender-ref ref="console"/>
|
||||
</Logger>
|
||||
<Logger name="clients.l1" level="DEBUG" additivity="false">
|
||||
<DebouncingFilter/>
|
||||
<appender-ref ref="console"/>
|
||||
</Logger>
|
||||
<!-- <Logger name="clients.TracesCounters" level="TRACE" additivity="false">-->
|
||||
<!-- <DebouncingFilter/>-->
|
||||
<!-- <appender-ref ref="console"/>-->
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <reified T : Any> loadConfigsOrError(
|
||||
configFiles: List<Path>,
|
||||
): Result<T, String> {
|
||||
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<T>(emptyList()).let { config ->
|
||||
if (config.isInvalid()) {
|
||||
Err(config.getInvalidUnsafe().description())
|
||||
} else {
|
||||
Ok(config.getUnsafe())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun logErrorIfPresent(
|
||||
configName: String,
|
||||
configFiles: List<Path>,
|
||||
configLoadingResult: Result<Any?, String>,
|
||||
logger: Logger,
|
||||
) {
|
||||
if (configLoadingResult is Err) {
|
||||
logger.error("Failed to load $configName from files=$configFiles with error=${configLoadingResult.error}")
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> loadConfigsAndLogErrors(
|
||||
configFiles: List<Path>,
|
||||
configName: String,
|
||||
logger: Logger = LogManager.getLogger("linea.coordinator.config"),
|
||||
): Result<T, String> {
|
||||
return loadConfigsOrError<T>(configFiles)
|
||||
.also { logErrorIfPresent(configName, configFiles, it, logger) }
|
||||
}
|
||||
|
||||
fun loadConfigsOrError(
|
||||
coordinatorConfigFiles: List<Path>,
|
||||
tracesLimitsFileV2: Path,
|
||||
gasPriceCapTimeOfDayMultipliersFile: Path,
|
||||
smartContractErrorsFile: Path,
|
||||
logger: Logger = LogManager.getLogger("linea.coordinator.config"),
|
||||
): Result<CoordinatorConfigTomlDto, String> {
|
||||
val coordinatorBaseConfigs =
|
||||
loadConfigsAndLogErrors<CoordinatorConfigTomlDto>(coordinatorConfigFiles, "coordinator", logger)
|
||||
val tracesLimitsV2Configs =
|
||||
loadConfigsAndLogErrors<TracesLimitsV2ConfigFile>(listOf(tracesLimitsFileV2), "traces limits v2", logger)
|
||||
val gasPriceCapTimeOfDayMultipliersConfig =
|
||||
loadConfigsAndLogErrors<GasPriceCapTimeOfDayMultipliersConfig>(
|
||||
listOf(gasPriceCapTimeOfDayMultipliersFile),
|
||||
"l1 submission gas prices caps",
|
||||
logger,
|
||||
)
|
||||
val smartContractErrorsConfig = loadConfigsAndLogErrors<SmartContractErrorCodesConfig>(
|
||||
listOf(smartContractErrorsFile),
|
||||
"smart contract errors",
|
||||
logger,
|
||||
)
|
||||
val configError = listOf(
|
||||
coordinatorBaseConfigs,
|
||||
tracesLimitsV2Configs,
|
||||
gasPriceCapTimeOfDayMultipliersConfig,
|
||||
smartContractErrorsConfig,
|
||||
)
|
||||
.find { it is Err }
|
||||
|
||||
if (configError != null) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return configError as Result<CoordinatorConfigTomlDto, String>
|
||||
}
|
||||
|
||||
val baseConfig = coordinatorBaseConfigs.get()!!
|
||||
val finalConfig = baseConfig.copy(
|
||||
conflation = baseConfig.conflation.copy(
|
||||
_tracesLimitsV2 = tracesLimitsV2Configs.get()?.tracesLimits?.let { TracesCountersV2(it) },
|
||||
_smartContractErrors = smartContractErrorsConfig.get()!!.smartContractErrors,
|
||||
),
|
||||
l1DynamicGasPriceCapService = baseConfig.l1DynamicGasPriceCapService.copy(
|
||||
gasPriceCapCalculation = baseConfig.l1DynamicGasPriceCapService.gasPriceCapCalculation.copy(
|
||||
timeOfDayMultipliers = gasPriceCapTimeOfDayMultipliersConfig.get()?.gasPriceCapTimeOfDayMultipliers,
|
||||
),
|
||||
),
|
||||
)
|
||||
return Ok(finalConfig)
|
||||
}
|
||||
|
||||
fun loadConfigs(
|
||||
coordinatorConfigFiles: List<Path>,
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package linea.coordinator.config.v2
|
||||
|
||||
data class ApiConfig(
|
||||
val observabilityPort: UInt,
|
||||
)
|
||||
@@ -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<ULong>? = null,
|
||||
val aggregationSizeMultipleOf: UInt = 1u,
|
||||
)
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
@@ -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,
|
||||
),
|
||||
)
|
||||
@@ -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()
|
||||
@@ -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,
|
||||
)
|
||||
@@ -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<String, Double>,
|
||||
)
|
||||
|
||||
data class FeeHistoryFetcherConfig(
|
||||
val l1Endpoint: URL,
|
||||
val fetchInterval: Duration,
|
||||
val maxBlockCount: UInt,
|
||||
val rewardPercentiles: List<UInt>,
|
||||
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
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
@@ -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?,
|
||||
)
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<URL>,
|
||||
val requestLimitPerEndpoint: UInt = UInt.MAX_VALUE,
|
||||
val requestRetries: RetryConfig = RetryConfig.endlessRetry(
|
||||
backoffDelay = 1.seconds,
|
||||
failuresWarningThreshold = 3u,
|
||||
),
|
||||
)
|
||||
@@ -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<URL>,
|
||||
val requestLimitPerEndpoint: UInt = 100u,
|
||||
val requestRetries: RetryConfig = RetryConfig.endlessRetry(
|
||||
backoffDelay = 1.seconds,
|
||||
failuresWarningThreshold = 3u,
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -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<URL>,
|
||||
val requestRetries: RetryConfig = RetryConfig.endlessRetry(
|
||||
backoffDelay = 1.seconds,
|
||||
failuresWarningThreshold = 3u,
|
||||
),
|
||||
val l1QueryBlockTag: BlockParameter.Tag = BlockParameter.Tag.FINALIZED,
|
||||
val l1PollingInterval: Duration = 6.seconds,
|
||||
) : FeatureToggle
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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 <reified T : Any> parseConfig(toml: String, strict: Boolean = true): T {
|
||||
return ConfigLoaderBuilder
|
||||
.default()
|
||||
.withExplicitSealedTypes()
|
||||
.addCoordinatorTomlDecoders(strict)
|
||||
.addSource(TomlPropertySource(toml))
|
||||
.build()
|
||||
.loadConfigOrThrow<T>()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalHoplite::class)
|
||||
inline fun <reified T : Any> loadConfigsOrError(
|
||||
configFiles: List<Path>,
|
||||
strict: Boolean,
|
||||
): Result<T, String> {
|
||||
val confLoader = ConfigLoaderBuilder
|
||||
.empty()
|
||||
.addDefaults()
|
||||
.withExplicitSealedTypes()
|
||||
.addCoordinatorTomlDecoders(strict)
|
||||
.build()
|
||||
|
||||
return confLoader
|
||||
.loadConfig<T>(configFiles.reversed().map { it.toAbsolutePath().toString() })
|
||||
.let { configResult: ConfigResult<T> ->
|
||||
when (configResult) {
|
||||
is Validated.Valid -> Ok(configResult.value)
|
||||
is Validated.Invalid -> Err(configResult.getInvalidUnsafe().description())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun logErrorIfPresent(
|
||||
configLoadingResult: Result<Any?, String>,
|
||||
logger: Logger,
|
||||
logLevel: Level = Level.ERROR,
|
||||
) {
|
||||
if (configLoadingResult is Err) {
|
||||
logger.log(logLevel, configLoadingResult.error)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> loadConfigsAndLogErrors(
|
||||
configFiles: List<Path>,
|
||||
logger: Logger = LogManager.getLogger("linea.coordinator.config"),
|
||||
strict: Boolean,
|
||||
): Result<T, String> {
|
||||
return loadConfigsOrError<T>(configFiles, strict = strict)
|
||||
.also {
|
||||
val logLevel = if (strict) Level.WARN else Level.ERROR
|
||||
logErrorIfPresent(it, logger, logLevel)
|
||||
}
|
||||
}
|
||||
|
||||
fun loadConfigsOrError(
|
||||
coordinatorConfigFiles: List<Path>,
|
||||
tracesLimitsFileV2: Path,
|
||||
gasPriceCapTimeOfDayMultipliersFile: Path,
|
||||
smartContractErrorsFile: Path,
|
||||
logger: Logger = LogManager.getLogger("linea.coordinator.config"),
|
||||
strict: Boolean = false,
|
||||
): Result<CoordinatorConfigToml, String> {
|
||||
val coordinatorBaseConfigs =
|
||||
loadConfigsAndLogErrors<CoordinatorConfigFileToml>(coordinatorConfigFiles, logger, strict)
|
||||
val tracesLimitsV2Configs =
|
||||
loadConfigsAndLogErrors<TracesLimitsConfigFileToml>(listOf(tracesLimitsFileV2), logger, strict)
|
||||
val gasPriceCapTimeOfDayMultipliersConfig =
|
||||
loadConfigsAndLogErrors<GasPriceCapTimeOfDayMultipliersConfigFileToml>(
|
||||
listOf(gasPriceCapTimeOfDayMultipliersFile),
|
||||
logger,
|
||||
strict,
|
||||
)
|
||||
val smartContractErrorsConfig = loadConfigsAndLogErrors<SmartContractErrorCodesConfigFileToml>(
|
||||
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<CoordinatorConfigToml, String>
|
||||
}
|
||||
|
||||
val finalConfig = CoordinatorConfigToml(
|
||||
configs = coordinatorBaseConfigs.get()!!,
|
||||
tracesLimitsV2 = tracesLimitsV2Configs.get()!!,
|
||||
l1DynamicGasPriceCapTimeOfDayMultipliers = gasPriceCapTimeOfDayMultipliersConfig.get(),
|
||||
smartContractErrors = smartContractErrorsConfig.get(),
|
||||
)
|
||||
return Ok(finalConfig)
|
||||
}
|
||||
|
||||
fun loadConfigs(
|
||||
coordinatorConfigFiles: List<Path>,
|
||||
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()
|
||||
}
|
||||
@@ -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<ULong>? = 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,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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<TracingModuleV2, UInt>,
|
||||
)
|
||||
|
||||
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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
)
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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<UInt> = 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(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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"),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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) },
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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<URL>,
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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<URL>,
|
||||
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
|
||||
)
|
||||
}
|
||||
*/
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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<URL>,
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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<BlockParameter> {
|
||||
override fun supports(type: KType): Boolean = type.classifier == BlockParameter::class
|
||||
override fun decode(node: Node, type: KType, context: DecoderContext): ConfigResult<BlockParameter> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
open class AbstractBlockParameterDecoder<T : BlockParameter> : Decoder<T> {
|
||||
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<T> {
|
||||
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<BlockParameter> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BlockParameterTagDecoder : AbstractBlockParameterDecoder<BlockParameter.Tag>() {
|
||||
override fun supports(type: KType): Boolean = type.classifier == BlockParameter.Tag::class
|
||||
}
|
||||
|
||||
class BlockParameterNumberDecoder : AbstractBlockParameterDecoder<BlockParameter.BlockNumber>() {
|
||||
override fun supports(type: KType): Boolean = type.classifier == BlockParameter.BlockNumber::class
|
||||
}
|
||||
|
||||
class BlockParameterDecoder : AbstractBlockParameterDecoder<BlockParameter>() {
|
||||
override fun supports(type: KType): Boolean = type.classifier == BlockParameter::class
|
||||
}
|
||||
@@ -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<ByteArray> {
|
||||
override fun decode(
|
||||
node: Node,
|
||||
type: KType,
|
||||
context: DecoderContext,
|
||||
): ConfigResult<ByteArray> {
|
||||
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<Duration> {
|
||||
override fun decode(
|
||||
node: Node,
|
||||
type: KType,
|
||||
context: DecoderContext,
|
||||
): ConfigResult<Duration> {
|
||||
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<SignerConfigToml.SignerType> {
|
||||
override fun decode(
|
||||
node: Node,
|
||||
type: KType,
|
||||
context: DecoderContext,
|
||||
): ConfigResult<SignerConfigToml.SignerType> {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Unit> {
|
||||
return SafeFuture.completedFuture(Unit)
|
||||
}
|
||||
|
||||
override fun stop(): CompletableFuture<Unit> {
|
||||
return SafeFuture.completedFuture(Unit)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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<Long> = 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<ULong> = _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<Long> = emptyList(),
|
||||
) {
|
||||
val targetEndBlocks: List<ULong> = _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<URL>,
|
||||
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<URL>,
|
||||
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<Double>,
|
||||
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<URL>,
|
||||
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<TracingModuleV2, UInt>)
|
||||
|
||||
//
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<URL>,
|
||||
val besuGasPriceUpdateRecipients: List<URL>,
|
||||
) : FeatureToggleable {
|
||||
init {
|
||||
require(disabled || (gethGasPriceUpdateRecipients.isNotEmpty() || besuGasPriceUpdateRecipients.isNotEmpty())) {
|
||||
"There is no point of enabling JSON RPC pricing propagation if there are no " +
|
||||
"gethGasPriceUpdateRecipients or besuGasPriceUpdateRecipients defined"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class ExtraDataPricingPropagationTomlDto(
|
||||
override var disabled: Boolean = false,
|
||||
val extraDataUpdateRecipient: URL,
|
||||
) : FeatureToggleable
|
||||
|
||||
data class L2NetworkGasPricingTomlDto(
|
||||
override var disabled: Boolean = false,
|
||||
override val requestRetry: RequestRetryConfigTomlFriendly,
|
||||
|
||||
val priceUpdateInterval: Duration,
|
||||
val feeHistoryBlockCount: Int,
|
||||
val feeHistoryRewardPercentile: Double,
|
||||
|
||||
val blobSubmissionExpectedExecutionGas: Int,
|
||||
@ConfigAlias("bytesPerDataSubmission") val _bytesPerDataSubmission: Int?,
|
||||
val l1BlobGas: Int,
|
||||
|
||||
val legacy: LegacyGasPricingTomlDto,
|
||||
val variableCostPricing: VariableCostPricingTomlDto,
|
||||
val jsonRpcPricingPropagation: JsonRpcPricingPropagationTomlDto?,
|
||||
val extraDataPricingPropagation: ExtraDataPricingPropagationTomlDto,
|
||||
) : FeatureToggleable, RequestRetryConfigurable {
|
||||
init {
|
||||
require(feeHistoryBlockCount > 0) { "feeHistoryBlockCount must be greater than 0" }
|
||||
require(blobSubmissionExpectedExecutionGas > 0) { "blobSubmissionExpectedExecutionGas must be greater than 0" }
|
||||
require(l1BlobGas > 0) { "l1BlobGas must be greater than 0" }
|
||||
|
||||
require(disabled || (jsonRpcPricingPropagation?.enabled == 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,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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<WrapperConfig>(toml).api).isEqualTo(config)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should parse api minimal config`() {
|
||||
assertThat(parseConfig<WrapperConfig>(tomlMinimal).api).isEqualTo(configMinimal)
|
||||
}
|
||||
}
|
||||
@@ -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<WrapperConfig>(toml).conflation,
|
||||
)
|
||||
.isEqualTo(config)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should parse conflation toml configs - minimal`() {
|
||||
assertThat(
|
||||
parseConfig<WrapperConfig>(tomlMinimal).conflation,
|
||||
)
|
||||
.isEqualTo(configMinimal)
|
||||
}
|
||||
}
|
||||
@@ -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<CoordinatorConfigFileToml>(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<CoordinatorConfigFileToml>(toml)).isEqualTo(config)
|
||||
}
|
||||
}
|
||||
@@ -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<WrapperConfig>(toml).database).isEqualTo(config)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should parse database minimal config`() {
|
||||
assertThat(parseConfig<WrapperConfig>(tomlMinimal).database).isEqualTo(configMinimal)
|
||||
}
|
||||
}
|
||||
@@ -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<WrapperConfig>(toml).defaults).isEqualTo(config)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should parse defaults minimal configs`() {
|
||||
assertThat(parseConfig<WrapperConfig>(tomlMinimal).defaults).isEqualTo(configMinimal)
|
||||
}
|
||||
}
|
||||
@@ -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<WrapperConfig>(toml).l1FinalizationMonitor)
|
||||
.isEqualTo(config)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should parse finalization monitor minimal config`() {
|
||||
assertThat(parseConfig<WrapperConfig>(tomlMinimal).l1FinalizationMonitor)
|
||||
.isEqualTo(configMinimal)
|
||||
}
|
||||
}
|
||||
@@ -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<WrapperConfig>(toml).l1Submission)
|
||||
.isEqualTo(config)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should parse l1 submission minimal config`() {
|
||||
assertThat(parseConfig<WrapperConfig>(tomlMinimal).l1Submission).isEqualTo(configMinimal)
|
||||
}
|
||||
}
|
||||
@@ -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<WrapperConfig>(toml).l2NetworkGasPricing)
|
||||
.isEqualTo(config)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should parse l2 network gaspricing minimal config`() {
|
||||
assertThat(parseConfig<WrapperConfig>(tomlMinimal).l2NetworkGasPricing)
|
||||
.isEqualTo(configMinimal)
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<WrapperConfig>(toml).messageAnchoring)
|
||||
.isEqualTo(config)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should parse message anchoring minimal config`() {
|
||||
assertThat(parseConfig<WrapperConfig>(tomlMinimal).messageAnchoring)
|
||||
.isEqualTo(configMinimal)
|
||||
}
|
||||
}
|
||||
@@ -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<WrapperConfig>(toml).protocol)
|
||||
.isEqualTo(config)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should parse protocol minimal configs`() {
|
||||
assertThat(parseConfig<WrapperConfig>(tomlMinimal).protocol)
|
||||
.isEqualTo(configMinimal)
|
||||
}
|
||||
}
|
||||
@@ -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<WrapperConfig>(toml).prover,
|
||||
).isEqualTo(config)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should parse conflation toml configs and provide defaults`() {
|
||||
assertThat(
|
||||
parseConfig<WrapperConfig>(tomlMinimal).prover,
|
||||
).isEqualTo(configMinimal)
|
||||
}
|
||||
}
|
||||
@@ -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<WrapperConfig>(toml)).isEqualTo(config)
|
||||
}
|
||||
}
|
||||
@@ -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<WrapperConfig>(toml).stateManager)
|
||||
.isEqualTo(config)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should parse state manager minimal config`() {
|
||||
assertThat(parseConfig<WrapperConfig>(tomlMinimal).stateManager)
|
||||
.isEqualTo(configMinimal)
|
||||
}
|
||||
}
|
||||
@@ -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"),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<TracesToml.ClientApiConfigToml>(
|
||||
"""
|
||||
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<WrapperConfig>(toml).traces)
|
||||
.isEqualTo(config)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should parse traces minimal config`() {
|
||||
assertThat(parseConfig<WrapperConfig>(tomlMinimal).traces)
|
||||
.isEqualTo(configMinimal)
|
||||
}
|
||||
}
|
||||
@@ -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<WrapperConfig>(toml).type2StateProofProvider)
|
||||
.isEqualTo(config)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should parse type2-state-proof-provider minimal config`() {
|
||||
assertThat(parseConfig<WrapperConfig>(tomlMinimal).type2StateProofProvider)
|
||||
.isEqualTo(configMinimal)
|
||||
}
|
||||
}
|
||||
@@ -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<ConfigTomTag>("""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<ConfigTomTag>("""block-parameter = "latest" """))
|
||||
.isEqualTo(ConfigTomTag(BlockParameter.Tag.LATEST))
|
||||
|
||||
assertThat(parseConfig<ConfigTomTag>(""" """))
|
||||
.isEqualTo(ConfigTomTag(null))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should decode block parameter number`() {
|
||||
data class ConfigTomTag(val blockParameter: BlockParameter.BlockNumber)
|
||||
|
||||
assertThat(parseConfig<ConfigTomTag>("""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<ConfigTomTag>("""block-parameter = 2_000 """))
|
||||
.isEqualTo(ConfigTomTag(BlockParameter.BlockNumber(2_000UL)))
|
||||
|
||||
assertThat(parseConfig<ConfigTomTag>(""" """))
|
||||
.isEqualTo(ConfigTomTag(null))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should decode block parameter generic type with tag set`() {
|
||||
data class ConfigTomTag(val blockParameter: BlockParameter)
|
||||
|
||||
assertThat(parseConfig<ConfigTomTag>("""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<ConfigTomTag>("""block-parameter = 2_000 """))
|
||||
.isEqualTo(ConfigTomTag(BlockParameter.BlockNumber(2_000UL)))
|
||||
}
|
||||
}
|
||||
@@ -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<TestConfig>(
|
||||
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<IllegalArgumentException> {
|
||||
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<IllegalArgumentException> {
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
@@ -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,
|
||||
),
|
||||
)
|
||||
@@ -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<Config>().l2NetworkGasPricing
|
||||
}
|
||||
|
||||
private val naiveL2NetworkGasPricingServiceConfigToml = """
|
||||
[l2-network-gas-pricing]
|
||||
disabled = false
|
||||
price-update-interval = "PT12S"
|
||||
|
||||
fee-history-block-count = 50
|
||||
fee-history-reward-percentile = 15
|
||||
|
||||
blob-submission-expected-execution-gas = 213000.0 # Lower to 120k as we improve efficiency
|
||||
# Defaults to expected-blob-gas
|
||||
#bytes-per-data-submission=131072.0 # 2^17
|
||||
l1-blob-gas = 131072 # 2^17
|
||||
|
||||
[l2-network-gas-pricing.request-retry]
|
||||
max-retries = 3
|
||||
timeout = "PT6S"
|
||||
backoff-delay = "PT1S"
|
||||
failures-warning-threshold = 2
|
||||
|
||||
[l2-network-gas-pricing.variable-cost-pricing]
|
||||
gas-price-fixed-cost = 3000000
|
||||
legacy-fees-multiplier = 1.2
|
||||
margin = 4.0
|
||||
variable-cost-upper-bound = 10000000001 # ~10 GWEI
|
||||
variable-cost-lower-bound = 90000001 # ~0.09 GWEI
|
||||
|
||||
[l2-network-gas-pricing.extra-data-pricing-propagation]
|
||||
extra-data-update-recipient = "http://sequencer:8545/"
|
||||
|
||||
[l2-network-gas-pricing.legacy]
|
||||
type="Naive"
|
||||
gas-price-upper-bound = 10000000000 # 10 GWEI
|
||||
gas-price-lower-bound = 90000000 # 0.09 GWEI
|
||||
|
||||
[l2-network-gas-pricing.legacy.naive-gas-pricing]
|
||||
base-fee-coefficient = 0.1
|
||||
priority-fee-coefficient = 1.0
|
||||
base-fee-blob-coefficient = 0.1
|
||||
|
||||
|
||||
[l2-network-gas-pricing.json-rpc-pricing-propagation]
|
||||
geth-gas-price-update-recipients = [
|
||||
"http://traces-node:8545/",
|
||||
"http://l2-node:8545/"
|
||||
]
|
||||
besu-gas-price-update-recipients = [
|
||||
"http://sequencer:8545/"
|
||||
]
|
||||
""".trimIndent()
|
||||
|
||||
private val sampleTransactionL2NetworkGasPricingServiceConfigToml = """
|
||||
[l2-network-gas-pricing]
|
||||
disabled = false
|
||||
price-update-interval = "PT12S"
|
||||
|
||||
fee-history-block-count = 50
|
||||
fee-history-reward-percentile = 15
|
||||
|
||||
blob-submission-expected-execution-gas = 213000.0 # Lower to 120k as we improve efficiency
|
||||
# Defaults to expected-blob-gas
|
||||
#bytes-per-data-submission=131072.0 # 2^17
|
||||
l1-blob-gas = 131072 # 2^17
|
||||
|
||||
[l2-network-gas-pricing.request-retry]
|
||||
max-retries = 3
|
||||
timeout = "PT6S"
|
||||
backoff-delay = "PT1S"
|
||||
failures-warning-threshold = 2
|
||||
|
||||
[l2-network-gas-pricing.variable-cost-pricing]
|
||||
gas-price-fixed-cost = 3000000
|
||||
legacy-fees-multiplier = 1.2
|
||||
margin = 4.0
|
||||
variable-cost-upper-bound = 10000000001 # ~10 GWEI
|
||||
variable-cost-lower-bound = 90000001 # ~0.09 GWEI
|
||||
|
||||
[l2-network-gas-pricing.extra-data-pricing-propagation]
|
||||
extra-data-update-recipient = "http://sequencer:8545/"
|
||||
|
||||
[l2-network-gas-pricing.legacy]
|
||||
type="SampleTransaction"
|
||||
gas-price-upper-bound = 10000000000 # 10 GWEI
|
||||
gas-price-lower-bound = 90000000 # 0.09 GWEI
|
||||
|
||||
[l2-network-gas-pricing.json-rpc-pricing-propagation]
|
||||
geth-gas-price-update-recipients = [
|
||||
"http://traces-node:8545/",
|
||||
"http://l2-node:8545/"
|
||||
]
|
||||
besu-gas-price-update-recipients = [
|
||||
"http://sequencer:8545/"
|
||||
]
|
||||
""".trimIndent()
|
||||
|
||||
@Test
|
||||
fun `dto with naive legacy gas calculator is parseable`() {
|
||||
val config = parseConfig(naiveL2NetworkGasPricingServiceConfigToml)
|
||||
assertThat(config).isEqualTo(
|
||||
L2NetworkGasPricingTomlDto(
|
||||
requestRetry = RequestRetryConfigTomlFriendly(
|
||||
maxRetries = 3,
|
||||
timeout = 6.seconds.toJavaDuration(),
|
||||
backoffDelay = 1.seconds.toJavaDuration(),
|
||||
failuresWarningThreshold = 2,
|
||||
),
|
||||
|
||||
priceUpdateInterval = Duration.parse("PT12S"),
|
||||
feeHistoryBlockCount = 50,
|
||||
feeHistoryRewardPercentile = 15.0,
|
||||
|
||||
blobSubmissionExpectedExecutionGas = 213_000,
|
||||
_bytesPerDataSubmission = null,
|
||||
l1BlobGas = 131072,
|
||||
legacy = LegacyGasPricingTomlDto(
|
||||
type = LegacyGasPricingTomlDto.Type.Naive,
|
||||
gasPriceUpperBound = 10_000_000_000u,
|
||||
gasPriceLowerBound = 90_000_000u,
|
||||
naiveGasPricing = NaiveGasPricingTomlDto(
|
||||
baseFeeCoefficient = 0.1,
|
||||
priorityFeeCoefficient = 1.0,
|
||||
baseFeeBlobCoefficient = 0.1,
|
||||
),
|
||||
),
|
||||
variableCostPricing = VariableCostPricingTomlDto(
|
||||
gasPriceFixedCost = 3000000u,
|
||||
legacyFeesMultiplier = 1.2,
|
||||
margin = 4.0,
|
||||
variableCostUpperBound = 10_000_000_001u,
|
||||
variableCostLowerBound = 90_000_001u,
|
||||
),
|
||||
jsonRpcPricingPropagation = JsonRpcPricingPropagationTomlDto(
|
||||
gethGasPriceUpdateRecipients = listOf(
|
||||
URI("http://traces-node:8545/").toURL(),
|
||||
URI("http://l2-node:8545/").toURL(),
|
||||
),
|
||||
besuGasPriceUpdateRecipients = listOf(
|
||||
URI("http://sequencer:8545/").toURL(),
|
||||
),
|
||||
),
|
||||
extraDataPricingPropagation = ExtraDataPricingPropagationTomlDto(
|
||||
extraDataUpdateRecipient = URI("http://sequencer:8545/").toURL(),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `dto with sample transaction legacy gas calculator is parseable`() {
|
||||
val config = parseConfig(sampleTransactionL2NetworkGasPricingServiceConfigToml)
|
||||
assertThat(config).isEqualTo(
|
||||
L2NetworkGasPricingTomlDto(
|
||||
requestRetry = RequestRetryConfigTomlFriendly(
|
||||
maxRetries = 3,
|
||||
timeout = 6.seconds.toJavaDuration(),
|
||||
backoffDelay = 1.seconds.toJavaDuration(),
|
||||
failuresWarningThreshold = 2,
|
||||
),
|
||||
|
||||
priceUpdateInterval = Duration.parse("PT12S"),
|
||||
feeHistoryBlockCount = 50,
|
||||
feeHistoryRewardPercentile = 15.0,
|
||||
|
||||
blobSubmissionExpectedExecutionGas = 213_000,
|
||||
_bytesPerDataSubmission = null,
|
||||
l1BlobGas = 131072,
|
||||
legacy = LegacyGasPricingTomlDto(
|
||||
type = LegacyGasPricingTomlDto.Type.SampleTransaction,
|
||||
gasPriceUpperBound = 10_000_000_000u,
|
||||
gasPriceLowerBound = 90_000_000u,
|
||||
naiveGasPricing = null,
|
||||
),
|
||||
variableCostPricing = VariableCostPricingTomlDto(
|
||||
gasPriceFixedCost = 3000000u,
|
||||
legacyFeesMultiplier = 1.2,
|
||||
margin = 4.0,
|
||||
variableCostUpperBound = 10_000_000_001u,
|
||||
variableCostLowerBound = 90_000_001u,
|
||||
),
|
||||
jsonRpcPricingPropagation = JsonRpcPricingPropagationTomlDto(
|
||||
gethGasPriceUpdateRecipients = listOf(
|
||||
URI("http://traces-node:8545/").toURL(),
|
||||
URI("http://l2-node:8545/").toURL(),
|
||||
),
|
||||
besuGasPriceUpdateRecipients = listOf(
|
||||
URI("http://sequencer:8545/").toURL(),
|
||||
),
|
||||
),
|
||||
extraDataPricingPropagation = ExtraDataPricingPropagationTomlDto(
|
||||
extraDataUpdateRecipient = URI("http://sequencer:8545/").toURL(),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reification is correct`() {
|
||||
val config = parseConfig(naiveL2NetworkGasPricingServiceConfigToml).reified()
|
||||
val l2NetworkGasPricingRequestretryConfig = RequestRetryConfig(
|
||||
maxRetries = 3u,
|
||||
timeout = 6.seconds,
|
||||
backoffDelay = 1.seconds,
|
||||
failuresWarningThreshold = 2u,
|
||||
)
|
||||
|
||||
assertThat(config).isEqualTo(
|
||||
L2NetworkGasPricingService.Config(
|
||||
feeHistoryFetcherConfig = FeeHistoryFetcherImpl.Config(
|
||||
feeHistoryBlockCount = 50U,
|
||||
feeHistoryRewardPercentile = 15.0,
|
||||
),
|
||||
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,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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<Config>()
|
||||
.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,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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<Config>()
|
||||
.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,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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<String, String>)
|
||||
ConfigLoaderBuilder.default()
|
||||
ConfigLoaderBuilder
|
||||
.default()
|
||||
.withExplicitSealedTypes()
|
||||
.addFileSource(filePath.toAbsolutePath().toString())
|
||||
.build()
|
||||
.loadConfigOrThrow<ErrorsFile>()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -11,7 +11,7 @@ services:
|
||||
extends:
|
||||
file: compose-spec-l2-services.yml
|
||||
service: shomei-frontend
|
||||
|
||||
|
||||
postman:
|
||||
extends:
|
||||
file: compose-spec-l2-services.yml
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -6,6 +6,8 @@ import linea.domain.BlockWithTxHashes
|
||||
import tech.pegasys.teku.infrastructure.async.SafeFuture
|
||||
|
||||
interface EthApiClient : EthLogsClient {
|
||||
fun getChainId(): SafeFuture<ULong>
|
||||
|
||||
fun findBlockByNumber(
|
||||
blockParameter: BlockParameter,
|
||||
): SafeFuture<Block?>
|
||||
|
||||
@@ -17,6 +17,7 @@ import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
class FakeEthApiClient(
|
||||
initialLogsDb: Set<EthLog> = emptySet(),
|
||||
val chainId: ULong = 101UL,
|
||||
val genesisTimestamp: Instant = Instant.parse("2025-04-01T00:00:00Z"),
|
||||
val blockTime: Duration = 1.seconds,
|
||||
initialTagsBlocks: Map<BlockParameter.Tag, ULong> = mapOf(
|
||||
@@ -112,6 +113,10 @@ class FakeEthApiClient(
|
||||
}
|
||||
}
|
||||
|
||||
override fun getChainId(): SafeFuture<ULong> {
|
||||
return SafeFuture.completedFuture(chainId)
|
||||
}
|
||||
|
||||
override fun findBlockByNumber(blockParameter: BlockParameter): SafeFuture<Block?> {
|
||||
val blockNumber = blockParameterToBlockNumber(blockParameter)
|
||||
if (isAfterHead(blockNumber)) {
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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 {
|
||||
return Web3j.build(
|
||||
HttpService(
|
||||
rpcUrl,
|
||||
okHttpClientBuilder(
|
||||
logger = log,
|
||||
requestResponseLogLevel = requestResponseLogLevel,
|
||||
failuresLogLevel = failuresLogLevel,
|
||||
).build(),
|
||||
),
|
||||
pollingInterval.inWholeMilliseconds,
|
||||
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(
|
||||
/* 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(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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<ULong> {
|
||||
return web3jClient
|
||||
.ethChainId()
|
||||
.requestAsync { resp ->
|
||||
resp.chainId?.toULong() ?: throw IllegalStateException("Chain ID not found in response")
|
||||
}
|
||||
}
|
||||
|
||||
override fun findBlockByNumber(blockParameter: BlockParameter): SafeFuture<Block?> {
|
||||
return web3jClient
|
||||
.ethGetBlockByNumber(blockParameter.toWeb3j(), true)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -28,6 +28,10 @@ class Web3jEthApiClientWithRetries(
|
||||
)
|
||||
}
|
||||
|
||||
override fun getChainId(): SafeFuture<ULong> {
|
||||
return retry { ethApiClient.getChainId() }
|
||||
}
|
||||
|
||||
override fun findBlockByNumber(blockParameter: BlockParameter): SafeFuture<Block?> {
|
||||
return retry { ethApiClient.findBlockByNumber(blockParameter) }
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user