diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index c6268745caf13..2ccb6c3dddd4c 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -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" @@ -25,6 +26,7 @@ #include "wallet/wallet.h" #endif +#include #include #include @@ -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 promise; + RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL}); // parse hex string from parameter @@ -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++) { @@ -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())); @@ -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"); @@ -950,6 +968,7 @@ UniValue sendrawtransaction(const JSONRPCRequest& request) { pnode->PushInventory(inv); }); + return hashTx.GetHex(); }