MF3-H03: fix(nitronode): skip unsupported NodeBalanceUpdated tokens instead of fatal#827
Conversation
…nstead of fatal A NodeBalanceUpdated event emitted for a token that is not configured in the node asset store previously caused handleNodeBalanceUpdated() to return an error. That error propagated through HandleEvent() before StoreContractEvent() ran, so the event was never recorded; the listener unsubscribed and main() escalated via logger.Fatal(). Because depositToNode() accepts any ERC20, this was a permissionless, low-cost way to stop nitronode event processing, and the unrecorded event would be replayed on restart, requiring manual intervention. Introduce a typed core.ErrTokenNotSupported sentinel returned by the memory asset store when a token is not configured. handleNodeBalanceUpdated() now detects it via errors.Is, logs and skips the balance update, and returns nil so HandleEvent() records the event and processing continues. Genuine store errors still propagate, so only the deterministic "not configured" case is skipped. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
nksazonov
left a comment
There was a problem hiding this comment.
Excellent work — the MF3-H03 fix is correct, minimal, and the new subtest locks in the right invariants (HandleEvent returns nil, processedSuccess=true, balance handler not called, StoreContractEvent is called). Verified locally: targeted test passes, go vet clean across the touched packages, CI fully green.
ihsraham
left a comment
There was a problem hiding this comment.
Requesting changes for one remaining H-03 closure gap. The typed skip is the right shape for configured chains with unknown tokens, but an enabled chain with no token map still returns a generic error and can hit the old fatal listener path. I left the exact branch inline.
…ed tokens A ChannelHub-enabled chain with no configured token entries (e.g. world_chain) caused GetTokenAsset/GetTokenDecimals to return a generic "blockchain not supported" error that the reactor did not recognize as skippable, letting an arbitrary NodeBalanceUpdated event bubble out and stop the listener — the same fatal path MF3-H03 set out to close. - memory_store: wrap core.ErrTokenNotSupported in the no-token-map branch of both GetTokenAsset and GetTokenDecimals - channel_hub_reactor: extract skipIfUnsupportedToken helper to de-dup the two skip-and-record guards; document the no-replay (dedup, not replay) semantics - tests: cover the GetTokenDecimals skip branch in the reactor and add memory_store regression cases for an enabled chain with no token entries Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ihsraham
left a comment
There was a problem hiding this comment.
Approved for H-03. The no-token-map branch now returns the same ErrTokenNotSupported sentinel as the unknown-token branch, so the world_chain/chain 480 case skips cleanly and still reaches StoreContractEvent for dedup. The added asset/decimals and reactor tests cover the closure path; no remaining blockers from me.
There was a problem hiding this comment.
All three review comments addressed in 887b14e — verified locally: mirror subtest for the GetTokenDecimals skip branch, skipIfUnsupportedToken helper de-dups both guards, helper doc comment spells out the no-replay (dedup, not replay) semantics. Bonus follow-up from @ihsraham on the enabled-chain-no-tokens path (e.g. world_chain 480) also wrapped in core.ErrTokenNotSupported with memory_store regressions. go test ./pkg/blockchain/evm/... ./nitronode/store/memory/... green. LGTM.
Finding (MF3-H03)
ChannelHubReactor.HandleEvent()treated any handler error as fatal: it returned beforeStoreContractEvent(), soListener.processEvents()unsubscribed andmain()exited vialogger.Fatal().depositToNode()accepts any ERC20 and emitsNodeBalanceUpdated.handleNodeBalanceUpdated()required the token to be configured in the node asset store, so depositing an unconfigured ERC20 was a permissionless, low-cost way to stop nitronode event processing. Since the failing event was never recorded, it replayed on restart — recovery needed manual intervention or a config/code change.Fix
core.ErrTokenNotSupportedsentinel, returned (wrapped) by the memory asset store'sGetTokenAsset/GetTokenDecimalswhen a token is not configured.handleNodeBalanceUpdated()checkserrors.Is(err, core.ErrTokenNotSupported), logs a warning, skips the balance update, and returnsnil.HandleEvent()then records the event viaStoreContractEvent(), so it is not replayed, the listener stays subscribed, and the process does not die.Out of scope (per finding's optional suggestion): broader listener-level error classification so handler failures don't all collapse to
logger.Fatal. Can follow up separately.Tests
unsupported token is skipped and recorded: assertsHandleEventreturns nil,StoreContractEventis called, the balance handler is NOT called, and the processed callback reports success.go test ./pkg/blockchain/evm/... ./nitronode/store/memory/...andgo vetpass.🤖 Generated with Claude Code