src/coincontrol.h \
src/sync.h \
src/util.h \
+ src/timestamps.h \
src/uint256.h \
src/kernel.h \
src/scrypt.h \
if (txout.IsEmpty() && !IsCoinBase() && !IsCoinStake())
return DoS(100, error("CTransaction::CheckTransaction() : txout empty for user transaction"));
- // NovaCoin: enforce minimum output amount for user transactions until 1 May 2014 04:00:00 GMT
- if (!fTestNet && !IsCoinBase() && !txout.IsEmpty() && nTime < OUTPUT_SWITCH_TIME && txout.nValue < MIN_TXOUT_AMOUNT)
- return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue below minimum"));
-
if (txout.nValue < 0)
return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue is negative"));
if (txout.nValue > MAX_MONEY)
return true;
}
-int64 CTransaction::GetMinFee(unsigned int nBlockSize, bool fAllowFree,
- enum GetMinFee_mode mode, unsigned int nBytes) const
+int64 CTransaction::GetMinFee(unsigned int nBlockSize, bool fAllowFree, enum GetMinFee_mode mode, unsigned int nBytes) const
{
- // Base fee is either MIN_TX_FEE or MIN_RELAY_TX_FEE
- int64 nBaseFee = (mode == GMF_RELAY) ? MIN_RELAY_TX_FEE : MIN_TX_FEE;
+ // Use new fees approach if we are on test network or
+ // switch date has been reached
+ bool fNewApproach = fTestNet || nTime > FEE_SWITCH_TIME;
+
+ int64 nMinTxFee = MIN_TX_FEE, nMinRelayTxFee = MIN_RELAY_TX_FEE;
+
+ if(!fNewApproach || IsCoinStake())
+ {
+ // Enforce 0.01 as minimum fee for old approach or coinstake
+ nMinTxFee = CENT;
+ nMinRelayTxFee = CENT;
+ }
+
+ // Base fee is either nMinTxFee or nMinRelayTxFee
+ int64 nBaseFee = (mode == GMF_RELAY) ? nMinRelayTxFee : nMinTxFee;
unsigned int nNewBlockSize = nBlockSize + nBytes;
int64 nMinFee = (1 + (int64)nBytes / 1000) * nBaseFee;
- // To limit dust spam, require MIN_TX_FEE/MIN_RELAY_TX_FEE if any output is less than 0.01
- if (nMinFee < nBaseFee)
+ if (fNewApproach)
{
+ if (fAllowFree)
+ {
+ if (nBlockSize == 1)
+ {
+ // Transactions under 1K are free
+ if (nBytes < 1000)
+ nMinFee = 0;
+ }
+ else
+ {
+ // Free transaction area
+ if (nNewBlockSize < 27000)
+ nMinFee = 0;
+ }
+ }
+
+ // To limit dust spam, require additional MIN_TX_FEE/MIN_RELAY_TX_FEE for
+ // each non empty output which is less than 0.01
+ //
+ // It's safe to ignore empty outputs here, because these inputs are allowed
+ // only for coinbase and coinstake transactions.
+ BOOST_FOREACH(const CTxOut& txout, vout)
+ if (txout.nValue < CENT && !txout.IsEmpty())
+ nMinFee += nBaseFee;
+ }
+ else if (nMinFee < nBaseFee)
+ {
+ // To limit dust spam, require MIN_TX_FEE/MIN_RELAY_TX_FEE if
+ // any output is less than 0.01
BOOST_FOREACH(const CTxOut& txout, vout)
if (txout.nValue < CENT)
nMinFee = nBaseFee;
if (!MoneyRange(nMinFee))
nMinFee = MAX_MONEY;
+
return nMinFee;
}
unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
// Don't accept it if it can't get into a block
- int64 txMinFee = tx.GetMinFee(1000, false, GMF_RELAY, nSize);
+ int64 txMinFee = tx.GetMinFee(1000, true, GMF_RELAY, nSize);
if (nFees < txMinFee)
return error("CTxMemPool::accept() : not enough fees %s, %"PRI64d" < %"PRI64d,
hash.ToString().c_str(),
if (!GetCoinAge(txdb, nCoinAge))
return error("ConnectInputs() : %s unable to get coin age for coinstake", GetHash().ToString().substr(0,10).c_str());
- int64 nStakeReward = GetValueOut() - nValueIn;
- int64 nCalculatedStakeReward = GetProofOfStakeReward(nCoinAge, pindexBlock->nBits, nTime) - GetMinFee() + MIN_TX_FEE;
+ unsigned int nTxSize = (nTime > VALIDATION_SWITCH_TIME || fTestNet) ? GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION) : 0;
- if (nStakeReward > nCalculatedStakeReward)
- return DoS(100, error("ConnectInputs() : coinstake pays too much(actual=%"PRI64d" vs calculated=%"PRI64d")", nStakeReward, nCalculatedStakeReward));
+ int64 nReward = GetValueOut() - nValueIn;
+ int64 nCalculatedReward = GetProofOfStakeReward(nCoinAge, pindexBlock->nBits, nTime) - GetMinFee(1, false, GMF_BLOCK, nTxSize) + CENT;
+
+ if (nReward > nCalculatedReward)
+ return DoS(100, error("CheckInputs() : coinstake pays too much(actual=%"PRI64d" vs calculated=%"PRI64d")", nReward, nCalculatedReward));
}
else
{
if (nTxFee < 0)
return DoS(100, error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str()));
- // enforce transaction fees for every block until 1 May 2014 04:00:00 GMT
- if (!fTestNet && nTxFee < GetMinFee() && nTime < OUTPUT_SWITCH_TIME)
- return fBlock? DoS(100, error("ConnectInputs() : %s not paying required fee=%s, paid=%s", GetHash().ToString().substr(0,10).c_str(), FormatMoney(GetMinFee()).c_str(), FormatMoney(nTxFee).c_str())) : false;
-
nFees += nTxFee;
if (!MoneyRange(nFees))
return DoS(100, error("ConnectInputs() : nFees out of range"));
#ifndef BITCOIN_MAIN_H
#define BITCOIN_MAIN_H
+#include "timestamps.h"
#include "bignum.h"
#include "sync.h"
#include "net.h"
static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50;
static const unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100;
static const unsigned int MAX_INV_SZ = 50000;
-static const int64 MIN_TX_FEE = CENT;
-static const int64 MIN_RELAY_TX_FEE = CENT;
+
+static const int64 MIN_TX_FEE = CENT/10;
+static const int64 MIN_RELAY_TX_FEE = CENT/50;
+
static const int64 MAX_MONEY = 2000000000 * COIN;
static const int64 MAX_MINT_PROOF_OF_WORK = 100 * COIN;
static const int64 MAX_MINT_PROOF_OF_STAKE = 1 * COIN;
-static const int64 MIN_TXOUT_AMOUNT = MIN_TX_FEE;
-
-static const unsigned int ENTROPY_SWITCH_TIME = 1362791041; // Sat, 09 Mar 2013 01:04:01 GMT
-static const unsigned int STAKE_SWITCH_TIME = 1371686400; // Thu, 20 Jun 2013 00:00:00 GMT
-static const unsigned int TARGETS_SWITCH_TIME = 1374278400; // Sat, 20 Jul 2013 00:00:00 GMT
-static const unsigned int CHAINCHECKS_SWITCH_TIME = 1379635200; // Fri, 20 Sep 2013 00:00:00 GMT
-static const unsigned int STAKECURVE_SWITCH_TIME = 1382227200; // Sun, 20 Oct 2013 00:00:00 GMT
-static const unsigned int OUTPUT_SWITCH_TIME = 1398916800; // Thu, 01 May 2014 04:00:00 GMT
-
+static const int64 MIN_TXOUT_AMOUNT = CENT/100;
inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); }
// Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp.
continue;
// Simplify transaction fee - allow free = false
- int64 nMinFee = tx.GetMinFee(nBlockSize, false, GMF_BLOCK);
+ int64 nMinFee = tx.GetMinFee(nBlockSize, true, GMF_BLOCK, nTxSize);
// Skip free transactions if we're past the minimum block size:
if (fSortedByFee && (dFeePerKb < nMinTxFee) && (nBlockSize + nTxSize >= nBlockMinSize))
double dPriority = 0;
double dPriorityInputs = 0;
unsigned int nQuantity = 0;
-
+
vector<COutPoint> vCoinControl;
vector<COutput> vOutputs;
coinControl->ListSelected(vCoinControl);
{
// Quantity
nQuantity++;
-
+
// Amount
nAmount += out.tx->vout[out.i].nValue;
-
+
// Priority
dPriorityInputs += (double)out.tx->vout[out.i].nValue * (out.nDepth+1);
-
+
// Bytes
CTxDestination address;
if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, address))
}
else nBytesInputs += 148;
}
-
+
// calculation
if (nQuantity > 0)
{
// Bytes
nBytes = nBytesInputs + ((CoinControlDialog::payAmounts.size() > 0 ? CoinControlDialog::payAmounts.size() + 1 : 2) * 34) + 10; // always assume +1 output for change here
-
+
// Priority
dPriority = dPriorityInputs / nBytes;
sPriorityLabel = CoinControlDialog::getPriorityLabel(dPriority);
-
+
// Fee
int64 nFee = nTransactionFee * (1 + (int64)nBytes / 1000);
-
+
// Min Fee
- int64 nMinFee = txDummy.GetMinFee(1, false, GMF_SEND, nBytes);
-
+ bool fAllowFree = CTransaction::AllowFree(dPriority);
+
+ // Disable free transactions until 1 July 2014
+ if (!fTestNet && txDummy.nTime < FEE_SWITCH_TIME)
+ {
+ fAllowFree = false;
+ }
+
+ int64 nMinFee = txDummy.GetMinFee(1, fAllowFree, GMF_SEND, nBytes);
+
nPayFee = max(nFee, nMinFee);
-
+
if (nPayAmount > 0)
{
nChange = nAmount - nPayFee - nPayAmount;
-
+
// if sub-cent change is required, the fee must be raised to at least CTransaction::nMinTxFee
- if (nPayFee < CENT && nChange > 0 && nChange < CENT)
+ if (txDummy.nTime < FEE_SWITCH_TIME && !fTestNet)
{
- if (nChange < CENT) // change < 0.01 => simply move all change to fees
+ if (nPayFee < CENT && nChange > 0 && nChange < CENT)
{
- nPayFee = nChange;
- nChange = 0;
+ if (nChange < CENT) // change < 0.01 => simply move all change to fees
+ {
+ nPayFee = nChange;
+ nChange = 0;
+ }
+ else
+ {
+ nChange = nChange + nPayFee - CENT;
+ nPayFee = CENT;
+ }
}
- else
- {
- nChange = nChange + nPayFee - CENT;
- nPayFee = CENT;
- }
}
-
+
if (nChange == 0)
nBytes -= 34;
}
-
+
// after fee
nAfterFee = nAmount - nPayFee;
if (nAfterFee < 0)
nAfterFee = 0;
}
-
+
// actually update labels
int nDisplayUnit = BitcoinUnits::BTC;
if (model && model->getOptionsModel())
nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
-
+
QLabel *l1 = dialog->findChild<QLabel *>("labelCoinControlQuantity");
QLabel *l2 = dialog->findChild<QLabel *>("labelCoinControlAmount");
QLabel *l3 = dialog->findChild<QLabel *>("labelCoinControlFee");
QLabel *l6 = dialog->findChild<QLabel *>("labelCoinControlPriority");
QLabel *l7 = dialog->findChild<QLabel *>("labelCoinControlLowOutput");
QLabel *l8 = dialog->findChild<QLabel *>("labelCoinControlChange");
-
+
// enable/disable "low output" and "change"
dialog->findChild<QLabel *>("labelCoinControlLowOutputText")->setEnabled(nPayAmount > 0);
dialog->findChild<QLabel *>("labelCoinControlLowOutput") ->setEnabled(nPayAmount > 0);
dialog->findChild<QLabel *>("labelCoinControlChangeText") ->setEnabled(nPayAmount > 0);
dialog->findChild<QLabel *>("labelCoinControlChange") ->setEnabled(nPayAmount > 0);
-
+
// stats
l1->setText(QString::number(nQuantity)); // Quantity
l2->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAmount)); // Amount
l6->setText(sPriorityLabel); // Priority
l7->setText((fLowOutput ? (fDust ? tr("DUST") : tr("yes")) : tr("no"))); // Low Output / Dust
l8->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nChange)); // Change
-
+
// turn labels "red"
- l5->setStyleSheet((nBytes >= 10000) ? "color:red;" : ""); // Bytes >= 10000
+ l5->setStyleSheet((nBytes >= 5000) ? "color:red;" : ""); // Bytes >= 5000
l6->setStyleSheet((dPriority <= 576000) ? "color:red;" : ""); // Priority < "medium"
l7->setStyleSheet((fLowOutput) ? "color:red;" : ""); // Low Output = "yes"
l8->setStyleSheet((nChange > 0 && nChange < CENT) ? "color:red;" : ""); // Change < 0.01BTC
-
+
// tool tips
- l5->setToolTip(tr("This label turns red, if the transaction size is bigger than 10000 bytes.\n\n This means a fee of at least %1 per kb is required.\n\n Can vary +/- 1 Byte per input.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT)));
+ l5->setToolTip(tr("This label turns red, if the transaction size is bigger than 5000 bytes.\n\n This means a fee of at least %1 per kb is required.\n\n Can vary +/- 1 Byte per input.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT)));
l6->setToolTip(tr("Transactions with higher priority get more likely into a block.\n\nThis label turns red, if the priority is smaller than \"medium\".\n\n This means a fee of at least %1 per kb is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT)));
l7->setToolTip(tr("This label turns red, if any recipient receives an amount smaller than %1.\n\n This means a fee of at least %2 is required. \n\n Amounts below 0.546 times the minimum relay fee are shown as DUST.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT)).arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT)));
l8->setToolTip(tr("This label turns red, if the change is smaller than %1.\n\n This means a fee of at least %2 is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT)).arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT)));
dialog->findChild<QLabel *>("labelCoinControlPriorityText") ->setToolTip(l6->toolTip());
dialog->findChild<QLabel *>("labelCoinControlLowOutputText")->setToolTip(l7->toolTip());
dialog->findChild<QLabel *>("labelCoinControlChangeText") ->setToolTip(l8->toolTip());
-
+
// Insufficient funds
QLabel *label = dialog->findChild<QLabel *>("labelCoinControlInsuffFunds");
if (label)
--- /dev/null
+#ifndef BITCOIN_TIMESTAMPS_H
+#define BITCOIN_TIMESTAMPS_H
+
+static const unsigned int ENTROPY_SWITCH_TIME = 1362791041; // Sat, 09 Mar 2013 01:04:01 GMT
+static const unsigned int STAKE_SWITCH_TIME = 1371686400; // Thu, 20 Jun 2013 00:00:00 GMT
+static const unsigned int TARGETS_SWITCH_TIME = 1374278400; // Sat, 20 Jul 2013 00:00:00 GMT
+static const unsigned int CHAINCHECKS_SWITCH_TIME = 1379635200; // Fri, 20 Sep 2013 00:00:00 GMT
+static const unsigned int STAKECURVE_SWITCH_TIME = 1382227200; // Sun, 20 Oct 2013 00:00:00 GMT
+static const unsigned int FEE_SWITCH_TIME = 1405814400; // Sun, 20 Jul 2014 00:00:00 GMT
+
+static const unsigned int VALIDATION_SWITCH_TIME = 1408492800; // Wed, 20 Aug 2014 00:00:00 GMT
+
+#endif
// if sub-cent change is required, the fee must be raised to at least MIN_TX_FEE
// or until nChange becomes zero
// NOTE: this depends on the exact behaviour of GetMinFee
- if (nFeeRet < MIN_TX_FEE && nChange > 0 && nChange < CENT)
+ if (wtxNew.nTime < FEE_SWITCH_TIME && !fTestNet)
{
- int64 nMoveToFee = min(nChange, MIN_TX_FEE - nFeeRet);
- nChange -= nMoveToFee;
- nFeeRet += nMoveToFee;
- }
+ if (nFeeRet < MIN_TX_FEE && nChange > 0 && nChange < CENT)
+ {
+ int64 nMoveToFee = min(nChange, MIN_TX_FEE - nFeeRet);
+ nChange -= nMoveToFee;
+ nFeeRet += nMoveToFee;
+ }
- // ppcoin: sub-cent change is moved to fee
- if (nChange > 0 && nChange < MIN_TXOUT_AMOUNT)
- {
- nFeeRet += nChange;
- nChange = 0;
+ // sub-cent change is moved to fee
+ if (nChange > 0 && nChange < CENT)
+ {
+ nFeeRet += nChange;
+ nChange = 0;
+ }
}
if (nChange > 0)
// Check that enough fee is included
int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000);
- int64 nMinFee = wtxNew.GetMinFee(1, false, GMF_SEND, nBytes);
+ bool fAllowFree = CTransaction::AllowFree(dPriority);
+
+ // Disable free transactions until 1 July 2014
+ if (!fTestNet && wtxNew.nTime < FEE_SWITCH_TIME)
+ {
+ fAllowFree = false;
+ }
+
+ int64 nMinFee = wtxNew.GetMinFee(1, fAllowFree, GMF_SEND, nBytes);
if (nFeeRet < max(nPayFee, nMinFee))
{
return error("CreateCoinStake : exceeded coinstake size limit");
// Check enough fee is paid
- if (nMinFee < txNew.GetMinFee() - MIN_TX_FEE)
+ if (nMinFee < txNew.GetMinFee(1, false, GMF_BLOCK, nBytes) - CENT)
{
- nMinFee = txNew.GetMinFee() - MIN_TX_FEE;
+ nMinFee = txNew.GetMinFee(1, false, GMF_BLOCK, nBytes) - CENT;
continue; // try signing again
}
else