Skip to content

perf: reduce redundant RPC calls across hot paths#732

Merged
Niks988 merged 4 commits into
mainfrom
fix/rpc-usage-optimizations
May 27, 2026
Merged

perf: reduce redundant RPC calls across hot paths#732
Niks988 merged 4 commits into
mainfrom
fix/rpc-usage-optimizations

Conversation

@Niks988
Copy link
Copy Markdown
Contributor

@Niks988 Niks988 commented May 27, 2026

Summary

Targeted RPC optimizations that eliminate unnecessary eth_call round-trips on the hottest code paths without changing any observable behavior.

1. Cache IdentityStorage, ConvictionStakingStorage, StakingStorage at init

These three contracts were re-resolved from the Hub (hub.getContractAddress()) on every invocation of:

  • verifyACKIdentity() — called on every incoming peer ACK
  • verifySyncIdentity() — called on every sync message
  • getIdentityId() — called at boot + every prover tick
  • createChallenge() — called on prover rotation
  • ensureOperationalWalletsRegistered() — called at boot

Each resolution = 1 Hub read + 1 contract view = 2 wasted RPC calls per invocation. On relay nodes handling frequent peer traffic, this alone eliminates dozens of redundant Hub reads per minute.

Now cached in this.contracts during init(), same pattern as Identity, Profile, ParametersStorage, etc. A getIdentityStorage() helper falls back to per-call resolution if the contract wasn't available at init time.

2. Parallelize wallet balance reads in /api/wallets/balances

The sequential for loop issued 2×N serial RPC calls (getBalance + balanceOf per wallet). Now fires all reads concurrently via Promise.all, reducing wall-clock latency from O(N) to O(1) round-trips.

3. Parallelize identity lookups in ensureOperationalWalletsRegistered

The sequential getIdentityId() loop over operational keys is replaced with a single Promise.all batch, cutting boot-time identity checks from O(keys) to O(1) round-trips.

What's NOT changed

  • cacheTimeout: -1 on the provider — was added intentionally, may affect time-sensitive prover reads, needs separate analysis
  • Polling intervals (12s event poller, 30s prover tick) — already appropriate for L2 block times
  • HubResolutionCache for RandomSampling — already well-designed with TTL + invalidation

Test plan

  • Existing unit tests pass (no behavioral change — only Hub resolution is cached, view calls still hit RPC)
  • Verify relay nodes process ACKs and sync messages normally after deploy
  • Confirm /api/wallets/balances returns correct balances
  • Monitor RPC call volume before/after (expect ~30-50% reduction on relays)

Three targeted optimizations that cut unnecessary JSON-RPC round-trips
without changing any observable behavior:

1. Cache IdentityStorage, ConvictionStakingStorage, StakingStorage in
   init() alongside the other Hub-resolved contracts. Previously these
   were re-resolved from the Hub on every call to getIdentityId(),
   verifyACKIdentity(), verifySyncIdentity(), createChallenge(), and
   ensureOperationalWalletsRegistered() — each resolution is an
   extra eth_call to Hub.getContractAddress(). On relay nodes handling
   frequent peer ACKs, this alone eliminates dozens of redundant Hub
   reads per minute. A private getIdentityStorage() helper falls back
   to per-call resolution if the contract was not available at init.

2. Parallelize wallet balance reads in /api/wallets/balances: the
   sequential for-loop issued 2×N serial RPC calls (getBalance +
   balanceOf per wallet). Now fires all reads concurrently via
   Promise.all, reducing wall-clock latency from O(N) to O(1)
   round-trips.

3. Parallelize identity lookups in ensureOperationalWalletsRegistered:
   the sequential getIdentityId() loop is replaced with a single
   Promise.all batch, cutting startup identity-check time from
   O(keys) to O(1) round-trips.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread packages/chain/src/evm-adapter.ts Outdated
Comment thread packages/chain/src/evm-adapter.ts Outdated
Niks988 and others added 2 commits May 27, 2026 13:56
Address review feedback: init-time caching of IdentityStorage,
ConvictionStakingStorage, StakingStorage had no invalidation path —
a transient RPC failure at boot or a Hub rotation would leave stale
or undefined handles for the lifetime of the process.

Switch to lazy-cache-on-first-use: private getIdentityStorage(),
getConvictionStakingStorage(), getStakingStorage() helpers resolve
from Hub on the first call and cache the result. Removes init()
pre-resolution entirely so boot-time RPC failures self-heal on
next actual use. verifyACKIdentity() now uses the lazy helpers
instead of reading this.contracts directly.

Note: once rc.12 PR #689 (Hub rotation auto-recovery for all
boot-bound contracts) lands, these cached references will be
automatically invalidated on Hub.ContractChanged events via the
BOUND_CONTRACT_INVALIDATORS map — the lazy-cache pattern is
complementary to that approach.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread packages/chain/src/evm-adapter.ts
getIdentityStorage, getConvictionStakingStorage, getStakingStorage
are TS-private helpers that survive into the prototype at runtime.
They are not part of the ChainAdapter interface and don't need
mirroring on MockChainAdapter — same treatment as nextSigner,
walletKeyHash, resolveContract, etc.

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Codex review produced 3 comment(s) but all targeted lines outside the diff and were dropped. Check the workflow logs for details.

@Niks988 Niks988 merged commit 3f30100 into main May 27, 2026
39 checks passed
branarakic added a commit that referenced this pull request May 27, 2026
chore: merge main into release/rc.12 (RPC perf optimizations from #732)
matic031 pushed a commit to KilianTrunk/dkg that referenced this pull request Jun 2, 2026
…ginTrail#732)

Brings in PR OriginTrail#732 — perf: reduce redundant RPC calls across hot paths,
plus its 3 follow-up commits (lazy-cache refactor, CI retrigger, parity
test exemption).

Conflict resolution
-------------------
`packages/chain/src/evm-adapter.ts` :: `verifyACKIdentity` — kept rc.12's
delegation `(await this.verifyACKIdentityDetailed(...)).valid` because
the structured variant added in PR OriginTrail#711 is the canonical ACK-signer
gate now (operational-key purpose AND sharding-table membership,
matching the on-chain `KnowledgeAssetsV10._verifyACKSignature` check).
The legacy inline V10/V8 stake-fallback that lived here is superseded
by the ST-membership check inside `verifyACKIdentityDetailed`, which
StakingV10 updates atomically whenever a node crosses `minimumStake`.

OriginTrail#732's lazy-cache perf optimization (`getIdentityStorage()` etc.) is
unaffected — it's already applied to every other call site in the file
via the auto-merge. `verifyACKIdentityDetailed` continues to use
`resolveContract` directly; extending it to the lazy-cache helpers is
a follow-up nicety, not a correctness requirement.

Validation: `pnpm --filter @origintrail-official/dkg-chain test`
→ 449 tests pass.

Co-authored-by: Cursor <cursoragent@cursor.com>
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.

2 participants