Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
partial bitcoin#14978: Factor BroadcastTransaction out of sendrawtran…
…saction
  • Loading branch information
kwvg committed Jul 16, 2021
commit b88b35790d21d9876bb17fdb584be9f8751ba944
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ BITCOIN_CORE_H = \
netfulfilledman.h \
netmessagemaker.h \
node/coinstats.h \
node/transaction.h \
noui.h \
policy/feerate.h \
policy/fees.h \
Expand Down Expand Up @@ -367,6 +368,7 @@ libdash_server_a_SOURCES = \
netfulfilledman.cpp \
net_processing.cpp \
node/coinstats.cpp \
node/transaction.cpp \
noui.cpp \
policy/fees.cpp \
policy/policy.cpp \
Expand Down
76 changes: 76 additions & 0 deletions src/node/transaction.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <consensus/validation.h>
#include <net.h>
#include <rpc/server.h>
#include <txmempool.h>
#include <validation.h>
#include <validationinterface.h>
#include <node/transaction.h>
#include <util/validation.h>

#include <future>

uint256 BroadcastTransaction(const CTransactionRef tx, const bool allowhighfees) {
std::promise<void> promise;
const uint256& hashTx = tx->GetHash();

CAmount nMaxRawTxFee = maxTxFee;
if (allowhighfees)
nMaxRawTxFee = 0;

{ // cs_main scope
LOCK(cs_main);
CCoinsViewCache &view = *pcoinsTip;
bool fHaveChain = false;
for (size_t o = 0; !fHaveChain && o < tx->vout.size(); o++) {
const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o));
fHaveChain = !existingCoin.IsSpent();
}
bool fHaveMempool = mempool.exists(hashTx);
if (!fHaveMempool && !fHaveChain) {
// push to local node and sync with wallets
CValidationState state;
bool fMissingInputs;
if (!AcceptToMemoryPool(mempool, state, std::move(tx), &fMissingInputs,
false /* plTxnReplaced */, false /* bypass_limits */, nMaxRawTxFee)) {
if (state.IsInvalid()) {
throw JSONRPCError(RPC_TRANSACTION_REJECTED, FormatStateMessage(state));
} else {
if (fMissingInputs) {
throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs");
}
throw JSONRPCError(RPC_TRANSACTION_ERROR, FormatStateMessage(state));
}
} 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");
} else {
// Make sure we don't block forever if re-sending
// a transaction already in mempool.
promise.set_value();
}

} // cs_main

promise.get_future().wait();

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

g_connman->RelayTransaction(*tx);

return hashTx;
}
14 changes: 14 additions & 0 deletions src/node/transaction.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) 2017-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_NODE_TRANSACTION_H
#define BITCOIN_NODE_TRANSACTION_H

#include <primitives/transaction.h>
#include <uint256.h>

/** Broadcast a transaction */
uint256 BroadcastTransaction(CTransactionRef tx, bool allowhighfees = false);

#endif // BITCOIN_NODE_TRANSACTION_H
68 changes: 4 additions & 64 deletions src/rpc/rawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <key_io.h>
#include <merkleblock.h>
#include <net.h>
#include <node/transaction.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
#include <rpc/rawtransaction.h>
Expand All @@ -40,7 +41,6 @@
#include <llmq/quorums_commitment.h>
#include <llmq/quorums_instantsend.h>

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

#include <univalue.h>
Expand Down Expand Up @@ -1096,76 +1096,16 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
+ HelpExampleRpc("sendrawtransaction", "\"signedhex\"")
);

std::promise<void> promise;

RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, 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)));
const uint256& hashTx = tx->GetHash();

CAmount nMaxRawTxFee = maxTxFee;
if (!request.params[1].isNull() && request.params[1].get_bool())
nMaxRawTxFee = 0;

bool fBypassLimits = false;
if (!request.params[3].isNull())
fBypassLimits = request.params[3].get_bool();

{ // cs_main scope
LOCK(cs_main);
CCoinsViewCache &view = *pcoinsTip;
bool fHaveChain = false;
for (size_t o = 0; !fHaveChain && o < tx->vout.size(); o++) {
const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o));
fHaveChain = !existingCoin.IsSpent();
}
bool fHaveMempool = mempool.exists(hashTx);
if (!fHaveMempool && !fHaveChain) {
// push to local node and sync with wallets
CValidationState state;
bool fMissingInputs;
if (!AcceptToMemoryPool(mempool, state, std::move(tx), &fMissingInputs,
fBypassLimits /* bypass_limits */, nMaxRawTxFee)) {
if (state.IsInvalid()) {
throw JSONRPCError(RPC_TRANSACTION_REJECTED, FormatStateMessage(state));
} else {
if (fMissingInputs) {
throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs");
}
throw JSONRPCError(RPC_TRANSACTION_ERROR, FormatStateMessage(state));
}
} 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");
} else {
// Make sure we don't block forever if re-sending
// a transaction already in mempool.
promise.set_value();
}

} // cs_main

promise.get_future().wait();

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

g_connman->RelayTransaction(*tx);

return hashTx.GetHex();
bool allowhighfees = false;
if (!request.params[1].isNull()) allowhighfees = request.params[1].get_bool();
return BroadcastTransaction(tx, allowhighfees).GetHex();
}

static UniValue testmempoolaccept(const JSONRPCRequest& request)
Expand Down