Skip to content

wallet-api: invoke_contract fails instead of hanging when the node can't be reached (#1695) - #2142

Open
profinch wants to merge 1 commit into
BeamMW:masterfrom
profinch:fix/1695-invoke-contract-no-node
Open

profinch wants to merge 1 commit into
BeamMW:masterfrom
profinch:fix/1695-invoke-contract-no-node

Conversation

@profinch

@profinch profinch commented Oct 6, 2026

Copy link
Copy Markdown

Fixes #1695.

The bug

ShadersManager starts a shader only once the wallet is in sync with a node (DoInSyncedWallet). With no node, that never happens. The call sits there forever, and so does every shader call queued behind it. A running shader that waits for a node response (vars, logs, ...) is stuck the same way after the node goes away.

Reproduced on master with wallet-api and bvm/Shaders/vault/app.wasm, read-only calls (create_tx: false):

action=view action=create_contract
node reachable answer in 0 s answer in 0 s
node unreachable no answer (curl timeout) no answer, queued behind view

The fix

The calls fail exactly when there's nothing left to wait for, with no arbitrary timeout:

  • IShadersManager::AbortCallsWaitingForNode(error) fails the current call, if it waits for the wallet sync or for a node response, and all the calls queued behind it. A shader waiting for a peer message (Comm_Wait) is left alone, since it has its own timeout. Cancelling uses what's already there: the start event that DoInSyncedWallet holds only weakly, and ManagerStd::Reset(), which drops the pending requests of a running shader.
  • WalletApiServer subscribes to NodeNetwork and calls it when a connection attempt fails while no node is connected. A live connection that drops is reported before it's uncounted, so a short drop doesn't fail anything. Only a failed reconnect does.
  • The desktop wallet uses the same ShadersManager, but its behaviour is unchanged: it never calls the new method.

The client now gets an error:

{"error":{"code":-32019,"data":"No connection to the node: connection refused","message":"Contract call failed"},"id":2,"jsonrpc":"2.0"}

Tests

  • New wallet/unittests/shaders_manager_test (wallet_core only, no network). The wallet has a node endpoint that is never connected. A call waiting for the sync and a call queued behind it both get the error, the manager is usable afterwards, and aborting with nothing in progress is a no-op. Making the method a no-op makes the test fail.
  • Manually, wallet-api against a mainnet node through a local TCP proxy:
    1. node up: answer in 0.2 s
    2. proxy killed: error in 4.5 s, at the next failed reconnect attempt
    3. proxy back: calls work again
    4. proxy back while a call waits: the call completes, nothing aborted
  • A never-synced wallet with no node: both calls get the error at the first failed connection attempt.
  • wallet_test, wallet_api_test, wallet_db_test, slatepack_test pass.

…n the node can't be reached

A shader call waits for the wallet to sync with a node, so with no node it
never started and blocked every call queued behind it. When a connection
attempt fails and no node is connected, the calls that can't progress
without the node now fail with "No connection to the node: <reason>".
@profinch

profinch commented Oct 6, 2026

Copy link
Copy Markdown
Author

Note: #2139 fixes the same issue and was opened a few minutes earlier. The two take different approaches, so here's the difference to help choose:

#2139 this PR
Trigger checks the connection count when invoke_contract is called aborts waiting calls when a connection attempt fails and no node is connected
No node at all error immediately error at the first failed attempt (within the reconnect timeout, ~5 s)
Call made right after start, before the connection is established error, although the node is fine waits, then runs normally
Node goes away while a running shader waits for its response (vars, logs) still hangs error
Node comes back while a call waits n/a the call completes

Either fixes the reported case. This one also covers a node lost mid-call and avoids false errors during startup.

@vsnation

Copy link
Copy Markdown

Tested this against master and against my #2139, which fixes the same issue differently. Setup: wallet-api --use_http=1, bvm/Shaders/vault/app.wasm role=manager,action=view, create_tx:false, macOS arm64.

no node (-n 127.0.0.1:1) master #2139 this PR
call 1 no answer (10 s timeout) -32019 No connection to the node, 0 s -32019 No connection to the node: connection refused, ~5 s
call 2 no answer same, 0 s same, ~5 s

shaders_manager_test passes. Both fix the hang. This one is the better design: #2139 rejects whenever getConnections()==0, so it would also fail calls during a short reconnect or right at startup. This PR only fails calls once a reconnect has actually failed, and it also releases calls that were already queued or waiting for a node response. I'm closing #2139 in favour of this one. LGTM.

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.

invoke_contract & no nnet

2 participants