Call wallet notify callbacks in scheduler thread (without cs_main) #10286
Open
TheBlueMatt
wants to merge 11 commits into
bitcoin:master
from
TheBlueMatt:2017-01-wallet-cache-inmempool-4
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
3e3dfec
Add a CValidationInterface::TransactionRemovedFromMempool
TheBlueMatt a000c66
Add ability to assert a lock is not held in DEBUG_LOCKORDER
TheBlueMatt 71f168a
Add CWallet::BlockUntilSyncedToCurrentChain()
TheBlueMatt b9bd729
Use callbacks to cache whether wallet transactions are in mempool
TheBlueMatt 0320be0
Call TransactionRemovedFromMempool in the CScheduler thread
TheBlueMatt c83ae72
Add CallFunctionInQueue to wait on validation interface queue drain
TheBlueMatt 15e5d05
Add calls to CWallet::BlockUntilSyncedToCurrentChain() in RPCs
TheBlueMatt 8e1d03b
Fix zmq tests now that txn/blocks are unordered
TheBlueMatt 964f376
Add a dev notes document describing the new wallet RPC blocking
TheBlueMatt 62e416d
Also call other wallet notify callbacks in scheduler thread
TheBlueMatt 5d4a81a
Fix wallet RPC race by waiting for callbacks in sendrawtransaction
TheBlueMatt
Jump to file or symbol
Failed to load files and symbols.
| @@ -11,6 +11,7 @@ | ||
| #include "init.h" | ||
| #include "keystore.h" | ||
| #include "validation.h" | ||
| +#include "validationinterface.h" | ||
| #include "merkleblock.h" | ||
| #include "net.h" | ||
| #include "policy/policy.h" | ||
| @@ -30,6 +31,7 @@ | ||
| #endif | ||
| #include <stdint.h> | ||
| +#include <future> | ||
| #include <univalue.h> | ||
| @@ -847,14 +849,18 @@ UniValue sendrawtransaction(const JSONRPCRequest& request) | ||
| + HelpExampleRpc("sendrawtransaction", "\"signedhex\"") | ||
| ); | ||
| + CTransactionRef tx; | ||
| + std::promise<void> promise; | ||
| + | ||
| + { // cs_main scope | ||
| LOCK(cs_main); | ||
| RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL}); | ||
| // parse hex string from parameter | ||
| CMutableTransaction mtx; | ||
| if (!DecodeHexTx(mtx, request.params[0].get_str())) | ||
| throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); | ||
| - CTransactionRef tx(MakeTransactionRef(std::move(mtx))); | ||
| + tx = MakeTransactionRef(std::move(mtx)); | ||
| const uint256& hashTx = tx->GetHash(); | ||
| CAmount nMaxRawTxFee = maxTxFee; | ||
| @@ -873,7 +879,7 @@ UniValue sendrawtransaction(const JSONRPCRequest& request) | ||
| CValidationState state; | ||
| bool fMissingInputs; | ||
| bool fLimitFree = true; | ||
| - if (!AcceptToMemoryPool(mempool, state, std::move(tx), fLimitFree, &fMissingInputs, NULL, false, nMaxRawTxFee)) { | ||
| + if (!AcceptToMemoryPool(mempool, state, tx, fLimitFree, &fMissingInputs, NULL, false, nMaxRawTxFee)) { | ||
| if (state.IsInvalid()) { | ||
| throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); | ||
| } else { | ||
| @@ -882,19 +888,34 @@ 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(); | ||
TheBlueMatt
Contributor
|
||
| + | ||
| if(!g_connman) | ||
| throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); | ||
| - CInv inv(MSG_TX, hashTx); | ||
| + CInv inv(MSG_TX, tx->GetHash()); | ||
| g_connman->ForEachNode([&inv](CNode* pnode) | ||
| { | ||
| pnode->PushInventory(inv); | ||
| }); | ||
| - return hashTx.GetHex(); | ||
| + | ||
| + return tx->GetHash().GetHex(); | ||
| } | ||
| static const CRPCCommand commands[] = | ||
10
src/sync.cpp
Oops, something went wrong.
In commit "Fix wallet RPC race by waiting for callbacks in sendrawtransaction"
Might be more efficient to wait for the promise after the PushInventory calls so they aren't blocked waiting for wallets.