Alert Cobalt Dove
High
The issue originates from how the IsUTXOSpent function checks whether a UTXO is spent by calling the RPC client's GetTxOut method with the mempool check disabled. Here's the problematic code snippet:
txOut, err := b.client.GetTxOut(hash, vout, false)
The third parameter in GetTxOut is a boolean flag that determines whether transactions in the mempool (i.e., unconfirmed transactions) are considered when checking for the existence of a UTXO. In this case, passing false
means that only confirmed transactions are checked. This creates a blind spot where a UTXO being spent in an unconfirmed (mempool) transaction is not detected as spent.
When a UTXO is spent in a confirmed transaction, GetTxOut correctly returns nil
, and the function returns true
(i.e., the UTXO is spent).
Suppose a transaction is broadcast to the network and is in the mempool, but not yet confirmed. The UTXO referenced by that transaction is effectively being spent. However, because the function calls GetTxOut with false
for the mempool parameter, it ignores this unconfirmed transaction. GetTxOut returns a non-nil
result, leading the function to incorrectly conclude that the UTXO is unspent.
Here’s a step-by-step explanation of how this bug comes into play:
- Step 1: A UTXO exists in the blockchain and is unspent.
- Step 2: A new transaction is created to spend this UTXO and is broadcast to the network. It enters the mempool but is not yet confirmed.
- Step 3: The system, relying on IsUTXOSpent to determine the availability of funds, calls the function.
- Step 4: The function executes:
Since
txOut, err := b.client.GetTxOut(hash, vout, false)
false
is passed, the RPC call ignores the mempool. - Step 5: GetTxOut returns a non-
nil
value because it sees the UTXO as unspent in the confirmed blockchain state, despite the mempool transaction spending it. - Step 6: The function then returns
false
for the "spent" flag, incorrectly indicating that the UTXO is available.
If the system uses this function to verify UTXO availability before initiating a fund transfer, it might inadvertently allow the same UTXO to be spent twice—once in the pending transaction and again in a new transaction. This can lead to a situation where funds are double-allocated or even double-spent, causing potential financial loss or other unintended behaviors in fund management systems.
To resolve the vulnerability, the function should include mempool data when checking the UTXO's status. This can be done by setting the third parameter of GetTxOut to true
:
txOut, err := b.client.GetTxOut(hash, vout, true)