Browse Source

Type-safe CFeeRate class

Use CFeeRate instead of an int64_t for quantities that are
fee-per-size.

Helps prevent unit-conversion mismatches between the wallet,
relaying, and mining code.
0.10
Gavin Andresen 11 years ago
parent
commit
c6cb21d17a
  1. 19
      src/core.cpp
  2. 37
      src/core.h
  3. 14
      src/init.cpp
  4. 30
      src/main.cpp
  5. 42
      src/miner.cpp
  6. 29
      src/qt/coincontroldialog.cpp
  7. 2
      src/qt/guiutil.cpp
  8. 4
      src/qt/optionsdialog.cpp
  9. 21
      src/qt/optionsmodel.cpp
  10. 2
      src/qt/paymentserver.cpp
  11. 6
      src/qt/walletmodel.cpp
  12. 4
      src/rpcmisc.cpp
  13. 2
      src/rpcnet.cpp
  14. 2
      src/rpcwallet.cpp
  15. 22
      src/wallet.cpp
  16. 2
      src/wallet.h

19
src/core.cpp

@ -72,6 +72,25 @@ void CTxOut::print() const
LogPrintf("%s\n", ToString()); LogPrintf("%s\n", ToString());
} }
CFeeRate::CFeeRate(int64_t nFeePaid, size_t nSize)
{
if (nSize > 0)
nSatoshisPerK = nFeePaid*1000/nSize;
else
nSatoshisPerK = 0;
}
int64_t CFeeRate::GetFee(size_t nSize)
{
return nSatoshisPerK*nSize / 1000;
}
std::string CFeeRate::ToString() const
{
std::string result = FormatMoney(nSatoshisPerK) + " BTC/kB";
return result;
}
uint256 CTransaction::GetHash() const uint256 CTransaction::GetHash() const
{ {
return SerializeHash(*this); return SerializeHash(*this);

37
src/core.h

@ -112,6 +112,28 @@ public:
/** Type-safe wrapper class to for fee rates
* (how much to pay based on transaction size)
*/
class CFeeRate
{
private:
int64_t nSatoshisPerK; // unit is satoshis-per-1,000-bytes
public:
explicit CFeeRate(int64_t _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) { }
CFeeRate(int64_t nFeePaid, size_t nSize);
CFeeRate(const CFeeRate& other) { nSatoshisPerK = other.nSatoshisPerK; }
int64_t GetFee(size_t size); // unit returned is satoshis
int64_t GetFeePerK() { return GetFee(1000); } // satoshis-per-1000-bytes
friend bool operator<(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK < b.nSatoshisPerK; }
friend bool operator>(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK > b.nSatoshisPerK; }
friend bool operator==(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK == b.nSatoshisPerK; }
std::string ToString() const;
};
/** An output of a transaction. It contains the public key that the next input /** An output of a transaction. It contains the public key that the next input
* must be able to sign with to claim it. * must be able to sign with to claim it.
@ -148,17 +170,18 @@ public:
uint256 GetHash() const; uint256 GetHash() const;
bool IsDust(int64_t nMinRelayTxFee) const bool IsDust(CFeeRate minRelayTxFee) const
{ {
// "Dust" is defined in terms of CTransaction::nMinRelayTxFee, // "Dust" is defined in terms of CTransaction::minRelayTxFee,
// which has units satoshis-per-kilobyte. // which has units satoshis-per-kilobyte.
// If you'd pay more than 1/3 in fees // If you'd pay more than 1/3 in fees
// to spend something, then we consider it dust. // to spend something, then we consider it dust.
// A typical txout is 34 bytes big, and will // A typical txout is 34 bytes big, and will
// need a CTxIn of at least 148 bytes to spend, // need a CTxIn of at least 148 bytes to spend:
// so dust is a txout less than 546 satoshis // so dust is a txout less than 546 satoshis
// with default nMinRelayTxFee. // with default minRelayTxFee.
return ((nValue*1000)/(3*((int)GetSerializeSize(SER_DISK,0)+148)) < nMinRelayTxFee); size_t nSize = GetSerializeSize(SER_DISK,0)+148u;
return (nValue < 3*minRelayTxFee.GetFee(nSize));
} }
friend bool operator==(const CTxOut& a, const CTxOut& b) friend bool operator==(const CTxOut& a, const CTxOut& b)
@ -183,8 +206,8 @@ public:
class CTransaction class CTransaction
{ {
public: public:
static int64_t nMinTxFee; static CFeeRate minTxFee;
static int64_t nMinRelayTxFee; static CFeeRate minRelayTxFee;
static const int CURRENT_VERSION=1; static const int CURRENT_VERSION=1;
int nVersion; int nVersion;
std::vector<CTxIn> vin; std::vector<CTxIn> vin;

14
src/init.cpp

@ -281,8 +281,8 @@ std::string HelpMessage(HelpMessageMode hmm)
strUsage += " -limitfreerelay=<n> " + _("Continuously rate-limit free transactions to <n>*1000 bytes per minute (default:15)") + "\n"; strUsage += " -limitfreerelay=<n> " + _("Continuously rate-limit free transactions to <n>*1000 bytes per minute (default:15)") + "\n";
strUsage += " -maxsigcachesize=<n> " + _("Limit size of signature cache to <n> entries (default: 50000)") + "\n"; strUsage += " -maxsigcachesize=<n> " + _("Limit size of signature cache to <n> entries (default: 50000)") + "\n";
} }
strUsage += " -mintxfee=<amt> " + _("Fees smaller than this are considered zero fee (for transaction creation) (default:") + " " + FormatMoney(CTransaction::nMinTxFee) + ")" + "\n"; strUsage += " -mintxfee=<amt> " + _("Fees smaller than this are considered zero fee (for transaction creation) (default:") + " " + FormatMoney(CTransaction::minTxFee.GetFeePerK()) + ")" + "\n";
strUsage += " -minrelaytxfee=<amt> " + _("Fees smaller than this are considered zero fee (for relaying) (default:") + " " + FormatMoney(CTransaction::nMinRelayTxFee) + ")" + "\n"; strUsage += " -minrelaytxfee=<amt> " + _("Fees smaller than this are considered zero fee (for relaying) (default:") + " " + FormatMoney(CTransaction::minRelayTxFee.GetFeePerK()) + ")" + "\n";
strUsage += " -printtoconsole " + _("Send trace/debug info to console instead of debug.log file") + "\n"; strUsage += " -printtoconsole " + _("Send trace/debug info to console instead of debug.log file") + "\n";
if (GetBoolArg("-help-debug", false)) if (GetBoolArg("-help-debug", false))
{ {
@ -560,7 +560,7 @@ bool AppInit2(boost::thread_group& threadGroup)
{ {
int64_t n = 0; int64_t n = 0;
if (ParseMoney(mapArgs["-mintxfee"], n) && n > 0) if (ParseMoney(mapArgs["-mintxfee"], n) && n > 0)
CTransaction::nMinTxFee = n; CTransaction::minTxFee = CFeeRate(n);
else else
return InitError(strprintf(_("Invalid amount for -mintxfee=<amount>: '%s'"), mapArgs["-mintxfee"])); return InitError(strprintf(_("Invalid amount for -mintxfee=<amount>: '%s'"), mapArgs["-mintxfee"]));
} }
@ -568,7 +568,7 @@ bool AppInit2(boost::thread_group& threadGroup)
{ {
int64_t n = 0; int64_t n = 0;
if (ParseMoney(mapArgs["-minrelaytxfee"], n) && n > 0) if (ParseMoney(mapArgs["-minrelaytxfee"], n) && n > 0)
CTransaction::nMinRelayTxFee = n; CTransaction::minRelayTxFee = CFeeRate(n);
else else
return InitError(strprintf(_("Invalid amount for -minrelaytxfee=<amount>: '%s'"), mapArgs["-minrelaytxfee"])); return InitError(strprintf(_("Invalid amount for -minrelaytxfee=<amount>: '%s'"), mapArgs["-minrelaytxfee"]));
} }
@ -576,10 +576,12 @@ bool AppInit2(boost::thread_group& threadGroup)
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
if (mapArgs.count("-paytxfee")) if (mapArgs.count("-paytxfee"))
{ {
if (!ParseMoney(mapArgs["-paytxfee"], nTransactionFee)) int64_t nFeePerK = 0;
if (!ParseMoney(mapArgs["-paytxfee"], nFeePerK))
return InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s'"), mapArgs["-paytxfee"])); return InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s'"), mapArgs["-paytxfee"]));
if (nTransactionFee > nHighTransactionFeeWarning) if (nFeePerK > nHighTransactionFeeWarning)
InitWarning(_("Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction.")); InitWarning(_("Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction."));
payTxFee = CFeeRate(nFeePerK, 1000);
} }
bSpendZeroConfChange = GetArg("-spendzeroconfchange", true); bSpendZeroConfChange = GetArg("-spendzeroconfchange", true);

30
src/main.cpp

@ -50,9 +50,9 @@ bool fTxIndex = false;
unsigned int nCoinCacheSize = 5000; unsigned int nCoinCacheSize = 5000;
/** Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) */ /** Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) */
int64_t CTransaction::nMinTxFee = 10000; // Override with -mintxfee CFeeRate CTransaction::minTxFee = CFeeRate(10000); // Override with -mintxfee
/** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */ /** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */
int64_t CTransaction::nMinRelayTxFee = 1000; CFeeRate CTransaction::minRelayTxFee = CFeeRate(1000);
struct COrphanBlock { struct COrphanBlock {
uint256 hashBlock; uint256 hashBlock;
@ -543,7 +543,7 @@ bool IsStandardTx(const CTransaction& tx, string& reason)
} }
if (whichType == TX_NULL_DATA) if (whichType == TX_NULL_DATA)
nDataOut++; nDataOut++;
else if (txout.IsDust(CTransaction::nMinRelayTxFee)) { else if (txout.IsDust(CTransaction::minRelayTxFee)) {
reason = "dust"; reason = "dust";
return false; return false;
} }
@ -783,10 +783,10 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
int64_t GetMinFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree, enum GetMinFee_mode mode) int64_t GetMinFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree, enum GetMinFee_mode mode)
{ {
// Base fee is either nMinTxFee or nMinRelayTxFee // Base fee is either minTxFee or minRelayTxFee
int64_t nBaseFee = (mode == GMF_RELAY) ? tx.nMinRelayTxFee : tx.nMinTxFee; CFeeRate baseFeeRate = (mode == GMF_RELAY) ? tx.minRelayTxFee : tx.minTxFee;
int64_t nMinFee = (1 + (int64_t)nBytes / 1000) * nBaseFee; int64_t nMinFee = baseFeeRate.GetFee(nBytes);
if (fAllowFree) if (fAllowFree)
{ {
@ -800,16 +800,6 @@ int64_t GetMinFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree,
nMinFee = 0; nMinFee = 0;
} }
// This code can be removed after enough miners have upgraded to version 0.9.
// Until then, be safe when sending and require a fee if any output
// is less than CENT:
if (nMinFee < nBaseFee && mode == GMF_SEND)
{
BOOST_FOREACH(const CTxOut& txout, tx.vout)
if (txout.nValue < CENT)
nMinFee = nBaseFee;
}
if (!MoneyRange(nMinFee)) if (!MoneyRange(nMinFee))
nMinFee = MAX_MONEY; nMinFee = MAX_MONEY;
return nMinFee; return nMinFee;
@ -916,10 +906,10 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
hash.ToString(), nFees, txMinFee), hash.ToString(), nFees, txMinFee),
REJECT_INSUFFICIENTFEE, "insufficient fee"); REJECT_INSUFFICIENTFEE, "insufficient fee");
// Continuously rate-limit free transactions // Continuously rate-limit free (really, very-low-fee)transactions
// This mitigates 'penny-flooding' -- sending thousands of free transactions just to // This mitigates 'penny-flooding' -- sending thousands of free transactions just to
// be annoying or make others' transactions take longer to confirm. // be annoying or make others' transactions take longer to confirm.
if (fLimitFree && nFees < CTransaction::nMinRelayTxFee) if (fLimitFree && nFees < CTransaction::minRelayTxFee.GetFee(nSize))
{ {
static CCriticalSection csFreeLimiter; static CCriticalSection csFreeLimiter;
static double dFreeCount; static double dFreeCount;
@ -940,10 +930,10 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
dFreeCount += nSize; dFreeCount += nSize;
} }
if (fRejectInsaneFee && nFees > CTransaction::nMinRelayTxFee * 10000) if (fRejectInsaneFee && nFees > CTransaction::minRelayTxFee.GetFee(nSize) * 10000)
return error("AcceptToMemoryPool: : insane fees %s, %d > %d", return error("AcceptToMemoryPool: : insane fees %s, %d > %d",
hash.ToString(), hash.ToString(),
nFees, CTransaction::nMinRelayTxFee * 10000); nFees, CTransaction::minRelayTxFee.GetFee(nSize) * 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.

42
src/miner.cpp

@ -52,25 +52,30 @@ void SHA256Transform(void* pstate, void* pinput, const void* pinit)
((uint32_t*)pstate)[i] = ctx.h[i]; ((uint32_t*)pstate)[i] = ctx.h[i];
} }
// Some explaining would be appreciated //
// Unconfirmed transactions in the memory pool often depend on other
// transactions in the memory pool. When we select transactions from the
// pool, we select by highest priority or fee rate, so we might consider
// transactions that depend on transactions that aren't yet in the block.
// The COrphan class keeps track of these 'temporary orphans' while
// CreateBlock is figuring out which transactions to include.
//
class COrphan class COrphan
{ {
public: public:
const CTransaction* ptx; const CTransaction* ptx;
set<uint256> setDependsOn; set<uint256> setDependsOn;
double dPriority; double dPriority;
double dFeePerKb; CFeeRate feeRate;
COrphan(const CTransaction* ptxIn) COrphan(const CTransaction* ptxIn) : ptx(ptxIn), feeRate(0), dPriority(0)
{ {
ptx = ptxIn;
dPriority = dFeePerKb = 0;
} }
void print() const void print() const
{ {
LogPrintf("COrphan(hash=%s, dPriority=%.1f, dFeePerKb=%.1f)\n", LogPrintf("COrphan(hash=%s, dPriority=%.1f, fee=%s)\n",
ptx->GetHash().ToString(), dPriority, dFeePerKb); ptx->GetHash().ToString(), dPriority, feeRate.ToString());
BOOST_FOREACH(uint256 hash, setDependsOn) BOOST_FOREACH(uint256 hash, setDependsOn)
LogPrintf(" setDependsOn %s\n", hash.ToString()); LogPrintf(" setDependsOn %s\n", hash.ToString());
} }
@ -80,8 +85,8 @@ public:
uint64_t nLastBlockTx = 0; uint64_t nLastBlockTx = 0;
uint64_t nLastBlockSize = 0; uint64_t nLastBlockSize = 0;
// We want to sort transactions by priority and fee, so: // We want to sort transactions by priority and fee rate, so:
typedef boost::tuple<double, double, const CTransaction*> TxPriority; typedef boost::tuple<double, CFeeRate, const CTransaction*> TxPriority;
class TxPriorityCompare class TxPriorityCompare
{ {
bool byFee; bool byFee;
@ -210,18 +215,15 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
dPriority = tx.ComputePriority(dPriority, nTxSize); dPriority = tx.ComputePriority(dPriority, nTxSize);
// This is a more accurate fee-per-kilobyte than is used by the client code, because the CFeeRate feeRate(nTotalIn-tx.GetValueOut(), nTxSize);
// client code rounds up the size to the nearest 1K. That's good, because it gives an
// incentive to create smaller transactions.
double dFeePerKb = double(nTotalIn-tx.GetValueOut()) / (double(nTxSize)/1000.0);
if (porphan) if (porphan)
{ {
porphan->dPriority = dPriority; porphan->dPriority = dPriority;
porphan->dFeePerKb = dFeePerKb; porphan->feeRate = feeRate;
} }
else else
vecPriority.push_back(TxPriority(dPriority, dFeePerKb, &mi->second.GetTx())); vecPriority.push_back(TxPriority(dPriority, feeRate, &mi->second.GetTx()));
} }
// Collect transactions into block // Collect transactions into block
@ -237,7 +239,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
{ {
// Take highest priority transaction off the priority queue: // Take highest priority transaction off the priority queue:
double dPriority = vecPriority.front().get<0>(); double dPriority = vecPriority.front().get<0>();
double dFeePerKb = vecPriority.front().get<1>(); CFeeRate feeRate = vecPriority.front().get<1>();
const CTransaction& tx = *(vecPriority.front().get<2>()); const CTransaction& tx = *(vecPriority.front().get<2>());
std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer); std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer);
@ -254,7 +256,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
continue; continue;
// Skip free transactions if we're past the minimum block size: // Skip free transactions if we're past the minimum block size:
if (fSortedByFee && (dFeePerKb < CTransaction::nMinRelayTxFee) && (nBlockSize + nTxSize >= nBlockMinSize)) if (fSortedByFee && (feeRate < CTransaction::minRelayTxFee) && (nBlockSize + nTxSize >= nBlockMinSize))
continue; continue;
// Prioritize by fee once past the priority size or we run out of high-priority // Prioritize by fee once past the priority size or we run out of high-priority
@ -298,8 +300,8 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
if (fPrintPriority) if (fPrintPriority)
{ {
LogPrintf("priority %.1f feeperkb %.1f txid %s\n", LogPrintf("priority %.1f fee %s txid %s\n",
dPriority, dFeePerKb, tx.GetHash().ToString()); dPriority, feeRate.ToString(), tx.GetHash().ToString());
} }
// Add transactions that depend on this one to the priority queue // Add transactions that depend on this one to the priority queue
@ -312,7 +314,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
porphan->setDependsOn.erase(hash); porphan->setDependsOn.erase(hash);
if (porphan->setDependsOn.empty()) if (porphan->setDependsOn.empty())
{ {
vecPriority.push_back(TxPriority(porphan->dPriority, porphan->dFeePerKb, porphan->ptx)); vecPriority.push_back(TxPriority(porphan->dPriority, porphan->feeRate, porphan->ptx));
std::push_heap(vecPriority.begin(), vecPriority.end(), comparer); std::push_heap(vecPriority.begin(), vecPriority.end(), comparer);
} }
} }

29
src/qt/coincontroldialog.cpp

@ -453,7 +453,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
CTxOut txout(amount, (CScript)vector<unsigned char>(24, 0)); CTxOut txout(amount, (CScript)vector<unsigned char>(24, 0));
txDummy.vout.push_back(txout); txDummy.vout.push_back(txout);
if (txout.IsDust(CTransaction::nMinRelayTxFee)) if (txout.IsDust(CTransaction::minRelayTxFee))
fDust = true; fDust = true;
} }
} }
@ -525,7 +525,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
sPriorityLabel = CoinControlDialog::getPriorityLabel(dPriority); sPriorityLabel = CoinControlDialog::getPriorityLabel(dPriority);
// Fee // Fee
int64_t nFee = nTransactionFee * (1 + (int64_t)nBytes / 1000); int64_t nFee = payTxFee.GetFee(nBytes);
// Min Fee // Min Fee
int64_t nMinFee = GetMinFee(txDummy, nBytes, AllowFree(dPriority), GMF_SEND); int64_t nMinFee = GetMinFee(txDummy, nBytes, AllowFree(dPriority), GMF_SEND);
@ -536,26 +536,11 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
{ {
nChange = nAmount - nPayFee - nPayAmount; nChange = nAmount - nPayFee - nPayAmount;
// if sub-cent change is required, the fee must be raised to at least CTransaction::nMinTxFee
if (nPayFee < CTransaction::nMinTxFee && nChange > 0 && nChange < CENT)
{
if (nChange < CTransaction::nMinTxFee) // change < 0.0001 => simply move all change to fees
{
nPayFee += nChange;
nChange = 0;
}
else
{
nChange = nChange + nPayFee - CTransaction::nMinTxFee;
nPayFee = CTransaction::nMinTxFee;
}
}
// Never create dust outputs; if we would, just add the dust to the fee. // Never create dust outputs; if we would, just add the dust to the fee.
if (nChange > 0 && nChange < CENT) if (nChange > 0 && nChange < CENT)
{ {
CTxOut txout(nChange, (CScript)vector<unsigned char>(24, 0)); CTxOut txout(nChange, (CScript)vector<unsigned char>(24, 0));
if (txout.IsDust(CTransaction::nMinRelayTxFee)) if (txout.IsDust(CTransaction::minRelayTxFee))
{ {
nPayFee += nChange; nPayFee += nChange;
nChange = 0; nChange = 0;
@ -610,19 +595,19 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
// tool tips // tool tips
QString toolTip1 = tr("This label turns red, if the transaction size is greater than 1000 bytes.") + "<br /><br />"; QString toolTip1 = tr("This label turns red, if the transaction size is greater than 1000 bytes.") + "<br /><br />";
toolTip1 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::nMinTxFee)) + "<br /><br />"; toolTip1 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::minTxFee.GetFeePerK())) + "<br /><br />";
toolTip1 += tr("Can vary +/- 1 byte per input."); toolTip1 += tr("Can vary +/- 1 byte per input.");
QString toolTip2 = tr("Transactions with higher priority are more likely to get included into a block.") + "<br /><br />"; QString toolTip2 = tr("Transactions with higher priority are more likely to get included into a block.") + "<br /><br />";
toolTip2 += tr("This label turns red, if the priority is smaller than \"medium\".") + "<br /><br />"; toolTip2 += tr("This label turns red, if the priority is smaller than \"medium\".") + "<br /><br />";
toolTip2 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::nMinTxFee)); toolTip2 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::minTxFee.GetFeePerK()));
QString toolTip3 = tr("This label turns red, if any recipient receives an amount smaller than %1.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT)) + "<br /><br />"; QString toolTip3 = tr("This label turns red, if any recipient receives an amount smaller than %1.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT)) + "<br /><br />";
toolTip3 += tr("This means a fee of at least %1 is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::nMinTxFee)) + "<br /><br />"; toolTip3 += tr("This means a fee of at least %1 is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::minTxFee.GetFeePerK())) + "<br /><br />";
toolTip3 += tr("Amounts below 0.546 times the minimum relay fee are shown as dust."); toolTip3 += tr("Amounts below 0.546 times the minimum relay fee are shown as dust.");
QString toolTip4 = tr("This label turns red, if the change is smaller than %1.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT)) + "<br /><br />"; QString toolTip4 = tr("This label turns red, if the change is smaller than %1.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT)) + "<br /><br />";
toolTip4 += tr("This means a fee of at least %1 is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::nMinTxFee)); toolTip4 += tr("This means a fee of at least %1 is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::minTxFee.GetFeePerK()));
l5->setToolTip(toolTip1); l5->setToolTip(toolTip1);
l6->setToolTip(toolTip2); l6->setToolTip(toolTip2);

2
src/qt/guiutil.cpp

@ -210,7 +210,7 @@ bool isDust(const QString& address, qint64 amount)
CTxDestination dest = CBitcoinAddress(address.toStdString()).Get(); CTxDestination dest = CBitcoinAddress(address.toStdString()).Get();
CScript script; script.SetDestination(dest); CScript script; script.SetDestination(dest);
CTxOut txOut(amount, script); CTxOut txOut(amount, script);
return txOut.IsDust(CTransaction::nMinRelayTxFee); return txOut.IsDust(CTransaction::minRelayTxFee);
} }
QString HtmlEscape(const QString& str, bool fMultiLine) QString HtmlEscape(const QString& str, bool fMultiLine)

4
src/qt/optionsdialog.cpp

@ -14,7 +14,7 @@
#include "monitoreddatamapper.h" #include "monitoreddatamapper.h"
#include "optionsmodel.h" #include "optionsmodel.h"
#include "main.h" // for CTransaction::nMinTxFee and MAX_SCRIPTCHECK_THREADS #include "main.h" // for CTransaction::minTxFee and MAX_SCRIPTCHECK_THREADS
#include "netbase.h" #include "netbase.h"
#include "txdb.h" // for -dbcache defaults #include "txdb.h" // for -dbcache defaults
@ -101,7 +101,7 @@ OptionsDialog::OptionsDialog(QWidget *parent) :
#endif #endif
ui->unit->setModel(new BitcoinUnits(this)); ui->unit->setModel(new BitcoinUnits(this));
ui->transactionFee->setSingleStep(CTransaction::nMinTxFee); ui->transactionFee->setSingleStep(CTransaction::minTxFee.GetFeePerK());
/* Widget-to-option mapper */ /* Widget-to-option mapper */
mapper = new MonitoredDataMapper(this); mapper = new MonitoredDataMapper(this);

21
src/qt/optionsmodel.cpp

@ -94,7 +94,7 @@ void OptionsModel::Init()
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
if (!settings.contains("nTransactionFee")) if (!settings.contains("nTransactionFee"))
settings.setValue("nTransactionFee", (qint64)DEFAULT_TRANSACTION_FEE); settings.setValue("nTransactionFee", (qint64)DEFAULT_TRANSACTION_FEE);
nTransactionFee = settings.value("nTransactionFee").toLongLong(); // if -paytxfee is set, this will be overridden later in init.cpp payTxFee = CFeeRate(settings.value("nTransactionFee").toLongLong()); // if -paytxfee is set, this will be overridden later in init.cpp
if (mapArgs.count("-paytxfee")) if (mapArgs.count("-paytxfee"))
addOverriddenOption("-paytxfee"); addOverriddenOption("-paytxfee");
@ -187,15 +187,16 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const
return settings.value("nSocksVersion", 5); return settings.value("nSocksVersion", 5);
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
case Fee: case Fee: {
// Attention: Init() is called before nTransactionFee is set in AppInit2()! // Attention: Init() is called before payTxFee is set in AppInit2()!
// To ensure we can change the fee on-the-fly update our QSetting when // To ensure we can change the fee on-the-fly update our QSetting when
// opening OptionsDialog, which queries Fee via the mapper. // opening OptionsDialog, which queries Fee via the mapper.
if (nTransactionFee != settings.value("nTransactionFee").toLongLong()) if (!(payTxFee == CFeeRate(settings.value("nTransactionFee").toLongLong(), 1000)))
settings.setValue("nTransactionFee", (qint64)nTransactionFee); settings.setValue("nTransactionFee", (qint64)payTxFee.GetFeePerK());
// Todo: Consider to revert back to use just nTransactionFee here, if we don't want // Todo: Consider to revert back to use just payTxFee here, if we don't want
// -paytxfee to update our QSettings! // -paytxfee to update our QSettings!
return settings.value("nTransactionFee"); return settings.value("nTransactionFee");
}
case SpendZeroConfChange: case SpendZeroConfChange:
return settings.value("bSpendZeroConfChange"); return settings.value("bSpendZeroConfChange");
#endif #endif
@ -284,12 +285,14 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
} }
break; break;
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
case Fee: // core option - can be changed on-the-fly case Fee: { // core option - can be changed on-the-fly
// Todo: Add is valid check and warn via message, if not // Todo: Add is valid check and warn via message, if not
nTransactionFee = value.toLongLong(); qint64 nTransactionFee = value.toLongLong();
settings.setValue("nTransactionFee", (qint64)nTransactionFee); payTxFee = CFeeRate(nTransactionFee, 1000);
settings.setValue("nTransactionFee", nTransactionFee);
emit transactionFeeChanged(nTransactionFee); emit transactionFeeChanged(nTransactionFee);
break; break;
}
case SpendZeroConfChange: case SpendZeroConfChange:
if (settings.value("bSpendZeroConfChange") != value) { if (settings.value("bSpendZeroConfChange") != value) {
settings.setValue("bSpendZeroConfChange", value); settings.setValue("bSpendZeroConfChange", value);

2
src/qt/paymentserver.cpp

@ -551,7 +551,7 @@ bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, SendCoins
// Extract and check amounts // Extract and check amounts
CTxOut txOut(sendingTo.second, sendingTo.first); CTxOut txOut(sendingTo.second, sendingTo.first);
if (txOut.IsDust(CTransaction::nMinRelayTxFee)) { if (txOut.IsDust(CTransaction::minRelayTxFee)) {
emit message(tr("Payment request error"), tr("Requested payment amount of %1 is too small (considered dust).") emit message(tr("Payment request error"), tr("Requested payment amount of %1 is too small (considered dust).")
.arg(BitcoinUnits::formatWithUnit(optionsModel->getDisplayUnit(), sendingTo.second)), .arg(BitcoinUnits::formatWithUnit(optionsModel->getDisplayUnit(), sendingTo.second)),
CClientUIInterface::MSG_ERROR); CClientUIInterface::MSG_ERROR);

6
src/qt/walletmodel.cpp

@ -231,12 +231,6 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
return AmountExceedsBalance; return AmountExceedsBalance;
} }
if((total + nTransactionFee) > nBalance)
{
transaction.setTransactionFee(nTransactionFee);
return SendCoinsReturn(AmountWithFeeExceedsBalance);
}
{ {
LOCK2(cs_main, wallet->cs_wallet); LOCK2(cs_main, wallet->cs_wallet);

4
src/rpcmisc.cpp

@ -81,9 +81,9 @@ Value getinfo(const Array& params, bool fHelp)
} }
if (pwalletMain && pwalletMain->IsCrypted()) if (pwalletMain && pwalletMain->IsCrypted())
obj.push_back(Pair("unlocked_until", nWalletUnlockTime)); obj.push_back(Pair("unlocked_until", nWalletUnlockTime));
obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee))); obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK())));
#endif #endif
obj.push_back(Pair("relayfee", ValueFromAmount(CTransaction::nMinRelayTxFee))); obj.push_back(Pair("relayfee", ValueFromAmount(CTransaction::minRelayTxFee.GetFeePerK())));
obj.push_back(Pair("errors", GetWarnings("statusbar"))); obj.push_back(Pair("errors", GetWarnings("statusbar")));
return obj; return obj;
} }

2
src/rpcnet.cpp

@ -368,7 +368,7 @@ Value getnetworkinfo(const Array& params, bool fHelp)
obj.push_back(Pair("timeoffset", GetTimeOffset())); obj.push_back(Pair("timeoffset", GetTimeOffset()));
obj.push_back(Pair("connections", (int)vNodes.size())); obj.push_back(Pair("connections", (int)vNodes.size()));
obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string()))); obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string())));
obj.push_back(Pair("relayfee", ValueFromAmount(CTransaction::nMinRelayTxFee))); obj.push_back(Pair("relayfee", ValueFromAmount(CTransaction::minRelayTxFee.GetFeePerK())));
Array localAddresses; Array localAddresses;
{ {
LOCK(cs_mapLocalHost); LOCK(cs_mapLocalHost);

2
src/rpcwallet.cpp

@ -1883,7 +1883,7 @@ Value settxfee(const Array& params, bool fHelp)
if (params[0].get_real() != 0.0) if (params[0].get_real() != 0.0)
nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts
nTransactionFee = nAmount; payTxFee = CFeeRate(nAmount, 1000);
return true; return true;
} }

22
src/wallet.cpp

@ -16,7 +16,7 @@
using namespace std; using namespace std;
// Settings // Settings
int64_t nTransactionFee = DEFAULT_TRANSACTION_FEE; CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE);
bool bSpendZeroConfChange = true; bool bSpendZeroConfChange = true;
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
@ -1233,7 +1233,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend,
{ {
LOCK2(cs_main, cs_wallet); LOCK2(cs_main, cs_wallet);
{ {
nFeeRet = nTransactionFee; nFeeRet = payTxFee.GetFeePerK();
while (true) while (true)
{ {
wtxNew.vin.clear(); wtxNew.vin.clear();
@ -1246,7 +1246,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend,
BOOST_FOREACH (const PAIRTYPE(CScript, int64_t)& s, vecSend) BOOST_FOREACH (const PAIRTYPE(CScript, int64_t)& s, vecSend)
{ {
CTxOut txout(s.second, s.first); CTxOut txout(s.second, s.first);
if (txout.IsDust(CTransaction::nMinRelayTxFee)) if (txout.IsDust(CTransaction::minRelayTxFee))
{ {
strFailReason = _("Transaction amount too small"); strFailReason = _("Transaction amount too small");
return false; return false;
@ -1272,16 +1272,6 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend,
} }
int64_t nChange = nValueIn - nValue - nFeeRet; int64_t nChange = nValueIn - nValue - nFeeRet;
// The following if statement should be removed once enough miners
// have upgraded to the 0.9 GetMinFee() rules. Until then, this avoids
// creating free transactions that have change outputs less than
// CENT bitcoins.
if (nFeeRet < CTransaction::nMinTxFee && nChange > 0 && nChange < CENT)
{
int64_t nMoveToFee = min(nChange, CTransaction::nMinTxFee - nFeeRet);
nChange -= nMoveToFee;
nFeeRet += nMoveToFee;
}
if (nChange > 0) if (nChange > 0)
{ {
@ -1317,7 +1307,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend,
// Never create dust outputs; if we would, just // Never create dust outputs; if we would, just
// add the dust to the fee. // add the dust to the fee.
if (newTxOut.IsDust(CTransaction::nMinRelayTxFee)) if (newTxOut.IsDust(CTransaction::minRelayTxFee))
{ {
nFeeRet += nChange; nFeeRet += nChange;
reservekey.ReturnKey(); reservekey.ReturnKey();
@ -1355,7 +1345,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend,
dPriority = wtxNew.ComputePriority(dPriority, nBytes); dPriority = wtxNew.ComputePriority(dPriority, nBytes);
// Check that enough fee is included // Check that enough fee is included
int64_t nPayFee = nTransactionFee * (1 + (int64_t)nBytes / 1000); int64_t nPayFee = payTxFee.GetFee(nBytes);
bool fAllowFree = AllowFree(dPriority); bool fAllowFree = AllowFree(dPriority);
int64_t nMinFee = GetMinFee(wtxNew, nBytes, fAllowFree, GMF_SEND); int64_t nMinFee = GetMinFee(wtxNew, nBytes, fAllowFree, GMF_SEND);
if (nFeeRet < max(nPayFee, nMinFee)) if (nFeeRet < max(nPayFee, nMinFee))
@ -1464,7 +1454,7 @@ string CWallet::SendMoneyToDestination(const CTxDestination& address, int64_t nV
// Check amount // Check amount
if (nValue <= 0) if (nValue <= 0)
return _("Invalid amount"); return _("Invalid amount");
if (nValue + nTransactionFee > GetBalance()) if (nValue > GetBalance())
return _("Insufficient funds"); return _("Insufficient funds");
// Parse Bitcoin address // Parse Bitcoin address

2
src/wallet.h

@ -24,7 +24,7 @@
#include <vector> #include <vector>
// Settings // Settings
extern int64_t nTransactionFee; extern CFeeRate payTxFee;
extern bool bSpendZeroConfChange; extern bool bSpendZeroConfChange;
// -paytxfee default // -paytxfee default

Loading…
Cancel
Save