Skip to content

Improve Transaction Throughput#744

Merged
cwilvx merged 7 commits intostabilisationfrom
omni_throughput_debug
Apr 1, 2026
Merged

Improve Transaction Throughput#744
cwilvx merged 7 commits intostabilisationfrom
omni_throughput_debug

Conversation

@cwilvx
Copy link
Copy Markdown
Contributor

@cwilvx cwilvx commented Apr 1, 2026

Refactor HandleGCR to support batch application of GCR edits with in-mem GCR entities.

Summary by CodeRabbit

  • New Features

    • Batch GCR edit processing for more efficient transaction handling and side-effect execution.
  • Bug Fixes

    • Removed abrupt process exit during peer bootstrap on genesis mismatch; now logs and continues.
  • Configuration

    • Updated default devnet ports and docker-compose mappings.
    • Reduced RPC readiness wait timeout from 120s to 3s.
  • Chores

    • Refactored GCR/mempool flows to operate on in-memory account state and deferred side-effects.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 1, 2026

Walkthrough

Refactors GCR processing from per-edit repository operations to an in-memory, batch-apply model with preloaded GCRMain entities, deferred incentive side-effects, and new batch APIs (prepareAccounts, applyTransaction, applyTransactions, saveGCREditChanges). Also updates mempool/consensus call sites and several testing port defaults.

Changes

Cohort / File(s) Summary
Core GCR Refactoring
src/libs/blockchain/gcr/handleGCR.ts, src/libs/blockchain/gcr/gcr.ts
Added batch APIs (prepareAccounts, applyTransaction, applyTransactions, saveGCREditChanges), extended GCRResult with entity/sideEffect, added apply-to-entity helpers and snapshots, and switched callers to batch flow.
Balance & Nonce Routines
src/libs/blockchain/gcr/gcr_routines/GCRBalanceRoutines.ts, src/libs/blockchain/gcr/gcr_routines/GCRNonceRoutines.ts
apply now accepts in-memory accountGCR (DB persistence removed); added applyToEntity helpers; rollback/validation adjusted to operate on provided entity.
Identity Routines
src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts, src/libs/blockchain/gcr/gcr_routines/routines/...
All identity routines changed to accept accountGCR: GCRMain, removed internal repository/simulate logic, and return deferred sideEffect callbacks for incentive award/deduction.
Rewards & Utilities
src/libs/blockchain/gcr/gcr_routines/routines/rewardsRoutines.ts, .../web2Routines.ts, .../zkRoutines.ts, .../utils.ts
Award/rollback functions accept in-memory entities; ZK routines signature cleanup; isFirstConnection now obtains repository internally.
Consensus & Mempool Integration
src/libs/blockchain/routines/Sync.ts, src/libs/consensus/v2/PoRBFT.ts, src/libs/consensus/v2/routines/mergeMempools.ts, src/libs/blockchain/mempool_v2.ts, src/libs/network/endpointExecution.ts
Replaced per-tx applyToTx invocations with batch prepareAccounts + applyTransaction/applyTransactions; adjusted mempool receive/exclusion tracking and merged-mempool filtering.
L2PS Batch Integration
src/libs/l2ps/L2PSConsensus.ts
Adopted batch apply flow (mock tx + prepareAccounts + applyTransaction) and changed mock tx type to l2psEncryptedTx; uses saveGCREditChanges when persisting.
Error Handling
src/libs/peer/routines/peerBootstrap.ts
Removed unconditional process.exit(1) on genesis-hash mismatch; now logs and continues.
Testing & Devnet Defaults
testing/devnet/..., testing/loadgen/..., testing/scripts/..., testing/docker-compose.perf.yml
Adjusted default node and Omni ports across devnet artifacts, removed docker-compose version, removed extra -- in start script, and reduced loadgen default timeouts from 120s to 3s.

Sequence Diagram(s)

sequenceDiagram
    participant Consensus
    participant HandleGCR
    participant AccountLoader as AccountLoader
    participant InMemory as In-Memory<br/>GCRMain
    participant Persister

    Consensus->>HandleGCR: applyTransactions(txs, simulate=false)
    HandleGCR->>AccountLoader: prepareAccounts(txs)
    AccountLoader-->>HandleGCR: Map<pubkey,GCRMain>
    loop per tx
        HandleGCR->>InMemory: applyTransaction(accounts, tx, isRollback?, simulate?)
        InMemory-->>HandleGCR: GCRApplyResult (accounts, sideEffects, status)
    end
    HandleGCR->>Persister: saveGCREditChanges(accounts, sideEffects)
    Persister-->>HandleGCR: persisted / side-effects executed
    HandleGCR-->>Consensus: result
Loading
sequenceDiagram
    participant IdentityRoutine
    participant GCRMainEntity as GCRMain Entity
    participant IncentiveManager

    Note over IdentityRoutine: applyWeb2IdentityAdd(editOp, accountGCR)
    IdentityRoutine->>GCRMainEntity: mutate identities.web2[...] in-memory
    IdentityRoutine-->>IdentityRoutine: return { success, entity, sideEffect: awardPoints }
    Note over IdentityRoutine,IncentiveManager: later execution
    IdentityRoutine->>IncentiveManager: awardPoints() -> IncentiveManager.*Linked()
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related issues

Possibly related PRs

Suggested labels

Review effort 5/5

Poem

🐰 Soft hops through ledger fields I go,

In-memory burrows where batch edits flow.
Side-effects tucked in a cozy heap,
Persisted later while the network sleeps.
Carrots for incentives, stored for a while — hop!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Improve Transaction Throughput' is vague and generic, using non-descriptive phrasing that does not clearly convey the primary technical change (refactoring HandleGCR for batch GCR edit application). Consider revising the title to be more specific about the main change, such as 'Refactor HandleGCR to support batch GCR edit application with in-memory entities' to better reflect the substantial architectural changes in the changeset.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch omni_throughput_debug

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@qodo-code-review
Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Batch GCR edit processing with in-memory entity handling and infrastructure updates

✨ Enhancement 🐞 Bug fix

Grey Divider

Walkthroughs

Description
• Refactored HandleGCR to support batch application of GCR edits with in-memory entity processing,
  eliminating per-transaction database operations
• Added new batch processing methods: applyTransactions(), applyTransaction(),
  prepareAccounts(), and saveGCREditChanges() for efficient bulk operations
• Converted all identity routine modules (TLSN, Ethos, Human Passport, Nomis, XM, Web2, PQC, UD) to
  accept in-memory GCRMain entities instead of database repositories
• Refactored balance and nonce routines with new applyToEntity static methods for in-memory batch
  processing
• Updated consensus, sync, and endpoint execution layers to use new batch GCR processing API
• Moved incentive point operations and database saves into deferred async sideEffect callbacks for
  post-batch execution
• Improved mempool transaction filtering with better error handling for duplicate key constraints
• Updated testing infrastructure port configurations (53552/53554 → 53555/53557) and reduced RPC
  wait timeouts from 120s to 3s
• Fixed docker compose command syntax in devnet run script
Diagram
flowchart LR
  A["Transaction Input"] --> B["prepareAccounts"]
  B --> C["In-Memory GCRMain Entity"]
  C --> D["applyTransaction"]
  D --> E["Identity Routines<br/>Balance/Nonce/Rewards"]
  E --> F["Entity + SideEffect"]
  F --> G["saveGCREditChanges"]
  G --> H["Bulk Database Update"]
  I["Consensus/Sync/Endpoint"] --> A
Loading

Grey Divider

File Changes

1. src/libs/blockchain/gcr/handleGCR.ts ✨ Enhancement +567/-184

Batch GCR edit processing with in-memory entity handling

• Refactored HandleGCR to support batch application of GCR edits with in-memory entity processing
• Added new methods applyTransactions(), applyTransaction(), prepareAccounts(), and
 saveGCREditChanges() for efficient batch processing
• Introduced isBatchableGCREdit() type guard and helper functions for pubkey normalization
• Modified applyGCREdit() to accept in-memory GCRMain entity instead of fetching from database
• Added GCRApplyResult interface and updated GCRResult interface with entity and sideEffect
 fields
• Implemented bulk SQL update for assignedTxs via bulkUpdateAssignedTxs() method

src/libs/blockchain/gcr/handleGCR.ts


2. src/libs/l2ps/L2PSConsensus.ts ✨ Enhancement +303/-128

L2PS consensus integration with batch GCR processing

• Updated applyGCREdits() to use new batch processing API with prepareAccounts() and
 applyTransaction()
• Refactored proof rollback logic to use batch transaction processing instead of individual edit
 application
• Modified createMockTx() to include gcr_edits in transaction content
• Replaced individual edit application with saveGCREditChanges() for deferred database commits
• Updated imports to use isBatchableGCREdit type guard

src/libs/l2ps/L2PSConsensus.ts


3. src/libs/network/endpointExecution.ts ✨ Enhancement +63/-58

Endpoint execution updated for batch GCR processing

• Updated transaction execution to use new batch GCR processing API
• Replaced HandleGCR.applyToTx() calls with prepareAccounts() and applyTransaction()
• Added case blocks for transaction type handling to improve code organization
• Minor formatting improvements for code consistency

src/libs/network/endpointExecution.ts


View more (33)
4. src/libs/consensus/v2/PoRBFT.ts ✨ Enhancement +45/-66

Consensus routine optimized with batch GCR processing

• Refactored applyGCREditsFromMergedMempool() to use new batch processing with
 Chain.getExistingTransactionHashes()
• Simplified rollbackGCREditsFromTxs() to delegate to HandleGCR.applyTransactions()
• Updated return type from tuple to object with successfulTxs and failedTxs properties
• Changed log levels from info to debug for reduced verbosity
• Added batch transaction hash filtering before processing

src/libs/consensus/v2/PoRBFT.ts


5. src/libs/blockchain/routines/Sync.ts ✨ Enhancement +45/-50

Sync routine simplified with batch GCR processing

• Simplified syncGCRTables() to use new HandleGCR.applyTransactions() batch API
• Removed per-transaction loop and individual applyToTx() calls
• Updated return type to match new batch processing result format
• Minor formatting improvements for code consistency

src/libs/blockchain/routines/Sync.ts


6. src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts ✨ Enhancement +115/-143

Identity routines refactored for in-memory entity processing

• Changed apply() method signature to accept in-memory GCRMain entity instead of repository
• Removed simulate parameter from most routine calls (kept for zk transactions)
• Updated all identity routine calls to pass accountGCR entity directly
• Removed repository parameter from all downstream identity operation functions

src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts


7. src/libs/blockchain/gcr/gcr_routines/routines/tlsnRoutines.ts ✨ Enhancement +19/-35

TLSN identity routines converted to in-memory processing

• Refactored applyTLSNIdentityAdd() and applyTLSNIdentityRemove() to accept in-memory entity
• Removed database repository parameter and simulate flag
• Moved incentive point operations into async sideEffect callbacks
• Updated return values to include entity and sideEffect fields
• Removed direct database save operations

src/libs/blockchain/gcr/gcr_routines/routines/tlsnRoutines.ts


8. src/libs/blockchain/gcr/gcr_routines/routines/ethosRoutines.ts ✨ Enhancement +33/-49

Ethos identity routines converted to in-memory processing

• Refactored applyEthosIdentityUpsert() and applyEthosIdentityRemove() for in-memory entity
 processing
• Removed repository and simulate parameters
• Moved incentive operations into async sideEffect callbacks
• Updated return values to include entity and sideEffect fields
• Removed direct database save calls

src/libs/blockchain/gcr/gcr_routines/routines/ethosRoutines.ts


9. src/libs/blockchain/gcr/gcr_routines/routines/humanpassportRoutines.ts ✨ Enhancement +38/-41

Human Passport identity routines converted to in-memory processing

• Refactored applyHumanPassportIdentityAdd() and applyHumanPassportIdentityRemove() for
 in-memory processing
• Removed repository and simulate parameters
• Moved incentive operations into async sideEffect callbacks
• Updated return values to include entity and sideEffect fields
• Removed direct database save operations

src/libs/blockchain/gcr/gcr_routines/routines/humanpassportRoutines.ts


10. src/libs/blockchain/gcr/gcr_routines/routines/nomisRoutines.ts ✨ Enhancement +27/-47

Nomis identity routines converted to in-memory processing

• Refactored applyNomisIdentityUpsert() and applyNomisIdentityRemove() for in-memory entity
 processing
• Removed repository and simulate parameters
• Moved incentive operations into async sideEffect callbacks
• Updated return values to include entity and sideEffect fields
• Removed direct database save calls

src/libs/blockchain/gcr/gcr_routines/routines/nomisRoutines.ts


11. src/libs/blockchain/gcr/gcr_routines/routines/xmRoutines.ts ✨ Enhancement +22/-32

XM identity routines converted to in-memory processing

• Refactored applyXmIdentityAdd() and applyXmIdentityRemove() for in-memory entity processing
• Removed repository and simulate parameters
• Moved incentive operations into async sideEffect callbacks
• Updated return values to include entity and sideEffect fields
• Removed direct database save operations

src/libs/blockchain/gcr/gcr_routines/routines/xmRoutines.ts


12. src/libs/blockchain/gcr/gcr_routines/routines/web2Routines.ts ✨ Enhancement +16/-26

Web2 identity routines converted to in-memory processing

• Refactored applyWeb2IdentityAdd() and applyWeb2IdentityRemove() for in-memory entity
 processing
• Removed repository and simulate parameters
• Moved incentive operations into async sideEffect callbacks
• Updated return values to include entity and sideEffect fields
• Removed direct database save calls

src/libs/blockchain/gcr/gcr_routines/routines/web2Routines.ts


13. src/libs/blockchain/gcr/gcr_routines/routines/pqcRoutines.ts ✨ Enhancement +11/-29

PQC identity routines converted to in-memory processing

• Refactored applyPqcIdentityAdd() and applyPqcIdentityRemove() for in-memory entity processing
• Removed repository and simulate parameters
• Updated return values to include entity field
• Removed direct database save operations

src/libs/blockchain/gcr/gcr_routines/routines/pqcRoutines.ts


14. testing/loadgen/src/features/omni/shared.ts ⚙️ Configuration changes +1/-1

Omni port offset configuration adjustment

• Changed default OMNI_PORT_OFFSET from 10 to 1

testing/loadgen/src/features/omni/shared.ts


15. src/libs/blockchain/gcr/gcr_routines/GCRBalanceRoutines.ts ✨ Enhancement +69/-13

Refactor balance routine for batch in-memory processing

• Refactored apply method to accept accountGCR entity directly instead of repository and
 simulate flag
• Removed database operations from the main apply method (commented out repository queries and
 saves)
• Added new applyToEntity static method for in-memory batch processing of balance edits
• Updated return type to include the modified entity in the result

src/libs/blockchain/gcr/gcr_routines/GCRBalanceRoutines.ts


16. src/libs/blockchain/gcr/gcr_routines/routines/udRoutines.ts ✨ Enhancement +16/-31

Refactor UD identity routines for deferred side effects

• Changed function signatures to accept accountGCR entity directly instead of repository and
 simulate parameters
• Removed database save operations and wrapped point award logic in async sideEffect functions
• Updated return types to include entity and sideEffect callback for deferred operations
• Simplified applyUdIdentityRemove by removing account lookup logic

src/libs/blockchain/gcr/gcr_routines/routines/udRoutines.ts


17. src/libs/blockchain/gcr/gcr_routines/GCRNonceRoutines.ts ✨ Enhancement +37/-13

Refactor nonce routine for batch in-memory processing

• Refactored apply method to accept accountGCR entity directly instead of repository and
 simulate flag
• Removed database operations and account lookup from the main apply method
• Added new applyToEntity static method for in-memory batch processing of nonce edits
• Updated return type to include the modified entity

src/libs/blockchain/gcr/gcr_routines/GCRNonceRoutines.ts


18. src/libs/blockchain/mempool_v2.ts ✨ Enhancement +30/-23

Improve mempool transaction filtering and error handling

• Moved consensus loop check to the beginning of receive method for early exit
• Refactored transaction filtering logic to use a noSendBackTxs Map instead of incomingSet
 object
• Improved error handling to specifically catch duplicate key constraint violations
• Changed log level from error to info for already-existing transactions

src/libs/blockchain/mempool_v2.ts


19. src/libs/blockchain/gcr/gcr_routines/routines/rewardsRoutines.ts ✨ Enhancement +4/-21

Refactor rewards routines to remove database operations

• Removed database save operations from both applyAwardPoints and applyAwardPointsRollback
 functions
• Removed gcrMainRepository and simulate parameters from function signatures
• Updated return types to include the modified entity
• Simplified function logic by removing conditional save logic

src/libs/blockchain/gcr/gcr_routines/routines/rewardsRoutines.ts


20. testing/scripts/testenv-doctor.ts ⚙️ Configuration changes +7/-3

Update RPC target ports and add logging

• Updated default RPC target ports from 53552 and 53554 to 53555 and 53557
• Added console logging for probe results and error handling
• Refactored probe result assignment to intermediate variable before return

testing/scripts/testenv-doctor.ts


21. testing/loadgen/src/features/l2ps/shared.ts ⚙️ Configuration changes +4/-4

Update L2PS targets and reduce wait timeouts

• Updated default live targets ports from 53552 and 53554 to 53555 and 53557
• Reduced wait timeouts from 120 seconds to 3 seconds for RPC and TX readiness checks

testing/loadgen/src/features/l2ps/shared.ts


22. testing/scripts/run-suite.ts ⚙️ Configuration changes +2/-2

Update default local target ports

• Updated default local targets from ports 53552 and 53554 to 53555 and 53557
• Updated TARGETS environment variable default in scenario execution

testing/scripts/run-suite.ts


23. src/libs/blockchain/gcr/gcr.ts ✨ Enhancement +7/-5

Update GCR transaction application flow

• Fixed indentation for ternary operator in account identifier logic
• Refactored transaction application to use new prepareAccounts and applyTransaction methods
• Updated variable names from editResults to simulateResult for clarity

src/libs/blockchain/gcr/gcr.ts


24. src/libs/blockchain/gcr/gcr_routines/routines/utils.ts ✨ Enhancement +2/-1

Refactor utils to use datasource for repository access

• Added import for dataSource from datasource module
• Refactored isFirstConnection to obtain gcrMainRepository from dataSource instead of
 parameter
• Removed gcrMainRepository parameter from function signature

src/libs/blockchain/gcr/gcr_routines/routines/utils.ts


25. testing/loadgen/src/token_shared.ts ⚙️ Configuration changes +1/-1

Reduce RPC wait timeout

• Reduced RPC readiness wait timeout from 120 seconds to 3 seconds

testing/loadgen/src/token_shared.ts


26. testing/loadgen/src/features/tlsnotary/shared.ts ⚙️ Configuration changes +1/-1

Update TLS notary target ports

• Updated default TLS notary targets from ports 53552 and 53554 to 53555 and 53557

testing/loadgen/src/features/tlsnotary/shared.ts


27. testing/scripts/run-chaos-token-script-transfer.sh ⚙️ Configuration changes +4/-4

Update chaos test script target ports

• Updated default targets from ports 53552 and 53554 to 53555 and 53557
• Updated node service URL mappings for node-2, node-3, and node-4

testing/scripts/run-chaos-token-script-transfer.sh


28. testing/devnet/scripts/setup.sh 📝 Documentation +3/-3

Update devnet setup documentation

• Updated node endpoint documentation from ports 53552 and 53554 to 53555 and 53557

testing/devnet/scripts/setup.sh


29. testing/devnet/scripts/generate-peerlist.sh ⚙️ Configuration changes +3/-3

Update peerlist generation default ports

• Updated default node ports from 53552 and 53554 to 53555 and 53557

testing/devnet/scripts/generate-peerlist.sh


30. testing/devnet/docker-compose.yml ⚙️ Configuration changes +17/-18

Update docker-compose port configuration

• Removed docker-compose version specification
• Updated node RPC and OmniProtocol ports for all four nodes
• Changed OMNI_PORT mappings from 53561-53564 to 53552, 53554, 53556, 53558

testing/devnet/docker-compose.yml


31. testing/docker-compose.perf.yml ⚙️ Configuration changes +1/-1

Update performance test targets

• Updated TARGETS environment variable from ports 53552 and 53554 to 53555 and 53557

testing/docker-compose.perf.yml


32. testing/devnet/.env.example ⚙️ Configuration changes +7/-7

Update environment example port configuration

• Updated NODE2_PORT, NODE3_PORT, NODE4_PORT from 53552, 53553, 53554 to 53553, 53555, 53557
• Updated OMNI_PORT values from 53561-53564 to 53552, 53554, 53556, 53558

testing/devnet/.env.example


33. testing/devnet/run-devnet 🐞 Bug fix +1/-1

Fix docker compose command syntax

• Fixed docker compose command by removing extra -- separator before --no-tui flag

testing/devnet/run-devnet


34. src/libs/blockchain/gcr/gcr_routines/routines/zkRoutines.ts Additional files +0/-2

...

src/libs/blockchain/gcr/gcr_routines/routines/zkRoutines.ts


35. src/libs/consensus/v2/routines/mergeMempools.ts Additional files +0/-3

...

src/libs/consensus/v2/routines/mergeMempools.ts


36. src/libs/peer/routines/peerBootstrap.ts Additional files +0/-1

...

src/libs/peer/routines/peerBootstrap.ts


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review bot commented Apr 1, 2026

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Undefined gcr_edits spread🐞 Bug ≡ Correctness
Description
HandleGCR.applyTransaction spreads tx.content.gcr_edits before checking it exists, which throws
when gcr_edits is missing/non-iterable. This can crash consensus/sync/mempool paths that process
valid transactions without any GCR edits.
Code

src/libs/blockchain/gcr/handleGCR.ts[R401-405]

+        const gcrEdits = [...tx.content.gcr_edits]
+
+        // Skip txs without GCR edits (valid but no state changes)
+        if (!gcrEdits || !Array.isArray(gcrEdits) || gcrEdits.length === 0) {
+            // successfulTxs.push(tx.hash)
Evidence
applyTransaction constructs gcrEdits via spread before any null/array guard, so undefined will
throw immediately; meanwhile prepareAccounts explicitly treats missing gcr_edits as a valid case
(continues), indicating such txs are expected.

src/libs/blockchain/gcr/handleGCR.ts[395-414]
src/libs/blockchain/gcr/handleGCR.ts[332-341]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`HandleGCR.applyTransaction()` does `const gcrEdits = [...tx.content.gcr_edits]` before verifying `gcr_edits` exists/is an array. If `gcr_edits` is `undefined` (valid for some txs), this throws (`TypeError: ... is not iterable`) and crashes consensus/sync/simulation.
### Issue Context
`prepareAccounts()` already treats missing/non-array `gcr_edits` as valid and continues, so `applyTransaction()` must be equally defensive.
### Fix Focus Areas
- src/libs/blockchain/gcr/handleGCR.ts[395-414]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Iterator toArray crash 🐞 Bug ≡ Correctness
Description
saveGCREditChanges calls accounts.values().toArray(), but Map iterators don’t implement
toArray(), causing a runtime TypeError during batched persistence. This will break block
processing when attempting to save applied edits.
Code

src/libs/blockchain/gcr/handleGCR.ts[R601-607]

+        const entitiesToSave = accounts.values().toArray()
+        if (entitiesToSave.length > 0) {
+            log.debug(
+                `[applyGCREditsFromMergedMempool] Saving ${entitiesToSave.length} entities`,
+            )
+            const gcrMainRepo = dataSource.getRepository(GCRMain)
+            await gcrMainRepo.save(entitiesToSave)
Evidence
accounts is a Map, so accounts.values() returns an iterator; calling .toArray() on it will
fail at runtime, preventing the batched save from completing.

src/libs/blockchain/gcr/handleGCR.ts[597-608]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`saveGCREditChanges()` calls `accounts.values().toArray()`, but `Map.prototype.values()` returns an iterator without a `toArray()` method. This causes a runtime crash when saving batched updates.
### Issue Context
This is on the consensus/sync critical path because `applyTransactions()` always calls `saveGCREditChanges()`.
### Fix Focus Areas
- src/libs/blockchain/gcr/handleGCR.ts[597-608]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Uint8Array sender breaks SQL🐞 Bug ≡ Correctness
Description
applyTransactions uses tx.content.from_ed25519_address || tx.content.from as the key in
assignedTxsUpdates, but from_ed25519_address can be a Uint8Array. bulkUpdateAssignedTxs later calls
pubkey.replace(...) assuming a string, causing a runtime crash or malformed SQL.
Code

src/libs/blockchain/gcr/handleGCR.ts[R560-567]

+                const sender =
+                    tx.content?.from_ed25519_address || tx.content.from
+                if (sender && tx.hash) {
+                    if (!assignedTxsUpdates.has(sender)) {
+                        assignedTxsUpdates.set(sender, [])
+                    }
+
+                    assignedTxsUpdates.get(sender).push(tx.hash)
Evidence
The codebase explicitly models batchable accounts as string | Uint8Array (normalizePubkey) and
prepareAccounts normalizes from_ed25519_address, implying it may not be a string. However
applyTransactions stores the raw sender value into a Map and bulkUpdateAssignedTxs treats the key
as a string (replace).

src/libs/blockchain/gcr/handleGCR.ts[559-568]
src/libs/blockchain/gcr/handleGCR.ts[291-306]
src/libs/blockchain/gcr/handleGCR.ts[100-118]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`applyTransactions()` uses `tx.content.from_ed25519_address || tx.content.from` as the assignedTxsUpdates key. If `from_ed25519_address` is a `Uint8Array`, the key is not a string and `bulkUpdateAssignedTxs()` will crash when calling `.replace()` on it.
### Issue Context
A `normalizePubkey(account: string | Uint8Array)` helper already exists in `handleGCR.ts` and is used elsewhere for pubkey normalization.
### Fix Focus Areas
- src/libs/blockchain/gcr/handleGCR.ts[559-568]
- src/libs/blockchain/gcr/handleGCR.ts[291-310]
- src/libs/blockchain/gcr/handleGCR.ts[113-118]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
4. QueryRunner not connected 🐞 Bug ☼ Reliability
Description
bulkUpdateAssignedTxs creates a TypeORM QueryRunner and executes a query without calling
connect(). In this repo (typeorm ^0.3.17) other QueryRunner usage explicitly connects before use,
so this may throw at runtime and abort consensus after state saves.
Code

src/libs/blockchain/gcr/handleGCR.ts[R296-321]

+        const db = await Datasource.getInstance()
+        const queryRunner = db.getDataSource().createQueryRunner()
+
+        try {
+            // Build VALUES clause with proper escaping
+            // assignedTxs is jsonb, so we use jsonb arrays and || operator for concatenation
+            const valueEntries: string[] = []
+            for (const [pubkey, txHashes] of updates.entries()) {
+                const escapedPubkey = pubkey.replace(/'/g, "''")
+                // Create a JSON array string for jsonb
+                const jsonArray = JSON.stringify(txHashes).replace(/'/g, "''")
+                valueEntries.push(
+                    `('${escapedPubkey}'::text, '${jsonArray}'::jsonb)`,
+                )
+            }
+
+            const sql = `
+            UPDATE gcr_main AS g
+            SET "assignedTxs" = COALESCE(g."assignedTxs", '[]'::jsonb) || v.new_txs,
+                "updatedAt" = NOW()
+            FROM (VALUES ${valueEntries.join(",\n")}) AS v(pubkey, new_txs)
+            WHERE g.pubkey = v.pubkey
+        `
+
+            await queryRunner.query(sql)
+        } finally {
Evidence
bulkUpdateAssignedTxs calls createQueryRunner() then immediately uses queryRunner.query(sql)
with no connect(). Elsewhere in the repo, QueryRunner usage connects before starting work, and the
repo depends on typeorm ^0.3.17.

src/libs/blockchain/gcr/handleGCR.ts[296-323]
src/libs/blockchain/gcr/gcr_routines/routines/zkRoutines.ts[186-192]
package.json[145-156]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`bulkUpdateAssignedTxs()` creates a QueryRunner and runs `queryRunner.query(sql)` without an explicit `await queryRunner.connect()`. This can fail depending on TypeORM behavior/config and would throw in the consensus path.
### Issue Context
The repository uses `typeorm` ^0.3.17, and other code paths (e.g. zkRoutines) explicitly call `queryRunner.connect()` before executing transactional work.
### Fix Focus Areas
- src/libs/blockchain/gcr/handleGCR.ts[291-323]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@cwilvx cwilvx changed the title Omni throughput debug Improve Transaction Throughput Apr 1, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 18

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/libs/blockchain/mempool_v2.ts (1)

154-208: ⚠️ Potential issue | 🟠 Major

Only hide hashes after the transaction is actually stored.

Every incoming hash is inserted into noSendBackTxs before the save attempt. If this.repo.save(tx) fails for a non-duplicate reason, the catch only logs and the final response still omits that hash, so the peer gets success: true and never retries a transaction we did not persist.

Possible fix
         const noSendBackTxs = new Map<string, string>()
         for (const tx of incoming) {
             const isCoherent = TxUtils.isCoherent(tx)
             if (!isCoherent) {
                 log.error(
@@
                 return {
                     success: false,
                     mempool: [],
                 }
             }

-            noSendBackTxs.set(tx.hash, tx.hash)
         }

         const blockNumber = SecretaryManager.lastBlockRef
         const existingHashes = await this.getMempoolHashMap(blockNumber)
@@
         for (const tx of incoming) {
             if (existingHashes[tx.hash]) {
+                noSendBackTxs.set(tx.hash, tx.hash)
                 log.info(`tx already exists: ${tx.hash}`)
                 continue
             }

             try {
                 await this.repo.save(tx)
+                noSendBackTxs.set(tx.hash, tx.hash)
             } catch (error) {
                 if (
                     error instanceof QueryFailedError &&
                     error.message.includes(
                         "duplicate key value violates unique constraint",
@@
                 ) {
                     noSendBackTxs.set(tx.hash, tx.hash)
                     continue
                 }

-                // INFO: Log other errors
-                console.error(error)
+                throw error
             }
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/libs/blockchain/mempool_v2.ts` around lines 154 - 208, The code currently
adds every incoming tx.hash into noSendBackTxs before attempting to persist,
causing peers to think those txs were accepted even if repo.save(tx) fails;
change the flow in receive (the loop using TxUtils.isCoherent,
TxUtils.validateSignature and the later save via this.repo.save) so that
noSendBackTxs.set(tx.hash, tx.hash) is only executed after a successful save OR
when you detect a duplicate in the catch (the QueryFailedError branch); remove
the initial noSendBackTxs.set in the first validation loop and ensure
existingHashes (from getMempoolHashMap) still short-circuits duplicates before
save.
src/libs/blockchain/gcr/gcr_routines/routines/rewardsRoutines.ts (1)

11-27: ⚠️ Potential issue | 🔴 Critical

Mutate accountGCR, not a second lookup.

This routine still calls ensureGCRForUser(address) and updates account, but it returns entity: accountGCR. In the new batch flow the caller will persist accountGCR, so this points edit is effectively dropped.

Possible fix
 export async function applyAwardPoints(
     editOperation: any,
     accountGCR: GCRMain,
 ): Promise<GCRResult> {
-    const { account: address, amount, date } = editOperation
-    const account = await ensureGCRForUser(address)
+    const { amount, date } = editOperation

     const challengeEntry = {
         date,
         points: amount,
     }

-    if (!account.points.breakdown.weeklyChallenge) {
-        account.points.breakdown.weeklyChallenge = []
+    if (!accountGCR.points.breakdown.weeklyChallenge) {
+        accountGCR.points.breakdown.weeklyChallenge = []
     }

-    account.points.breakdown.weeklyChallenge.push(challengeEntry)
-    account.points.totalPoints = (account.points.totalPoints || 0) + amount
-    account.points.lastUpdated = new Date()
+    accountGCR.points.breakdown.weeklyChallenge.push(challengeEntry)
+    accountGCR.points.totalPoints =
+        (accountGCR.points.totalPoints || 0) + amount
+    accountGCR.points.lastUpdated = new Date()

     return { success: true, message: "Points awarded", entity: accountGCR }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/libs/blockchain/gcr/gcr_routines/routines/rewardsRoutines.ts` around
lines 11 - 27, The function is mutating the locally named variable account
(result of ensureGCRForUser(address)) but returns entity: accountGCR, so changes
are never persisted; update the routine to operate on the same object you
return—either rename account to accountGCR or assign ensureGCRForUser(address)
to accountGCR, then perform the breakdown/weeklyChallenge push, totalPoints and
lastUpdated updates on that accountGCR object (ensuring points.breakdown and
weeklyChallenge are initialized if missing) and return entity: accountGCR so the
caller's batch persistence picks up the edits.
src/libs/consensus/v2/PoRBFT.ts (1)

129-152: ⚠️ Potential issue | 🟠 Major

Reintroduce the phase-4 secretary barrier.

After batched GCR application and L2PSConsensus.applyPendingProofs(), this flow goes straight to forging. The only remaining phase-4 update in the file is inside the old commented helper below, so validators no longer synchronize after step 4.

🛠️ Possible fix
         if (l2psResult.proofsFailed > 0) {
             log.warning(
                 `[consensusRoutine] ${l2psResult.proofsFailed} L2PS proofs failed verification`,
             )
         }
+
+        await updateValidatorPhase(4, blockRef)
 
         // REVIEW Re-merge the mempools anyway to get the correct mempool from the whole shard
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/libs/consensus/v2/PoRBFT.ts` around lines 129 - 152, The flow skips the
phase-4 synchronization so validators can start forging prematurely; after
finishing GCR application (applyGCREditsFromMergedMempool) and
L2PSConsensus.applyPendingProofs(blockRef, false) you must reintroduce the
phase-4 secretary barrier so all validators wait for phase 4 to complete before
forging. Insert a call to the project’s phase-4 synchronization method (e.g.,
the secretary/PhaseSecretary barrier function used elsewhere in this file or
repo) immediately after L2PSConsensus.applyPendingProofs returns and before any
forging/next-phase logic in consensusRoutine, ensuring it awaits completion.
🧹 Nitpick comments (2)
src/libs/blockchain/gcr/gcr_routines/GCRBalanceRoutines.ts (1)

10-15: Collapse apply() onto applyToEntity().

The same balance rules now live in two places and they already diverge: apply() mutates editOperation.operation on rollback, while applyToEntity() doesn't. Reusing one implementation will keep the batched and non-batched paths consistent and lets you delete the commented repository code.

♻️ Possible consolidation
     static async apply(
         editOperation: GCREdit,
         accountGCR: GCRMain,
-        // gcrMainRepository: Repository<GCRMain>,
-        // simulate: boolean,
     ): Promise<GCRResult> {
-        if (editOperation.type !== "balance") {
-            return { success: false, message: "Invalid GCREdit type" }
-        }
-
-        const editOperationAccount =
-            typeof editOperation.account !== "string"
-                ? forgeToHex(editOperation.account)
-                : editOperation.account
-
-        // Safeguarding the operation by checking if the amount is positive
-        if (editOperation.amount <= 0) {
-            return { success: false, message: "Invalid amount" }
-        }
-
-        log.debug(
-            "Applying GCREdit balance: " +
-                editOperation.operation +
-                " " +
-                editOperation.amount +
-                " " +
-                editOperationAccount +
-                " " +
-                (editOperation.isRollback ? "ROLLBACK" : "NORMAL"),
-        )
-        // Reversing the operation if it is a rollback
-        if (editOperation.isRollback) {
-            editOperation.operation =
-                editOperation.operation === "add" ? "remove" : "add"
-        }
-
-        // Getting the actual balance to apply the operation
-        const actualBalance = accountGCR.balance
-
-        if (editOperation.operation === "add") {
-            accountGCR.balance =
-                BigInt(accountGCR.balance) + BigInt(editOperation.amount)
-        } else if (editOperation.operation === "remove") {
-            // Safeguarding the operation
-            // NOTE PROD flag is used to enable testing when not in production
-            if (
-                (actualBalance < editOperation.amount ||
-                    actualBalance === 0n) &&
-                getSharedState.PROD
-            ) {
-                return { success: false, message: "Insufficient balance" }
-            }
-            accountGCR.balance =
-                BigInt(accountGCR.balance) - BigInt(editOperation.amount)
-
-            // Safeguarding the operation by checking if the balance is negative
-            // NOTE This applies just to the non-production environment
-            if (accountGCR.balance < 0n && !getSharedState.PROD) {
-                accountGCR.balance = 0n
-            }
-        }
-
-        return { success: true, message: "Balance applied", entity: accountGCR }
+        const result = this.applyToEntity(editOperation, accountGCR)
+        return { ...result, entity: accountGCR }
     }

Also applies to: 93-145

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/libs/blockchain/gcr/gcr_routines/GCRBalanceRoutines.ts` around lines 10 -
15, The apply() implementation duplicates balance-rule logic from
applyToEntity() and they diverge (apply() mutates editOperation.operation on
rollback while applyToEntity() does not); refactor by collapsing one into the
other (e.g., have apply() delegate to applyToEntity() or vice versa) so there is
a single source of truth in GCRBalanceRoutines, remove the commented repository
parameters, and ensure rollback handling is unified — stop mutating
editOperation.operation in one path or move that mutation into the shared
implementation so both batched and non-batched flows behave identically.
src/libs/l2ps/L2PSConsensus.ts (1)

253-264: Remove the archived per-edit code path.

These commented blocks make the new batch flow much harder to audit, and Sonar is already flagging them. The old implementation is safer to keep in git history than inline in the file.

Also applies to: 274-297, 565-590

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/libs/l2ps/L2PSConsensus.ts` around lines 253 - 264, Remove the archived
per-edit commented code blocks (e.g., the commented-out rollbackEdits function
and the other commented sections referenced around the older per-edit flow) so
the file only contains the active batch flow; find occurrences of the commented
rollbackEdits code and any other commented per-edit logic (search for
"rollbackEdits", "rollbackEdit", and long commented blocks in L2PSConsensus) and
delete those commented lines entirely — leave no commented legacy per-edit
implementation in the source (it will remain in git history).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/libs/blockchain/gcr/gcr_routines/routines/ethosRoutines.ts`:
- Around line 18-25: The function applyEthosIdentityUpsert currently
destructures editOperation.data without guarding that data exists; change it to
first assign a local variable (e.g., const data = editOperation.data) and then
validate using optional chaining or an explicit check before destructuring (or
access properties via data?.chain, data?.subchain, data?.address) so the
function returns a structured GCRResult instead of throwing when
editOperation.data is undefined; mirror the safe pattern used in
applyEthosIdentityRemove to locate the correct spot and update checks
accordingly.

In `@src/libs/blockchain/gcr/gcr_routines/routines/utils.ts`:
- Line 4: The import statement 'import { dataSource } from
"@/model/datasource";' contains a trailing semicolon that violates the project's
`semi` ESLint rule; remove the stray semicolon so the line reads without the
final ';' (locate the import of dataSource in utils.ts) to satisfy formatting
rules and avoid the lint failure.

In `@src/libs/blockchain/gcr/handleGCR.ts`:
- Around line 368-377: prepareAccounts() currently creates unsaved GCRMain
objects for every missing pubkey (via HandleGCR.createAccount and
gcrMainCache.set) causing blank rows to be persisted later by
saveGCREditChanges() even for failed/no-op transactions; modify the flow to
track a dirty set (e.g., touchedPubkeys or dirtyPubkeys) and only add pubkeys to
gcrMainCache and persist them in saveGCREditChanges() when the transaction
successfully mutates state: 1) in places where you now call
HandleGCR.createAccount (including the loop over affectedPubkeys and the similar
block at lines ~597-607), create in-memory placeholders but do not set them into
gcrMainCache until the edit is confirmed successful; 2) when an operation
actually mutates an account, add its pubkey to the dirty set and ensure
saveGCREditChanges() iterates only over dirtyPubkeys (or checks a isDirty flag
on GCRMain) before writing to the DB; and 3) ensure rollback/error paths clear
any temporary placeholders so failed/no-op txs do not create rows.
- Around line 449-475: The code currently only handles applyGCREdit failures via
result.success, but applyGCREdit can throw and bypass snapshot restoration and
rollback; wrap the ApplyGCREdit call in a try/catch, and in the catch perform
the same snapshot restoration loop and non-batchable rollback as in the
result.success === false branch; additionally, maintain a separate list (e.g.,
appliedNonBatchableEdits) that you push to only when an edit is non-batchable
(use this list instead of appliedEdits when calling this.rollback) so you don't
double-rollback edits already restored from snapshots; ensure both the
false-result branch and the catch share the same unwind logic to avoid leaving
partial work.
- Around line 597-613: saveGCREditChanges currently saves the final batch state
then runs queued sideEffects (nomis/xm/tlsn) which call isFirstConnection(),
causing rewards to be computed against the post-batch DB state and running them
with Promise.all makes order nondeterministic and failure handling partial; fix
by moving incentive evaluation into applyTransaction() when each edit is applied
(or flush sideEffects immediately per transaction), i.e., evaluate and
enqueue/execute nomis/xm/tlsn callbacks inside applyTransaction for each
transaction (or run sideEffects sequentially per transaction instead of
collecting them for the whole batch), run them before persisting the next
transaction state (or at least before the final gcrMainRepo.save in
saveGCREditChanges), and replace Promise.all(sideEffects.map(...)) with
sequential execution that catches and surfaces individual errors so one
rejection doesn’t silently produce a partial commit.
- Around line 401-404: The spread [...tx.content.gcr_edits] runs before the
empty-edit check and will throw when tx.content.gcr_edits is undefined/null; to
fix, first check that tx.content && Array.isArray(tx.content.gcr_edits) (or use
a guarded expression like tx.content?.gcr_edits) and only spread when that check
passes, e.g. compute gcrEdits after the guard (or initialize gcrEdits = [] when
not an array) so the subsequent fast-path if (!gcrEdits ||
!Array.isArray(gcrEdits) || gcrEdits.length === 0) can run without an exception;
update the code in handleGCR around the gcrEdits declaration and any use of
tx.content.gcr_edits accordingly.
- Around line 560-567: The sender value used as the key in assignedTxsUpdates
must be normalized the same way prepareAccounts() does to avoid storing a
Uint8Array (which later causes pubkey.replace is not a function in
bulkUpdateAssignedTxs); update the block in handleGCR.ts where sender is derived
(around assignedTxsUpdates usage) to convert/normalize
tx.content?.from_ed25519_address or tx.content.from into the canonical string
form used by prepareAccounts() before using it as a Map key and before calling
assignedTxsUpdates.get/ set/ push so lookups and downstream updates use the same
string pubkey format.

In `@src/libs/blockchain/mempool_v2.ts`:
- Around line 147-152: The early return in Mempool.receive() (checking
getSharedState.inConsensusLoop in src/libs/blockchain/mempool_v2.ts) currently
returns { success: false, mempool: [] } which causes RPC callers to treat it as
an error; change this to return a successful no-op (e.g., { success: true,
mempool: [] }) when writes are intentionally disabled so callers (like the
endpoint handler that calls Mempool.receive()) treat it as a skipped/preserved
merge rather than a failure. Ensure the change is made in the function that
contains the getSharedState.inConsensusLoop check (Mempool.receive / the
early-exit block) and keep the returned mempool as an empty array to indicate
nothing was persisted.

In `@src/libs/blockchain/routines/Sync.ts`:
- Around line 725-726: The current syncGCRTables function swallows failed GCR
batch results from HandleGCR.applyTransactions; change it to propagate failures
by checking the result of HandleGCR.applyTransactions(txs, false) and throwing
an Error (including the returned failure details) when result.success === false
so callers like syncBlock and batchDownloadBlocks cannot silently continue;
update or leave callers to await and let the thrown error bubble up for proper
abort/handling.

In `@src/libs/consensus/v2/PoRBFT.ts`:
- Around line 397-407: The code filters mempool into pendingTxs but when
pendingTxs.length === 0 it returns empty failedTxs, dropping duplicates; update
the early return in the function (PoRBFT flow around the mempool filter using
variables mempool, existingTxHashes, failedTxs, pendingTxs) to return the
collected failedTxs instead of an empty array so callers can prune those
duplicate hashes from the mempool.
- Around line 367-368: The rollback helper rollbackGCREditsFromTxs currently
returns whatever HandleGCR.applyTransactions(txs, true) yields, but
applyTransactions can report partial failures; update rollbackGCREditsFromTxs to
check the result of HandleGCR.applyTransactions(txs, true) and if any
transaction failed to rollback (partial failure) throw an error or otherwise
fail hard so the invalid-block path cannot continue with a partially rolled-back
GCR state; reference HandleGCR.applyTransactions and rollbackGCREditsFromTxs
when implementing this guard.

In `@src/libs/l2ps/L2PSConsensus.ts`:
- Around line 334-339: The current flow commits GCR edits with
HandleGCR.saveGCREditChanges() before markProofApplied() (called later in
applyProof()), which can leave edits persisted while the proof status write
fails; make these two writes atomic by performing both the GCR persistence and
the proof-status update in a single transactional operation: either move the
call to HandleGCR.saveGCREditChanges(...) into applyProof() and wrap it together
with markProofApplied(...) in one DB transaction, or extend
HandleGCR.saveGCREditChanges to accept a callback/closure that executes
markProofApplied(proofId, status) inside the same transaction so both commits
succeed or both roll back. Ensure you reference and update the code paths around
saveGCREditChanges, markProofApplied, and applyProof to implement the
single-transaction behavior.
- Around line 602-611: The code calls HandleGCR.applyTransaction and then
unconditionally calls HandleGCR.saveGCREditChanges and resets proof state,
allowing a failed rollback to persist partial changes; modify the flow in the
block around HandleGCR.applyTransaction (the editResult handling for mockTx) to
check editResult.success before saving or changing proof state, and if success
is false, abort further persistence/setting of proof to pending (log or throw
and return early) so that saveGCREditChanges(editResult.accounts,
editResult.sideEffects) and any proof status update only run when
editResult.success is true.
- Around line 300-317: The code currently builds a synthetic transaction with an
empty account when no proof.gcr_edits passes isBatchableGCREdit; before calling
createMockTx/HandleGCR.prepareAccounts/HandleGCR.applyTransaction, validate that
you found a non-empty account (from the loop over proof.gcr_edits using
isBatchableGCREdit) and if none is found reject the proof by throwing an error
or returning a failed result; update the logic around createMockTx(proof,
account) to only proceed when account is set and add a clear error message
indicating the proof contains no account-backed edits so HandleGCR is not
invoked with a blank account.

In `@testing/devnet/run-devnet`:
- Line 150: The runner currently appends a bare flag
FINAL_COMMAND="${START_COMMAND} --no-tui" which causes digestArguments() to
treat it as an unexpected standalone command and exit; fix this by passing the
flag as a key=value pair (e.g., "--no-tui=true") or update digestArguments() to
accept standalone boolean flags (treat arguments starting with "--" and no "="
as boolean true) so that FINAL_COMMAND contains a key=value style argument or
digestArguments() recognizes bare flags; update the code that builds
FINAL_COMMAND or the parsing logic in digestArguments() and ensure tests cover
both "--no-tui" and "--no-tui=true" cases.

In `@testing/loadgen/src/features/l2ps/shared.ts`:
- Around line 60-61: The readiness timeout defaults for RPC and TX are set too
low—change the envInt calls that define waitForRpcSec and waitForTxSec to use
more conservative defaults (e.g., 30 or 60 seconds) so cold starts/CI don’t
flake; update the envInt invocations for waitForRpcSec and waitForTxSec to
higher default values and ensure any callers that assume implicit short waits
still respect these variables rather than hardcoding shorter values (search for
uses of waitForRpcSec and waitForTxSec and make sure they’re passed through to
the readiness/polling logic).

In `@testing/loadgen/src/token_shared.ts`:
- Line 219: The RPC readiness wait default was lowered to 3s causing cold-start
flakes; update the wait invocation at the call to waitForRpcReady to use the
same 120s default used elsewhere by changing the envInt default from 3 to 120
(i.e., envInt("WAIT_FOR_RPC_SEC", 120)) so shared wallet bootstrap uses a
longer, consistent timeout.

In `@testing/scripts/testenv-doctor.ts`:
- Around line 56-67: The unconditional console.log(res) breaks --json output;
update the probe so debug output is only emitted when verbose is enabled and
suppressed in JSON mode: replace the direct console.log(res) with a conditional
check that uses the runtime flags (e.g. --verbose and --json) so that res is
logged only when verbose is true and --json is not set (i.e., if (verbose &&
!jsonMode) console.log(res)); also ensure any other probe debug prints follow
the same gating (e.g., avoid printing in the catch path when JSON mode is
requested).

---

Outside diff comments:
In `@src/libs/blockchain/gcr/gcr_routines/routines/rewardsRoutines.ts`:
- Around line 11-27: The function is mutating the locally named variable account
(result of ensureGCRForUser(address)) but returns entity: accountGCR, so changes
are never persisted; update the routine to operate on the same object you
return—either rename account to accountGCR or assign ensureGCRForUser(address)
to accountGCR, then perform the breakdown/weeklyChallenge push, totalPoints and
lastUpdated updates on that accountGCR object (ensuring points.breakdown and
weeklyChallenge are initialized if missing) and return entity: accountGCR so the
caller's batch persistence picks up the edits.

In `@src/libs/blockchain/mempool_v2.ts`:
- Around line 154-208: The code currently adds every incoming tx.hash into
noSendBackTxs before attempting to persist, causing peers to think those txs
were accepted even if repo.save(tx) fails; change the flow in receive (the loop
using TxUtils.isCoherent, TxUtils.validateSignature and the later save via
this.repo.save) so that noSendBackTxs.set(tx.hash, tx.hash) is only executed
after a successful save OR when you detect a duplicate in the catch (the
QueryFailedError branch); remove the initial noSendBackTxs.set in the first
validation loop and ensure existingHashes (from getMempoolHashMap) still
short-circuits duplicates before save.

In `@src/libs/consensus/v2/PoRBFT.ts`:
- Around line 129-152: The flow skips the phase-4 synchronization so validators
can start forging prematurely; after finishing GCR application
(applyGCREditsFromMergedMempool) and L2PSConsensus.applyPendingProofs(blockRef,
false) you must reintroduce the phase-4 secretary barrier so all validators wait
for phase 4 to complete before forging. Insert a call to the project’s phase-4
synchronization method (e.g., the secretary/PhaseSecretary barrier function used
elsewhere in this file or repo) immediately after
L2PSConsensus.applyPendingProofs returns and before any forging/next-phase logic
in consensusRoutine, ensuring it awaits completion.

---

Nitpick comments:
In `@src/libs/blockchain/gcr/gcr_routines/GCRBalanceRoutines.ts`:
- Around line 10-15: The apply() implementation duplicates balance-rule logic
from applyToEntity() and they diverge (apply() mutates editOperation.operation
on rollback while applyToEntity() does not); refactor by collapsing one into the
other (e.g., have apply() delegate to applyToEntity() or vice versa) so there is
a single source of truth in GCRBalanceRoutines, remove the commented repository
parameters, and ensure rollback handling is unified — stop mutating
editOperation.operation in one path or move that mutation into the shared
implementation so both batched and non-batched flows behave identically.

In `@src/libs/l2ps/L2PSConsensus.ts`:
- Around line 253-264: Remove the archived per-edit commented code blocks (e.g.,
the commented-out rollbackEdits function and the other commented sections
referenced around the older per-edit flow) so the file only contains the active
batch flow; find occurrences of the commented rollbackEdits code and any other
commented per-edit logic (search for "rollbackEdits", "rollbackEdit", and long
commented blocks in L2PSConsensus) and delete those commented lines entirely —
leave no commented legacy per-edit implementation in the source (it will remain
in git history).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 64d09fd9-c3b2-4904-aa3b-ee133c25f04a

📥 Commits

Reviewing files that changed from the base of the PR and between b57292a and 628937b.

📒 Files selected for processing (36)
  • src/libs/blockchain/gcr/gcr.ts
  • src/libs/blockchain/gcr/gcr_routines/GCRBalanceRoutines.ts
  • src/libs/blockchain/gcr/gcr_routines/GCRIdentityRoutines.ts
  • src/libs/blockchain/gcr/gcr_routines/GCRNonceRoutines.ts
  • src/libs/blockchain/gcr/gcr_routines/routines/ethosRoutines.ts
  • src/libs/blockchain/gcr/gcr_routines/routines/humanpassportRoutines.ts
  • src/libs/blockchain/gcr/gcr_routines/routines/nomisRoutines.ts
  • src/libs/blockchain/gcr/gcr_routines/routines/pqcRoutines.ts
  • src/libs/blockchain/gcr/gcr_routines/routines/rewardsRoutines.ts
  • src/libs/blockchain/gcr/gcr_routines/routines/tlsnRoutines.ts
  • src/libs/blockchain/gcr/gcr_routines/routines/udRoutines.ts
  • src/libs/blockchain/gcr/gcr_routines/routines/utils.ts
  • src/libs/blockchain/gcr/gcr_routines/routines/web2Routines.ts
  • src/libs/blockchain/gcr/gcr_routines/routines/xmRoutines.ts
  • src/libs/blockchain/gcr/gcr_routines/routines/zkRoutines.ts
  • src/libs/blockchain/gcr/handleGCR.ts
  • src/libs/blockchain/mempool_v2.ts
  • src/libs/blockchain/routines/Sync.ts
  • src/libs/consensus/v2/PoRBFT.ts
  • src/libs/consensus/v2/routines/mergeMempools.ts
  • src/libs/l2ps/L2PSConsensus.ts
  • src/libs/network/endpointExecution.ts
  • src/libs/peer/routines/peerBootstrap.ts
  • testing/devnet/.env.example
  • testing/devnet/docker-compose.yml
  • testing/devnet/run-devnet
  • testing/devnet/scripts/generate-peerlist.sh
  • testing/devnet/scripts/setup.sh
  • testing/docker-compose.perf.yml
  • testing/loadgen/src/features/l2ps/shared.ts
  • testing/loadgen/src/features/omni/shared.ts
  • testing/loadgen/src/features/tlsnotary/shared.ts
  • testing/loadgen/src/token_shared.ts
  • testing/scripts/run-chaos-token-script-transfer.sh
  • testing/scripts/run-suite.ts
  • testing/scripts/testenv-doctor.ts
💤 Files with no reviewable changes (3)
  • src/libs/peer/routines/peerBootstrap.ts
  • src/libs/consensus/v2/routines/mergeMempools.ts
  • src/libs/blockchain/gcr/gcr_routines/routines/zkRoutines.ts

Comment on lines +602 to +611
const editResult = await HandleGCR.applyTransaction(
accounts,
mockTx,
true,
false,
)
await HandleGCR.saveGCREditChanges(
editResult.accounts,
editResult.sideEffects,
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Don't continue after a failed rollback batch.

This branch persists editResult.accounts and resets the proof to pending without checking editResult.success. A partial rollback would leave L1 state half-reverted while making the proof eligible for replay.

🛠️ Suggested guard
                 const editResult = await HandleGCR.applyTransaction(
                     accounts,
                     mockTx,
                     true,
                     false,
                 )
+
+                if (!editResult.success) {
+                    throw new Error(
+                        `Failed to rollback proof ${proof.id}: ${editResult.message}`,
+                    )
+                }
+
                 await HandleGCR.saveGCREditChanges(
                     editResult.accounts,
                     editResult.sideEffects,
                 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/libs/l2ps/L2PSConsensus.ts` around lines 602 - 611, The code calls
HandleGCR.applyTransaction and then unconditionally calls
HandleGCR.saveGCREditChanges and resets proof state, allowing a failed rollback
to persist partial changes; modify the flow in the block around
HandleGCR.applyTransaction (the editResult handling for mockTx) to check
editResult.success before saving or changing proof state, and if success is
false, abort further persistence/setting of proof to pending (log or throw and
return early) so that saveGCREditChanges(editResult.accounts,
editResult.sideEffects) and any proof status update only run when
editResult.success is true.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Apr 1, 2026

Quality Gate Passed Quality Gate passed

Issues
31 New issues
0 Accepted issues

Measures
0 Security Hotspots
No data about Coverage
0.0% Duplication on New Code

See analysis details on SonarQube Cloud

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (3)
src/libs/blockchain/gcr/handleGCR.ts (3)

368-377: ⚠️ Potential issue | 🟠 Major

Only persist mutated accounts, not the full preloaded cache.

prepareAccounts() eagerly creates placeholder GCRMain entities, and saveGCREditChanges() persists the entire map. Failed/no-op transactions can still produce blank rows.

Proposed direction
- static async saveGCREditChanges(
-     accounts: Map<string, GCRMain>,
-     sideEffects: (() => Promise<void>)[],
- ) {
-     const entitiesToSave = accounts.values().toArray()
+ static async saveGCREditChanges(
+     accounts: Map<string, GCRMain>,
+     dirtyPubkeys: Set<string>,
+     sideEffects: (() => Promise<void>)[],
+ ) {
+     const entitiesToSave = Array.from(dirtyPubkeys)
+         .map(pubkey => accounts.get(pubkey))
+         .filter((e): e is GCRMain => !!e)
// In applyTransaction/applyTransactions:
+ const dirtyPubkeys = new Set<string>()
+ // add pubkey only when an edit successfully mutates that account
+ dirtyPubkeys.add(pubkey)

Also applies to: 609-620

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/libs/blockchain/gcr/handleGCR.ts` around lines 368 - 377,
prepareAccounts() currently eagerly creates placeholder GCRMain entities via
HandleGCR.createAccount and gcrMainCache, and saveGCREditChanges() persists the
entire gcrMainCache which causes blank rows for failed/no-op txns; change the
flow to only persist accounts that were actually mutated: add and maintain a
mutatedPubkeys Set (or flag on GCRMain) whenever an account is created or
modified (e.g., in prepareAccounts(), transaction handlers, and wherever
HandleGCR.createAccount is called), and update saveGCREditChanges() to iterate
only over mutatedPubkeys to persist those entities (do not save the whole
gcrMainCache); ensure placeholders remain in-memory unless marked mutated so
they aren’t written to DB.

470-489: ⚠️ Potential issue | 🔴 Critical

Rollback can double-apply reversals for batchable edits after snapshot restore.

When a non-batchable edit fails, snapshots already restore batchable state, but rollback(tx, accounts, appliedEdits) still replays all prior edits, including batchables.

Proposed fix
- const appliedEdits: GCREdit[] = []
+ const appliedEdits: GCREdit[] = []
+ const appliedNonBatchableEdits: GCREdit[] = []

...
  appliedEdits.push(edit)
+ if (!this.GCRTxTypes.has(edit.type)) {
+     appliedNonBatchableEdits.push(edit)
+ }

...
- if (!simulate && !this.GCRTxTypes.has(edit.type)) {
-     await this.rollback(tx, accounts, appliedEdits)
+ if (!simulate && !this.GCRTxTypes.has(edit.type)) {
+     await this.rollback(tx, accounts, appliedNonBatchableEdits)
  }

Also applies to: 504-505, 891-927

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/libs/blockchain/gcr/handleGCR.ts` around lines 470 - 489, The rollback
currently replays all prior edits after restoring snapshots, causing batchable
edits to be applied twice; update the logic around rollback(tx, accounts,
appliedEdits) so it does not re-apply edits whose types are in this.GCRTxTypes
(i.e., filter appliedEdits to exclude edits where
this.GCRTxTypes.has(edit.type)) or add a flag to rollback to skip batchable
edits; apply the same fix to the other identical blocks that use
snapshots/accounts and call rollback (the blocks guarded by simulate and
GCRTxTypes checks).

571-573: ⚠️ Potential issue | 🟠 Major

Guard sender before normalization.

normalizePubkey(...) runs before null checks. If both from_ed25519_address and from are missing, this can throw before the if (sender && tx.hash) guard.

Proposed fix
- const sender = normalizePubkey(
-     tx.content?.from_ed25519_address || tx.content.from,
- )
- if (sender && tx.hash) {
+ const senderRaw =
+     tx.content?.from_ed25519_address ?? tx.content?.from
+ if (senderRaw && tx.hash) {
+     const sender = normalizePubkey(senderRaw)
      if (!assignedTxsUpdates.has(sender)) {
          assignedTxsUpdates.set(sender, [])
      }
      assignedTxsUpdates.get(sender).push(tx.hash)
  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/libs/blockchain/gcr/handleGCR.ts` around lines 571 - 573, The code calls
normalizePubkey(...) immediately on tx.content?.from_ed25519_address ||
tx.content.from which can throw if both are missing; instead, first read the raw
sender value (e.g., rawSender = tx.content?.from_ed25519_address ||
tx.content?.from), check that rawSender exists (and tx.hash if needed), then
call normalizePubkey(rawSender) and assign to sender. Update any existing guards
around sender and tx.hash to use the normalized value only after this existence
check; refer to normalizePubkey and the sender variable in handleGCR to locate
the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/libs/blockchain/gcr/handleGCR.ts`:
- Around line 668-677: The switch handling editOperation.type calls
GCRBalanceRoutines.apply, GCRNonceRoutines.apply, and GCRIdentityRoutines.apply
with a nullable account; add an explicit guard before dispatch (e.g., in the
switch or immediately above it) that checks if account is null/undefined and
handles it deterministically (throw a clear error or return a rejected result)
so the routines never receive a null account; reference the switch on
editOperation.type and the three routines GCRBalanceRoutines.apply,
GCRNonceRoutines.apply, GCRIdentityRoutines.apply when locating where to add the
null-check and error handling.
- Line 613: The code uses Iterator.toArray() which is unsupported on Node.js
<24; update the assignment to build the array compatibly by replacing the use of
accounts.values().toArray() when setting const entitiesToSave so it uses
Array.from(accounts.values()) (or an equivalent Array.from(...) conversion) to
ensure compatibility across Node.js versions while keeping the rest of the logic
in handleGCR.ts unchanged.

---

Duplicate comments:
In `@src/libs/blockchain/gcr/handleGCR.ts`:
- Around line 368-377: prepareAccounts() currently eagerly creates placeholder
GCRMain entities via HandleGCR.createAccount and gcrMainCache, and
saveGCREditChanges() persists the entire gcrMainCache which causes blank rows
for failed/no-op txns; change the flow to only persist accounts that were
actually mutated: add and maintain a mutatedPubkeys Set (or flag on GCRMain)
whenever an account is created or modified (e.g., in prepareAccounts(),
transaction handlers, and wherever HandleGCR.createAccount is called), and
update saveGCREditChanges() to iterate only over mutatedPubkeys to persist those
entities (do not save the whole gcrMainCache); ensure placeholders remain
in-memory unless marked mutated so they aren’t written to DB.
- Around line 470-489: The rollback currently replays all prior edits after
restoring snapshots, causing batchable edits to be applied twice; update the
logic around rollback(tx, accounts, appliedEdits) so it does not re-apply edits
whose types are in this.GCRTxTypes (i.e., filter appliedEdits to exclude edits
where this.GCRTxTypes.has(edit.type)) or add a flag to rollback to skip
batchable edits; apply the same fix to the other identical blocks that use
snapshots/accounts and call rollback (the blocks guarded by simulate and
GCRTxTypes checks).
- Around line 571-573: The code calls normalizePubkey(...) immediately on
tx.content?.from_ed25519_address || tx.content.from which can throw if both are
missing; instead, first read the raw sender value (e.g., rawSender =
tx.content?.from_ed25519_address || tx.content?.from), check that rawSender
exists (and tx.hash if needed), then call normalizePubkey(rawSender) and assign
to sender. Update any existing guards around sender and tx.hash to use the
normalized value only after this existence check; refer to normalizePubkey and
the sender variable in handleGCR to locate the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9a02f0bc-c388-4041-8a46-ce24fb20180b

📥 Commits

Reviewing files that changed from the base of the PR and between 628937b and 5dd0a9b.

📒 Files selected for processing (3)
  • src/libs/blockchain/gcr/gcr_routines/routines/utils.ts
  • src/libs/blockchain/gcr/handleGCR.ts
  • src/libs/consensus/v2/PoRBFT.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/libs/blockchain/gcr/gcr_routines/routines/utils.ts
  • src/libs/consensus/v2/PoRBFT.ts

accounts: Map<string, GCRMain>,
sideEffects: (() => Promise<void>)[],
) {
const entitiesToSave = accounts.values().toArray()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Checking runtime/compiler config for iterator-helper compatibility..."
fd -i 'package.json|tsconfig*.json' | while read -r f; do
  echo ">>> $f"
  rg -n '"engines"|"node"|target|lib' "$f" || true
done

echo
echo "Finding iterator toArray usage in TS sources..."
rg -n --type ts '\.toArray\('

Repository: kynesyslabs/node

Length of output: 882


🏁 Script executed:

head -50 package.json

Repository: kynesyslabs/node

Length of output: 3592


🏁 Script executed:

rg -A 5 '"engines"' package.json

Repository: kynesyslabs/node

Length of output: 42


🏁 Script executed:

# Check what accounts is and verify it's actually a Map or similar iterable
rg -B 10 -A 2 'const entitiesToSave = accounts\.values\(\)\.toArray' src/libs/blockchain/gcr/handleGCR.ts

Repository: kynesyslabs/node

Length of output: 504


🌐 Web query:

Iterator.toArray() JavaScript support Node.js version requirements

💡 Result:

Iterator.prototype.toArray is part of the ECMAScript 2025 Iterator Helpers proposal (Stage 4), which adds methods like map, filter, take, and toArray to Iterator.prototype for lazy iterator transformations. It converts an iterator's yielded values into an Array, equivalent to Array.from(iterator) or [...iterator] but chainable with other helpers. As of 2026-04-01 (today's date), Node.js fully supports Iterator.prototype.toArray starting from version 24.0.0 (released May 2025), which includes V8 13.6. This V8 version implements iterator helpers, including toArray. Node.js 24 is LTS ("Krypton"), with ongoing support. Earlier versions like 22.x (V8 12.4) do not support it natively—polyfills like core-js are needed. Browser support (for reference): Chrome 122+, Firefox 131+, Safari 18.4+ (per caniuse/MDN, baseline March 2025). Example: function* fib { let a = 1, b = 1; while (true) { yield a; [a, b] = [b, a + b]; } } const evens = fib.take(10).filter(x => x % 2 === 0).toArray; // [2, 8, 34, ...]

Citations:


Iterator.toArray() is unsupported on Node.js < 24.0.0.

Line 613 uses accounts.values().toArray(), which requires Node.js 24.0.0+ (ES2025 Iterator Helpers). Since this project does not specify a minimum Node.js version in engines, it implicitly supports earlier versions where this method is unavailable, causing runtime crashes. Replace with Array.from() for compatibility across all Node.js versions.

Safe fix
- const entitiesToSave = accounts.values().toArray()
+ const entitiesToSave = Array.from(accounts.values())
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const entitiesToSave = accounts.values().toArray()
const entitiesToSave = Array.from(accounts.values())
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/libs/blockchain/gcr/handleGCR.ts` at line 613, The code uses
Iterator.toArray() which is unsupported on Node.js <24; update the assignment to
build the array compatibly by replacing the use of accounts.values().toArray()
when setting const entitiesToSave so it uses Array.from(accounts.values()) (or
an equivalent Array.from(...) conversion) to ensure compatibility across Node.js
versions while keeping the rest of the logic in handleGCR.ts unchanged.

Comment on lines 668 to 677
switch (editOperation.type) {
case "balance":
result = await GCRBalanceRoutines.apply(
editOperation,
repositories.main as Repository<GCRMain>,
simulate,
)
result = await GCRBalanceRoutines.apply(editOperation, account)
break
case "nonce":
result = await GCRNonceRoutines.apply(
editOperation,
repositories.main as Repository<GCRMain>,
simulate,
)
result = await GCRNonceRoutines.apply(editOperation, account)
break
case "identity":
result = await GCRIdentityRoutines.apply(
editOperation,
repositories.main as Repository<GCRMain>,
simulate,
)
result = await GCRIdentityRoutines.apply(editOperation, account)
break
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add an explicit account null-check before routine dispatch.

balance/nonce/identity branches pass account directly, but account is nullable. A missing map entry can become a runtime failure inside routine implementations.

Proposed guard
switch (editOperation.type) {
  case "balance":
+     if (!account) {
+         return { success: false, message: "Missing account for balance edit" }
+     }
      result = await GCRBalanceRoutines.apply(editOperation, account)
      break
  case "nonce":
+     if (!account) {
+         return { success: false, message: "Missing account for nonce edit" }
+     }
      result = await GCRNonceRoutines.apply(editOperation, account)
      break
  case "identity":
+     if (!account) {
+         return { success: false, message: "Missing account for identity edit" }
+     }
      result = await GCRIdentityRoutines.apply(editOperation, account)
      break
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/libs/blockchain/gcr/handleGCR.ts` around lines 668 - 677, The switch
handling editOperation.type calls GCRBalanceRoutines.apply,
GCRNonceRoutines.apply, and GCRIdentityRoutines.apply with a nullable account;
add an explicit guard before dispatch (e.g., in the switch or immediately above
it) that checks if account is null/undefined and handles it deterministically
(throw a clear error or return a rejected result) so the routines never receive
a null account; reference the switch on editOperation.type and the three
routines GCRBalanceRoutines.apply, GCRNonceRoutines.apply,
GCRIdentityRoutines.apply when locating where to add the null-check and error
handling.

@cwilvx cwilvx merged commit e82ce9b into stabilisation Apr 1, 2026
8 checks passed
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.

1 participant