From 6a51fcf1bd3b4dda9dbd276a1e342b2a4531b34f Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Wed, 28 Aug 2013 15:41:46 -0700 Subject: [PATCH] [raw] reject insanely high fees by default in sendrawtransaction There have been several incidents where mainnet experimentation with raw transactions resulted in insane fees. This is hard to prevent in the raw transaction api because the inputs may not be known. Since sending doesn't work if the inputs aren't known, we can catch it there. This rejects fees > than 10000 * nMinRelayTxFee or 1 BTC with the defaults and can be overridden with a bool at the rpc. Conflicts: src/main.cpp src/main.h src/rpcrawtransaction.cpp Rebased-from: 6349de20f90f046aefa9eaae0de8e3faf11527fa 0.8.x --- src/bitcoinrpc.cpp | 1 + src/main.cpp | 11 ++++++++--- src/main.h | 4 ++-- src/rpcrawtransaction.cpp | 10 +++++++--- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 88df45fd5..ad7e574ad 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -1188,6 +1188,7 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector 1) ConvertTo(params[1]); if (strMethod == "signrawtransaction" && n > 1) ConvertTo(params[1], true); if (strMethod == "signrawtransaction" && n > 2) ConvertTo(params[2], true); + if (strMethod == "sendrawtransaction" && n > 1) ConvertTo(params[1], true); if (strMethod == "gettxout" && n > 1) ConvertTo(params[1]); if (strMethod == "gettxout" && n > 2) ConvertTo(params[2]); if (strMethod == "lockunspent" && n > 0) ConvertTo(params[0]); diff --git a/src/main.cpp b/src/main.cpp index c9e92966c..9865bafd3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -649,7 +649,7 @@ void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins) } bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckInputs, bool fLimitFree, - bool* pfMissingInputs) + bool* pfMissingInputs, bool fRejectInsaneFee) { if (pfMissingInputs) *pfMissingInputs = false; @@ -784,6 +784,11 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckIn dFreeCount += nSize; } + if (fRejectInsaneFee && nFees > CTransaction::nMinRelayTxFee * 10000) + return error("CTxMemPool::accept() : insane fees %s, %"PRI64d" > %"PRI64d, + hash.ToString().c_str(), + nFees, CTransaction::nMinRelayTxFee * 10000); + // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. if (!tx.CheckInputs(state, view, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC)) @@ -812,10 +817,10 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckIn return true; } -bool CTransaction::AcceptToMemoryPool(CValidationState &state, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs) +bool CTransaction::AcceptToMemoryPool(CValidationState &state, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs, bool fRejectInsaneFee) { try { - return mempool.accept(state, *this, fCheckInputs, fLimitFree, pfMissingInputs); + return mempool.accept(state, *this, fCheckInputs, fLimitFree, pfMissingInputs, fRejectInsaneFee); } catch(std::runtime_error &e) { return state.Abort(_("System error: ") + e.what()); } diff --git a/src/main.h b/src/main.h index ec305ae9e..81e71955b 100644 --- a/src/main.h +++ b/src/main.h @@ -682,7 +682,7 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach bool CheckTransaction(CValidationState &state) const; // Try to accept this transaction into the memory pool - bool AcceptToMemoryPool(CValidationState &state, bool fCheckInputs=true, bool fLimitFree = true, bool* pfMissingInputs=NULL); + bool AcceptToMemoryPool(CValidationState &state, bool fCheckInputs=true, bool fLimitFree = true, bool* pfMissingInputs=NULL, bool fRejectInsaneFee = false); protected: static const CTxOut &GetOutputFor(const CTxIn& input, CCoinsViewCache& mapInputs); @@ -2099,7 +2099,7 @@ public: std::map mapTx; std::map mapNextTx; - bool accept(CValidationState &state, CTransaction &tx, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs); + bool accept(CValidationState &state, CTransaction &tx, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs, bool fRejectInsaneFee = false); bool addUnchecked(const uint256& hash, const CTransaction &tx); bool remove(const CTransaction &tx, bool fRecursive = false); bool removeConflicts(const CTransaction &tx); diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index f4166bb9e..6c199219d 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -529,9 +529,9 @@ Value signrawtransaction(const Array& params, bool fHelp) Value sendrawtransaction(const Array& params, bool fHelp) { - if (fHelp || params.size() < 1 || params.size() > 1) + if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( - "sendrawtransaction \n" + "sendrawtransaction [allowhighfees=false]\n" "Submits raw transaction (serialized, hex-encoded) to local node and network."); // parse hex string from parameter @@ -539,6 +539,10 @@ Value sendrawtransaction(const Array& params, bool fHelp) CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); CTransaction tx; + bool fOverrideFees = false; + if (params.size() > 1) + fOverrideFees = params[1].get_bool(); + // deserialize binary data stream try { ssData >> tx; @@ -556,7 +560,7 @@ Value sendrawtransaction(const Array& params, bool fHelp) if (!fHave) { // push to local node CValidationState state; - if (!tx.AcceptToMemoryPool(state, true, false)) + if (!tx.AcceptToMemoryPool(state, true, false, NULL, !fOverrideFees)) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); // TODO: report validation state } }