Skip to content

DApp/shader operations deferred on every new block while wallet re-syncs (stutters with fast blocks) #2059

Description

@Maxnflaxl

Summary

DApp (shader app) operations — both read-only calls and transaction building — are gated on the wallet being fully synced. After every new block (Wallet::OnNewTip), the wallet posts sync requests, so SyncRemains() > 0 until the node answers them. Any shader operation started during that window is deferred into m_SyncActionsQueue and only runs once sync settles.

With fast block times (e.g. ~3s), this opens a "not synced" window on every block, so dapp operations — including pure reads — stutter roughly once per block.

Where it happens

All paths below are in wallet/core/:

  • contracts/shaders_manager.cpp: CallShader (read-only, ~L265) and CallShaderAndStartTx (tx, ~L246) both funnel through pushRequestnextRequest(), which wraps the shader start in m_Wallet.DoInSyncedWallet(...) (~L328).
  • wallet.cpp:
    • DoInSyncedWallet (~L522) runs the action immediately iff !SyncRemains() && IsNodeInSync(), otherwise pushes it onto m_SyncActionsQueue:
      bool bSynced = !SyncRemains() && IsNodeInSync();
      if (bSynced) { AsyncContextHolder holder(*this); action(); }
      else { m_SyncActionsQueue.push(std::move(action)); }
    • OnNewTip (~L2233) fires on every block and posts RequestBodies() / RequestEvents() / RequestStateSummary() before CheckSyncDone().
    • SyncRemains (~L2332) counts pending Utxo/Kernel/Events/StateSummary/BodyPack/Body requests (REQUEST_TYPES_Sync in wallet.h).
    • The deferred queue is flushed only in SaveKnownState (~L2371), reached via CheckSyncDone once SyncRemains() == 0 and the tip timestamp passes IsValidTimeStamp — so a stale tip blocks the flush as well.

Impact

  • Recurring deferral synchronized to block arrival — ~20×/min at 3s blocks.
  • Worst on the common integrated-node + owner-key setup, where all three request types (RequestBodies requires the integrated/mobile node; RequestEvents requires an owned node online) fire on every tip.
  • Read-only shader calls are gated identically to transactions, even though a slightly-stale read is usually harmless. This is the likely source of the perceived "paused every block" behavior.
  • Per-block window length ≈ node round-trip to satisfy the sync requests: short on a local node, but can stall noticeably under network latency or when the node streams blocks in a burst (catch-up).

Suggested direction

  • Let read-only CallShader bypass DoInSyncedWallet (or use a relaxed sync check), keeping the strict gate only for CallShaderAndStartTx, where building against stale state risks an invalid kernel.
  • Add enqueue/flush timing logs around m_SyncActionsQueue to quantify the per-block window before deciding on the fix.

Metadata

Metadata

Assignees

No one assigned

    Labels

    apiIssues related to Wallet APIdiscussneeds discussion!uxux/ui issues or inconsistencies

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions