monero.loveA little love for the network.
← All news

Monero / Privacy / Wallets

Monero Wallet Privacy: How a Remote Node Could Identify Your Pending Transaction

Monero PR #11744 targets a wallet request pattern that can expose a pending transaction to a remote node. Review the code, tests and proposed fix.

A wallet sends transaction requests to a remote server while one orange transaction card sits outside the request path.

A Monero wallet can reveal something through the transactions it does not request. When checking unconfirmed payments through a remote node, the wallet's fallback code can omit its own outgoing transaction from a request for transaction data. A node observing that difference could associate the transaction with the connected wallet.

Monero PR #11744 proposes changing that request pattern. The reported behavior concerns communication between a wallet and its daemon, the node software supplying blockchain data. The exposed information is a wallet-to-transaction association. Private keys remain local, and the report identifies no break in Monero's transaction cryptography.

Status at review, November 14, 2026, 04:00 UTC: PR #11744 is open and unmerged. The latest commit is a1d87e9. The author reports a three-wallet regtest reproduction. GitHub reports 18 successful checks, including Ubuntu 24.04 tests, but no submitted reviews or independent reproduction comments. The latest official CLI release is v0.18.5.4. Its source contains the relevant request filter. This review found no released fix or evidence of real-world exploitation.

A missing transaction in the request

The transaction pool holds transactions waiting for confirmation in a block. A wallet checks that pool to display incoming payments and track payments it has sent. Each transaction has an identifying hash. Requesting its body means asking the daemon for the transaction data associated with that hash.

Consider a pool containing transactions A and B. Alice's wallet sent A, Bob's wallet sent B, and Carol's wallet sent neither. For this example, the wallets have no earlier scans of A or B, and both payments go to other wallets.

In the reported fallback behavior, Alice asks for B, Bob asks for A, and Carol asks for both. The daemon knows which hashes it announced and which bodies each connection requested. The missing hash can reveal a relationship between that connection and a pending outgoing transaction.

This example describes a first fetch with a known starting state. The wallet also skips transactions because of earlier scans and payment state. Those details matter when interpreting requests outside a controlled test.

Normal pool data flows from daemon to wallet. In the fallback example, the daemon announces A and B but the wallet requests only B, allowing a possible inference about A.

The fallback exposes a selection decision that depends on the wallet's local state.

From getblocks.bin to gettransactions

The reviewed wallet source supports pool information alongside block synchronization through /getblocks.bin. When the response includes pool information, the wallet handles supplied transactions through update_pool_state_from_pool_data.

When pool_info_extent is NONE, the wallet can instead call update_pool_state_by_pool_query. The remote daemon controls that response field. A dishonest daemon can withhold pool information to select the fallback rather than waiting for an accidental compatibility problem.

The fallback first obtains pool hashes through /get_transaction_pool_hashes.bin. It then runs those hashes through accept_pool_tx_for_processing before requesting bodies. That helper consults m_unconfirmed_txs, the wallet's record of outgoing unconfirmed transactions, and ordinarily rejects transactions it recognizes as sent by this wallet.

The wallet passes the filtered hashes to NodeRPCProxy::get_transactions, which issues /gettransactions requests with pruning enabled. The privacy concern is the selected hash list, not the pruning setting.

The helper has exceptions. It can process a payment to another account within the same wallet so the receiving account displays it. Already scanned hashes and known incoming pool payments also affect filtering. Describing the request as always containing every transaction except the sender's would miss these conditions.

The reported three-wallet test

PR author kah-ja reports testing master at ef78ff3d7 with three monero-wallet-rpc instances on regtest, a local test network. A proxy changed pool_info_extent to zero to force the fallback. The author attributes the finding to a turingpoint penetration test.

According to the report, each sending wallet omitted its own transaction, while the third wallet requested both. With the proposed change, all three requested the same transactions. A second refresh requested no further bodies, as did a refresh after switching from the newer pool-data path to the fallback within the same session.

The author also reports matching balances and get_transfers results before and after confirmation, including a payment to another account of the same wallet. These checks address whether the request change preserves the tested wallet behavior.

The description and latest commit differ on testing. The PR description says the author built only the daemon and wallet RPC server, did not run the unit or functional suites, and added no test. The current commit changes an existing unit-test assertion. In retries_incomplete_remaining_pool_transactions, the legacy mode now expects both transaction hashes instead of one. GitHub's checks also report successful automated jobs.

The source and test change support the mechanism, while CI records automated build and test outcomes. An independent repetition of the three-wallet experiment would provide a different check. We inspected the code and public results but did not rerun that experiment or the Monero test suites. No separate reproduction or maintainer review appeared in the retrieved discussion.

Fetch first, then decide what to process

The patch separates fetching from processing. It requests announced transactions absent from the scanned-transaction caches, then applies the ownership-sensitive helper after their bodies arrive. The daemon no longer sees that ownership decision in this request-selection step.

It also records fetched transactions that the wallet chooses not to process. Otherwise, outgoing transactions could become the ones requested repeatedly on later refreshes. The change applies that bookkeeping to both pool synchronization paths, covering a switch between them.

The author describes the cost as one extra pruned body per sent transaction per wallet session. That describes the tested case. The code rotates bounded caches, and errors or later state changes can affect requests, so traffic can still differ between wallets with different histories.

Affected code and practical limits

The v0.18.5.4 wallet source contains both the fallback and the pre-fetch ownership filter. The official release record and download page identify that version as the current CLI release at review. Our release finding is limited to that source inspection. Earlier releases and third-party wallet implementations were not exhaustively assessed.

The observer needs access to the wallet's daemon requests. A malicious remote-node operator can supply the fallback-triggering response and compare the resulting requests. The outgoing transaction must still be relevant to the wallet's pending state. Cache history, self-transfers, pool changes and request failures complicate inference.

The resulting association is between a transaction and a wallet connection or session. Identifying a person requires other information. The observed requests contain transaction hashes, not seeds or private spend or view keys. The described inference gives the observer no spending authority and does not establish recipients or payment amounts.

Reducing exposure to remote-node observers

Using a synchronized node you control removes an outside daemon operator from the wallet-RPC connection. Other network privacy risks remain. A trusted operator reduces reliance on an unknown party but can still observe requests.

Tor can help conceal your network address from a remote daemon. It does not hide the RPC requests from their destination or make an ownership-dependent request uniform. Encryption in transit leaves the endpoint able to read requests.

Check official wallet release notes for this change before treating an update as protection. Updating only a remote daemon does not replace the wallet-side filtering code. Our remote-node connection guide explains setup choices. The separate remote-node fee safeguards concern misleading fee estimates under PR #11295, not this pending-transaction inference.

Support Monero Love

Help keep the public nodes and packages running. Send any amount of XMR.

Scan with a Monero wallet to donate to Monero Love.89hGTKxRVA3AUeKAbCRBt5TMSzq3MQZooNNsezCpNFjp2oeJPRx8AJeGKA8ziHZNqab78f4Vr1ntxhPDxX5298nnGZjHkhm