Skip to content

add configurable historical balance health check for finalized block …#352

Open
Krish-vemula wants to merge 4 commits intodevelopfrom
cre/PLEX-2476
Open

add configurable historical balance health check for finalized block …#352
Krish-vemula wants to merge 4 commits intodevelopfrom
cre/PLEX-2476

Conversation

@Krish-vemula
Copy link
Contributor

@Krish-vemula Krish-vemula commented Feb 11, 2026

Summary

Adds an optional health check that verifies RPC nodes can access historical blockchain state at finalized blocks.

Changes

New Configuration Options ([EVM.NodePool]):

  • HistoricalBalanceCheckEnabled (bool, default: false) - Enables the historical state health check
  • HistoricalBalanceCheckAddress (address, default: 0x0...0) - The address used for the eth_getBalance probe

Implementation:

  • During RPC node polling (every PollInterval), if enabled, executes eth_getBalance at the finalized block
  • For chains with FinalityTagEnabled = true : queries using the "finalized" block tag
  • For chains with FinalityTagEnabled = false : queries at latest - FinalityDepth
  • Failures count toward PollFailureThreshold - after enough consecutive failures, the node is marked unreachable and traffic fails over to healthy nodes

Configuration Example

[[EVM]]
ChainID = '1'
FinalityTagEnabled = true

[EVM.NodePool]
PollInterval = '10s'
PollFailureThreshold = 5
HistoricalBalanceCheckEnabled = true
HistoricalBalanceCheckAddress = '0x0000000000000000000000000000000000000000'

@github-actions
Copy link
Contributor

github-actions bot commented Feb 11, 2026

📊 API Diff Results

No modules to analyze

View full report

@Krish-vemula Krish-vemula marked this pull request as ready for review February 18, 2026 23:02
@Krish-vemula Krish-vemula requested review from a team as code owners February 18, 2026 23:02
jmank88
jmank88 previously approved these changes Feb 25, 2026
return tc.NodeFinalizedBlockPollInterval
}

func (tc TestNodePoolConfig) HistoricalBalanceCheckEnabled() bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This flag should be part of the multinode config. As multinode will perform the polling

return version, nil
}

func (r *RPCClient) checkHistoricalStateAtFinalized(ctx context.Context) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be public method, so that we can poll it from multinode

…cept probeAddress parameter, fall back to EVM config if empty.
return tc.ExternalRequestMaxResponseSizeVal
}

func (tc TestNodePoolConfig) FinalizedStateCheckEnabled() bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need two feature flags?

// This is used to detect non-archive nodes that cannot serve state queries for older blocks.
// If probeAddress is provided, it uses that address; otherwise falls back to the configured HistoricalBalanceCheckAddress.
// Returns nil immediately if historical balance check is not enabled and no probeAddress is provided.
func (r *RPCClient) CheckFinalizedStateAvailability(ctx context.Context, probeAddress string) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, there is no need to allow overriding the address. It adds complexity but we do not know if it'll be used.

blockNumber = big.NewInt(finalizedHeight)
}
_, err := r.BalanceAt(ctx, addr, blockNumber)
if err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to check the error against the regex here and return a dedicated error defined in the framework repo.
This way, we abstract away the EVM-specific way of checking the availability of finalized states.

}

func (n *NodePoolConfig) HistoricalBalanceCheckEnabled() bool {
if n.C.HistoricalBalanceCheckEnabled == nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Configs are built in a way that the value should never be nil. So here we should skip checks to keep everything consistent.

Also, you need to add new config fields to setFrom method.
It is required to handle overrides properly.
By overrides I mean the ability to define a common value in the fallback file and override it in a chain-specific TOML file or during runtime via a custom TOML file.

FinalizedStateCheckEnabledVal bool
FinalizedStateCheckAddressVal string
FinalizedStateCheckFailureThresholdVal uint32
FinalizedStateUnavailableRegexVal string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This regex should be defined as part of ClientErrors

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.

3 participants