Skip to content

Commit

Permalink
Fix wallet RPC race by waiting for callbacks in sendrawtransaction
Browse files Browse the repository at this point in the history
  • Loading branch information
TheBlueMatt authored and furszy committed Feb 21, 2021
1 parent c7ab490 commit 7d05997
Showing 1 changed file with 21 additions and 2 deletions.
23 changes: 21 additions & 2 deletions src/rpc/rawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "core_io.h"
#include "init.h"
#include "keystore.h"
#include "validationinterface.h"
#include "net.h"
#include "policy/policy.h"
#include "primitives/transaction.h"
Expand All @@ -25,6 +26,7 @@
#include "wallet/wallet.h"
#endif

#include <future>
#include <stdint.h>

#include <univalue.h>
Expand Down Expand Up @@ -903,6 +905,8 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
"\nSend the transaction (signed hex)\n" + HelpExampleCli("sendrawtransaction", "\"signedhex\"") +
"\nAs a json rpc call\n" + HelpExampleRpc("sendrawtransaction", "\"signedhex\""));

std::promise<void> promise;

RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL});

// parse hex string from parameter
Expand All @@ -915,7 +919,8 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
if (request.params.size() > 1)
fOverrideFees = request.params[1].get_bool();

AssertLockNotHeld(cs_main);
{ // cs_main scope
LOCK(cs_main);
CCoinsViewCache& view = *pcoinsTip;
bool fHaveChain = false;
for (size_t o = 0; !fHaveChain && o < mtx.vout.size(); o++) {
Expand All @@ -927,7 +932,6 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
CValidationState state;
bool fMissingInputs;
{
LOCK(cs_main);
if (!AcceptToMemoryPool(mempool, state, MakeTransactionRef(std::move(mtx)), true, &fMissingInputs, false, !fOverrideFees)) {
if (state.IsInvalid()) {
throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason()));
Expand All @@ -937,11 +941,25 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
}
throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason());
}
} else {
// If wallet is enabled, ensure that the wallet has been made aware
// of the new transaction prior to returning. This prevents a race
// where a user might call sendrawtransaction with a transaction
// to/from their wallet, immediately call some wallet RPC, and get
// a stale result because callbacks have not yet been processed.
CallFunctionInValidationInterfaceQueue([&promise] {
promise.set_value();
});
}
}
} else if (fHaveChain) {
throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain");
}

} // cs_main

promise.get_future().wait();

if(!g_connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");

Expand All @@ -950,6 +968,7 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
{
pnode->PushInventory(inv);
});

return hashTx.GetHex();
}

Expand Down

0 comments on commit 7d05997

Please sign in to comment.