Changeset View
Changeset View
Standalone View
Standalone View
src/node/transaction.cpp
Show All 12 Lines | |||||
#include <txmempool.h> | #include <txmempool.h> | ||||
#include <util/validation.h> | #include <util/validation.h> | ||||
#include <validation.h> | #include <validation.h> | ||||
#include <validationinterface.h> | #include <validationinterface.h> | ||||
#include <future> | #include <future> | ||||
TransactionError BroadcastTransaction(const Config &config, | TransactionError BroadcastTransaction(const Config &config, | ||||
const CTransactionRef tx, TxId &txid, | const CTransactionRef tx, | ||||
std::string &err_string, | std::string &err_string, | ||||
const Amount highFee) { | const Amount max_tx_fee, bool relay, | ||||
bool wait_callback) { | |||||
assert(g_connman); | |||||
std::promise<void> promise; | std::promise<void> promise; | ||||
txid = tx->GetId(); | TxId txid = tx->GetId(); | ||||
bool callback_set = false; | |||||
{ // cs_main scope | { // cs_main scope | ||||
LOCK(cs_main); | 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++) { | ||||
const Coin &existingCoin = view.AccessCoin(COutPoint(txid, o)); | const Coin &existingCoin = view.AccessCoin(COutPoint(txid, o)); | ||||
fHaveChain = !existingCoin.IsSpent(); | fHaveChain = !existingCoin.IsSpent(); | ||||
} | } | ||||
bool fHaveMempool = g_mempool.exists(txid); | bool fHaveMempool = g_mempool.exists(txid); | ||||
if (!fHaveMempool && !fHaveChain) { | if (!fHaveMempool && !fHaveChain) { | ||||
// Push to local node and sync with wallets. | // Push to local node and sync with wallets. | ||||
CValidationState state; | CValidationState state; | ||||
bool fMissingInputs; | bool fMissingInputs; | ||||
if (!AcceptToMemoryPool(config, g_mempool, state, std::move(tx), | if (!AcceptToMemoryPool(config, g_mempool, state, std::move(tx), | ||||
&fMissingInputs, false /* bypass_limits */, | &fMissingInputs, false /* bypass_limits */, | ||||
highFee)) { | max_tx_fee)) { | ||||
if (state.IsInvalid()) { | if (state.IsInvalid()) { | ||||
err_string = FormatStateMessage(state); | err_string = FormatStateMessage(state); | ||||
return TransactionError::MEMPOOL_REJECTED; | return TransactionError::MEMPOOL_REJECTED; | ||||
} | } | ||||
if (fMissingInputs) { | if (fMissingInputs) { | ||||
return TransactionError::MISSING_INPUTS; | return TransactionError::MISSING_INPUTS; | ||||
} | } | ||||
err_string = FormatStateMessage(state); | err_string = FormatStateMessage(state); | ||||
return TransactionError::MEMPOOL_ERROR; | return TransactionError::MEMPOOL_ERROR; | ||||
} else { | } else if (wait_callback) { | ||||
// If wallet is enabled, ensure that the wallet has been made | // If wallet is enabled, ensure that the wallet has been made | ||||
// aware of the new transaction prior to returning. This | // aware of the new transaction prior to returning. This | ||||
// prevents a race where a user might call sendrawtransaction | // prevents a race where a user might call sendrawtransaction | ||||
// with a transaction to/from their wallet, immediately call | // with a transaction to/from their wallet, immediately call | ||||
// some wallet RPC, and get a stale result because callbacks | // some wallet RPC, and get a stale result because callbacks | ||||
// have not yet been processed. | // have not yet been processed. | ||||
CallFunctionInValidationInterfaceQueue( | CallFunctionInValidationInterfaceQueue( | ||||
[&promise] { promise.set_value(); }); | [&promise] { promise.set_value(); }); | ||||
callback_set = true; | |||||
} | } | ||||
} else if (fHaveChain) { | } else if (fHaveChain) { | ||||
return TransactionError::ALREADY_IN_CHAIN; | return TransactionError::ALREADY_IN_CHAIN; | ||||
} else { | |||||
// Make sure we don't block forever if re-sending a transaction | |||||
// already in mempool. | |||||
promise.set_value(); | |||||
} | } | ||||
} // cs_main | } // cs_main | ||||
if (callback_set) { | |||||
promise.get_future().wait(); | promise.get_future().wait(); | ||||
if (!g_connman) { | |||||
return TransactionError::P2P_DISABLED; | |||||
} | } | ||||
if (relay) { | |||||
RelayTransaction(txid, *g_connman); | RelayTransaction(txid, *g_connman); | ||||
} | |||||
return TransactionError::OK; | return TransactionError::OK; | ||||
} | } |