[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.
This commit is contained in:
parent
bb7d0fc12f
commit
9d14e689c8
4 changed files with 15 additions and 5 deletions
|
@ -1193,6 +1193,7 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri
|
||||||
if (strMethod == "createrawtransaction" && n > 1) ConvertTo<Object>(params[1]);
|
if (strMethod == "createrawtransaction" && n > 1) ConvertTo<Object>(params[1]);
|
||||||
if (strMethod == "signrawtransaction" && n > 1) ConvertTo<Array>(params[1], true);
|
if (strMethod == "signrawtransaction" && n > 1) ConvertTo<Array>(params[1], true);
|
||||||
if (strMethod == "signrawtransaction" && n > 2) ConvertTo<Array>(params[2], true);
|
if (strMethod == "signrawtransaction" && n > 2) ConvertTo<Array>(params[2], true);
|
||||||
|
if (strMethod == "sendrawtransaction" && n > 1) ConvertTo<bool>(params[1], true);
|
||||||
if (strMethod == "gettxout" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
if (strMethod == "gettxout" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||||
if (strMethod == "gettxout" && n > 2) ConvertTo<bool>(params[2]);
|
if (strMethod == "gettxout" && n > 2) ConvertTo<bool>(params[2]);
|
||||||
if (strMethod == "lockunspent" && n > 0) ConvertTo<bool>(params[0]);
|
if (strMethod == "lockunspent" && n > 0) ConvertTo<bool>(params[0]);
|
||||||
|
|
|
@ -787,7 +787,7 @@ void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fLimitFree,
|
bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fLimitFree,
|
||||||
bool* pfMissingInputs)
|
bool* pfMissingInputs, bool fRejectInsaneFee)
|
||||||
{
|
{
|
||||||
if (pfMissingInputs)
|
if (pfMissingInputs)
|
||||||
*pfMissingInputs = false;
|
*pfMissingInputs = false;
|
||||||
|
@ -921,6 +921,11 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL
|
||||||
dFreeCount += nSize;
|
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
|
// Check against previous transactions
|
||||||
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
|
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
|
||||||
if (!CheckInputs(tx, state, view, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC))
|
if (!CheckInputs(tx, state, view, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC))
|
||||||
|
|
|
@ -1082,7 +1082,7 @@ public:
|
||||||
std::map<uint256, CTransaction> mapTx;
|
std::map<uint256, CTransaction> mapTx;
|
||||||
std::map<COutPoint, CInPoint> mapNextTx;
|
std::map<COutPoint, CInPoint> mapNextTx;
|
||||||
|
|
||||||
bool accept(CValidationState &state, const CTransaction &tx, bool fLimitFree, bool* pfMissingInputs);
|
bool accept(CValidationState &state, const CTransaction &tx, bool fLimitFree, bool* pfMissingInputs, bool fRejectInsaneFee = false);
|
||||||
bool addUnchecked(const uint256& hash, const CTransaction &tx);
|
bool addUnchecked(const uint256& hash, const CTransaction &tx);
|
||||||
bool remove(const CTransaction &tx, bool fRecursive = false);
|
bool remove(const CTransaction &tx, bool fRecursive = false);
|
||||||
bool removeConflicts(const CTransaction &tx);
|
bool removeConflicts(const CTransaction &tx);
|
||||||
|
|
|
@ -527,9 +527,9 @@ Value signrawtransaction(const Array& params, bool fHelp)
|
||||||
|
|
||||||
Value sendrawtransaction(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(
|
throw runtime_error(
|
||||||
"sendrawtransaction <hex string>\n"
|
"sendrawtransaction <hex string> [allowhighfees=false]\n"
|
||||||
"Submits raw transaction (serialized, hex-encoded) to local node and network.");
|
"Submits raw transaction (serialized, hex-encoded) to local node and network.");
|
||||||
|
|
||||||
// parse hex string from parameter
|
// parse hex string from parameter
|
||||||
|
@ -537,6 +537,10 @@ Value sendrawtransaction(const Array& params, bool fHelp)
|
||||||
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
|
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
|
||||||
CTransaction tx;
|
CTransaction tx;
|
||||||
|
|
||||||
|
bool fOverrideFees = false;
|
||||||
|
if (params.size() > 1)
|
||||||
|
fOverrideFees = params[1].get_bool();
|
||||||
|
|
||||||
// deserialize binary data stream
|
// deserialize binary data stream
|
||||||
try {
|
try {
|
||||||
ssData >> tx;
|
ssData >> tx;
|
||||||
|
@ -554,7 +558,7 @@ Value sendrawtransaction(const Array& params, bool fHelp)
|
||||||
if (!fHave) {
|
if (!fHave) {
|
||||||
// push to local node
|
// push to local node
|
||||||
CValidationState state;
|
CValidationState state;
|
||||||
if (!mempool.accept(state, tx, false, NULL))
|
if (!mempool.accept(state, tx, false, NULL, !fOverrideFees))
|
||||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); // TODO: report validation state
|
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); // TODO: report validation state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue