Browse Source

Fix wallet RPC race by waiting for callbacks in sendrawtransaction

0.16
Matt Corallo 7 years ago
parent
commit
cb06edf938
  1. 23
      src/rpc/rawtransaction.cpp

23
src/rpc/rawtransaction.cpp

@ -11,6 +11,7 @@
#include "init.h" #include "init.h"
#include "keystore.h" #include "keystore.h"
#include "validation.h" #include "validation.h"
#include "validationinterface.h"
#include "merkleblock.h" #include "merkleblock.h"
#include "net.h" #include "net.h"
#include "policy/policy.h" #include "policy/policy.h"
@ -30,6 +31,7 @@
#include "wallet/wallet.h" #include "wallet/wallet.h"
#endif #endif
#include <future>
#include <stdint.h> #include <stdint.h>
#include <univalue.h> #include <univalue.h>
@ -917,7 +919,9 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
); );
ObserveSafeMode(); ObserveSafeMode();
LOCK(cs_main);
std::promise<void> promise;
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL}); RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL});
// parse hex string from parameter // parse hex string from parameter
@ -931,6 +935,8 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
if (!request.params[1].isNull() && request.params[1].get_bool()) if (!request.params[1].isNull() && request.params[1].get_bool())
nMaxRawTxFee = 0; nMaxRawTxFee = 0;
{ // cs_main scope
LOCK(cs_main);
CCoinsViewCache &view = *pcoinsTip; CCoinsViewCache &view = *pcoinsTip;
bool fHaveChain = false; bool fHaveChain = false;
for (size_t o = 0; !fHaveChain && o < tx->vout.size(); o++) { for (size_t o = 0; !fHaveChain && o < tx->vout.size(); o++) {
@ -952,10 +958,24 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
} }
throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason()); 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) { } else if (fHaveChain) {
throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain"); throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain");
} }
} // cs_main
promise.get_future().wait();
if(!g_connman) if(!g_connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
@ -964,6 +984,7 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
{ {
pnode->PushInventory(inv); pnode->PushInventory(inv);
}); });
return hashTx.GetHex(); return hashTx.GetHex();
} }

Loading…
Cancel
Save