Skip to content

feat(framework): support solidity conditional shutdown#6734

Open
317787106 wants to merge 1 commit intotronprotocol:developfrom
317787106:feature/solidity_shutdown
Open

feat(framework): support solidity conditional shutdown#6734
317787106 wants to merge 1 commit intotronprotocol:developfrom
317787106:feature/solidity_shutdown

Conversation

@317787106
Copy link
Copy Markdown
Collaborator

Summary

Closes #6610.

Refactors SolidityNode into a Spring-managed component with proper lifecycle support, enabling it to respond to conditional shutdown signals (block height, block time, block count) already implemented in TronNetDelegate.processBlock. The previous static start() approach bypassed Spring entirely, so the shutdown conditions could never be reached during solidity sync.

Problem

SolidityNode previously initialized its own TronApplicationContext, created a SolidityNode instance manually, and called appT.blockUntilShutdown() in a separate JVM entry path. This architecture had two consequences:

  1. Shutdown conditions were unreachable. The conditional-shutdown logic lives in TronNetDelegate.processBlock (checking latestSolidityNumShutDown), but SolidityNode called Manager.pushVerifiedBlock -> Manager.pushBlock — a path that never passes through TronNetDelegate. The shutdown signal was therefore silently ignored.
  2. No graceful resource cleanup. Thread pools and gRPC connections were abandoned on JVM exit rather than being released through Spring's @PreDestroy lifecycle.

Changes

SolidityNode (framework)

  • Converted from a static utility class to a Spring @Component with @Conditional(SolidityCondition.class) — only registered when --solidity flag is set.
  • Implements ApplicationListener<ContextClosedEvent>: sets flag = false at the very start of context close, before any @PreDestroy methods run on other beans, preventing block pushes into a partially-shut-down system.
  • @PostConstruct init(): initialises named ExecutorService thread pools via ExecutorServiceManager. gRPC client and remoteBlockNum are initialised in run() to keep init lightweight.
  • @PreDestroy shutdown(): sets flag = false (safety net), shuts down both executors via ExecutorServiceManager.shutdownAndAwaitTermination, and closes the gRPC client cleanly.
  • processSolidityBlock() uses blockQueue.poll(timeout) instead of take(), and handles InterruptedException explicitly so thread interruption during executor shutdown is clean.
  • getBlockByNum(): loop changed to while (flag); throws RuntimeException("SolidityNode is closing.") when flag is false — avoids NullPointerException from blockQueue.put(null).
  • getLastSolidityBlockNum(): loop changed to while (flag); returns 0 when flag is false — lets the caller's while (flag) loop exit quietly without a misleading error log.
  • loopProcessBlock() now calls tronNetDelegate.pushVerifiedBlock() instead of dbManager.pushVerifiedBlock(), routing blocks through the shutdown-condition check in processBlock.

TronNetDelegate (framework)

  • Added pushVerifiedBlock(BlockCapsule): sets generatedByMyself = true and delegates to processBlock(block, true), which already contains the conditional-shutdown logic. Includes timing log consistent with the removed Manager method.
  • Shutdown condition in processBlock uses <= instead of == because pushBlock may commit multiple blocks in a single batch write, causing the DB header number to jump past the target block number and never equal it exactly.

Manager (framework)

  • Removed pushVerifiedBlock(). Block push for solidity sync now goes through TronNetDelegate.

FullNode (framework)

  • In solidity mode, trustNodeAddr validation and p2pDisable = true are now enforced before the Spring context is created.
  • After appT.startup(), retrieves the SolidityNode bean from the context and calls run() — reusing the same application lifecycle rather than spawning a second context.

Tests

  • testSolidityGrpcCall — verifies gRPC connectivity through the running RpcApiService.
  • testSolidityNodeHttpApiService — verifies HTTP service start/stop idempotency.
  • testExecutorsInitializedOnStartup@PostConstruct creates both executors before run() is called.
  • testOnApplicationEventSetsFlagFalseContextClosedEvent sets flag = false before any @PreDestroy runs.
  • testGetBlockByNumThrowsWhenClosedRuntimeException (not null) is thrown when flag = false.
  • testGetLastSolidityBlockNumReturnsZeroWhenClosed — returns 0 when flag = false for a quiet loop exit.
  • testSolidityConditionMatchesWhenSolidityFlagSetSolidityCondition.matches() returns true with --solidity.

…shutdown

- Convert SolidityNode from static utility to @conditional @component so it
  only registers when --solidity is passed
- Implement ApplicationListener<ContextClosedEvent> to set flag=false before
  any @PreDestroy method fires, stopping worker threads promptly
- Add pushVerifiedBlock() to TronNetDelegate to route solidity sync blocks
  through the conditional-shutdown check (<=, not ==, because a single
  batch write can advance the DB header past the target number)
- Guard saveLatestSolidifiedBlockNum in loopProcessBlock: skip the write when
  hitDown is true, preventing a phantom block-num from persisting when the
  block was never actually pushed to BlockStore
- Add unit tests covering lifecycle hooks, all retry paths, interrupt handling,
  and the hitDown save-guard; SolidityNode line coverage 28% -> 81%

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions github-actions Bot requested a review from xxo1shine April 30, 2026 03:14
@317787106 317787106 changed the title feat(framework): refactor SolidityNode as Spring component with safe … feat(framework): support solidity conditional shutdown Apr 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] SolidityNode supports conditional shutdown

1 participant