Skip to content

Coordinator review 2026-06-01 main 467d7a4 #3243

@github-actions

Description

@github-actions

Now I have sufficient verified findings. Let me compose the review.

Dead Code

Duplicated / Similar Code

Anti-patterns and Code Smells

Concurrency Issues

  • jvm-libs/linea/web3j-extensions/src/main/kotlin/linea/web3j/transactionmanager/AsyncFriendlyTransactionManager.kt:34resetNonce().get() is invoked synchronously inside the constructor (and repeated in the constructors at lines 43, 48, 57, 67); if this manager is built on an event-loop thread (Vert.x or Netty), the blocking RPC call deadlocks it. Defer initialization to an explicit init() method that returns a SafeFuture<Unit> and have callers chain it.
    • ❌ won't fix
    • ❌ false positive
    • 🤖 to be fixed
    • ✅ addressed/fixed
  • coordinator/core/src/main/kotlin/linea/coordination/blob/BlobCompressionProofCoordinator.kt:230vertx.cancelTimer(timerId!!) non-null asserts after a null check that is in a different thread-safety context than the timer rescheduling callback at line 200 (inside blobPollingAction synchronized on this@BlobCompressionProofCoordinator); if stop() interleaves with the callback's set, the !! can NPE. Capture timerId into a local val once after the null check (or use timerId?.let { vertx.cancelTimer(it) }).
    • ❌ won't fix
    • ❌ false positive
    • 🤖 to be fixed
    • ✅ addressed/fixed
  • coordinator/ethereum/finalization-monitor/src/main/kotlin/linea/finalization/FinalizationMonitorImpl.kt:55lastFinalizationUpdate.get() is read three times in close succession (lines 55, 58, 61) without a single snapshot; if start() (line 47) and action() race, the log line at 56-60 can report a previousFinalizedBlock that is not the value being replaced. Read it once into a local val previous = lastFinalizationUpdate.get() and use it consistently.
    • ❌ won't fix
    • ❌ false positive
    • 🤖 to be fixed
    • ✅ addressed/fixed
  • coordinator/ethereum/message-anchoring/src/main/kotlin/linea/anchoring/clients/L1MessageSentEventsFetcher.kt:77events.first() / events.last() are called inside log.debug after result.logs.map(...); if getLogsRollingForward returns a non-empty result whose logs list happens to be empty (filtered chunk), this throws NoSuchElementException from inside a future chain, aborting the polling tick. Guard with if (events.isNotEmpty()) around the log, or use firstOrNull/lastOrNull.
    • ❌ won't fix
    • ❌ false positive
    • 🤖 to be fixed
    • ✅ addressed/fixed
  • coordinator/ethereum/forced-transactions/src/main/kotlin/linea/ftx/ForcedTransactionsExecutionStatusUpdater.kt:153nextExpectedFtxNumber is @Volatile but updated in a read-modify-write inside the async processTransaction chain (line 153 sets it based on ftx.forcedTransactionNumber after reading it at lines 87, 93, 98); concurrent invocations of getUnprocessedForcedTransactions will race and may process the same FTX twice or skip one. Either guard the whole method with a mutex / single-threaded executor or use an AtomicReference<ULong> with compareAndSet.
    • ❌ won't fix
    • ❌ false positive
    • 🤖 to be fixed
    • ✅ addressed/fixed
  • coordinator/core/src/main/kotlin/linea/coordination/aggregation/AggregationProofHandler.kt:48 — The second .thenPeek block fires off a chained findHighestConsecutiveEndBlockNumber().thenApply(...) future but does not return it; the outer SafeFuture returned to the caller completes before this inner future does, and any successful consumer.accept happens after the caller already moved on. Replace the outer .thenPeek { ... } with .thenCompose { ... } so the inner future is part of the result.
    • ❌ won't fix
    • ❌ false positive
    • 🤖 to be fixed
    • ✅ addressed/fixed

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions