perf: reduce redundant RPC calls across hot paths#732
Merged
Conversation
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>
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>
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>
branarakic
approved these changes
May 27, 2026
There was a problem hiding this comment.
Codex review produced 3 comment(s) but all targeted lines outside the diff and were dropped. Check the workflow logs for details.
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Targeted RPC optimizations that eliminate unnecessary
eth_callround-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 ACKverifySyncIdentity()— called on every sync messagegetIdentityId()— called at boot + every prover tickcreateChallenge()— called on prover rotationensureOperationalWalletsRegistered()— called at bootEach 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.contractsduringinit(), same pattern as Identity, Profile, ParametersStorage, etc. AgetIdentityStorage()helper falls back to per-call resolution if the contract wasn't available at init time.2. Parallelize wallet balance reads in
/api/wallets/balancesThe sequential
forloop issued2×Nserial RPC calls (getBalance+balanceOfper wallet). Now fires all reads concurrently viaPromise.all, reducing wall-clock latency fromO(N)toO(1)round-trips.3. Parallelize identity lookups in
ensureOperationalWalletsRegisteredThe sequential
getIdentityId()loop over operational keys is replaced with a singlePromise.allbatch, cutting boot-time identity checks fromO(keys)toO(1)round-trips.What's NOT changed
cacheTimeout: -1on the provider — was added intentionally, may affect time-sensitive prover reads, needs separate analysisHubResolutionCachefor RandomSampling — already well-designed with TTL + invalidationTest plan
/api/wallets/balancesreturns correct balances