174 changes: 144 additions & 30 deletions qa/rpc-tests/util.py
Expand Up @@ -12,6 +12,7 @@

from decimal import Decimal
import json
import random
import shutil
import subprocess
import time
Expand All @@ -20,8 +21,10 @@
from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
from util import *

START_P2P_PORT=11000
START_RPC_PORT=11100
def p2p_port(n):
return 11000 + n + os.getpid()%999
def rpc_port(n):
return 12000 + n + os.getpid()%999

def check_json_precision():
"""Make sure json library being used does not lose precision converting BTC values"""
Expand Down Expand Up @@ -58,6 +61,18 @@ def sync_mempools(rpc_connections):

bitcoind_processes = []

def initialize_datadir(dir, n):
datadir = os.path.join(dir, "node"+str(n))
if not os.path.isdir(datadir):
os.makedirs(datadir)
with open(os.path.join(datadir, "bitcoin.conf"), 'w') as f:
f.write("regtest=1\n");
f.write("rpcuser=rt\n");
f.write("rpcpassword=rt\n");
f.write("port="+str(p2p_port(n))+"\n");
f.write("rpcport="+str(rpc_port(n))+"\n");
return datadir

def initialize_chain(test_dir):
"""
Create (or copy from cache) a 200-block-long chain and
Expand All @@ -69,25 +84,18 @@ def initialize_chain(test_dir):
devnull = open("/dev/null", "w+")
# Create cache directories, run bitcoinds:
for i in range(4):
datadir = os.path.join("cache", "node"+str(i))
os.makedirs(datadir)
with open(os.path.join(datadir, "bitcoin.conf"), 'w') as f:
f.write("regtest=1\n");
f.write("rpcuser=rt\n");
f.write("rpcpassword=rt\n");
f.write("port="+str(START_P2P_PORT+i)+"\n");
f.write("rpcport="+str(START_RPC_PORT+i)+"\n");
datadir=initialize_datadir("cache", i)
args = [ "bitcoind", "-keypool=1", "-datadir="+datadir ]
if i > 0:
args.append("-connect=127.0.0.1:"+str(START_P2P_PORT))
args.append("-connect=127.0.0.1:"+str(p2p_port(0)))
bitcoind_processes.append(subprocess.Popen(args))
subprocess.check_call([ "bitcoin-cli", "-datadir="+datadir,
"-rpcwait", "getblockcount"], stdout=devnull)
devnull.close()
rpcs = []
for i in range(4):
try:
url = "http://rt:rt@127.0.0.1:%d"%(START_RPC_PORT+i,)
url = "http://rt:rt@127.0.0.1:%d"%(rpc_port(i),)
rpcs.append(AuthServiceProxy(url))
except:
sys.stderr.write("Error connecting to "+url+"\n")
Expand All @@ -112,6 +120,7 @@ def initialize_chain(test_dir):
from_dir = os.path.join("cache", "node"+str(i))
to_dir = os.path.join(test_dir, "node"+str(i))
shutil.copytree(from_dir, to_dir)
initialize_datadir(test_dir, i) # Overwrite port/rpcport in bitcoin.conf

def _rpchost_to_args(rpchost):
'''Convert optional IP:port spec to rpcconnect/rpcport args'''
Expand All @@ -133,25 +142,28 @@ def _rpchost_to_args(rpchost):
rv += ['-rpcport=' + rpcport]
return rv

def start_nodes(num_nodes, dir, extra_args=None, rpchost=None):
# Start bitcoinds, and wait for RPC interface to be up and running:
def start_node(i, dir, extra_args=None, rpchost=None):
"""
Start a bitcoind and return RPC connection to it
"""
datadir = os.path.join(dir, "node"+str(i))
args = [ "bitcoind", "-datadir="+datadir, "-keypool=1" ]
if extra_args is not None: args.extend(extra_args)
bitcoind_processes.append(subprocess.Popen(args))
devnull = open("/dev/null", "w+")
for i in range(num_nodes):
datadir = os.path.join(dir, "node"+str(i))
args = [ "bitcoind", "-datadir="+datadir ]
if extra_args is not None:
args += extra_args[i]
bitcoind_processes.append(subprocess.Popen(args))
subprocess.check_call([ "bitcoin-cli", "-datadir="+datadir] +
_rpchost_to_args(rpchost) +
["-rpcwait", "getblockcount"], stdout=devnull)
subprocess.check_call([ "bitcoin-cli", "-datadir="+datadir] +
_rpchost_to_args(rpchost) +
["-rpcwait", "getblockcount"], stdout=devnull)
devnull.close()
# Create&return JSON-RPC connections
rpc_connections = []
for i in range(num_nodes):
url = "http://rt:rt@%s:%d" % (rpchost or '127.0.0.1', START_RPC_PORT+i,)
rpc_connections.append(AuthServiceProxy(url))
return rpc_connections
url = "http://rt:rt@%s:%d" % (rpchost or '127.0.0.1', rpc_port(i))
return AuthServiceProxy(url)

def start_nodes(num_nodes, dir, extra_args=None, rpchost=None):
"""
Start multiple bitcoinds, return RPC connections to them
"""
if extra_args is None: extra_args = [ None for i in range(num_nodes) ]
return [ start_node(i, dir, extra_args[i], rpchost) for i in range(num_nodes) ]

def debug_log(dir, n_node):
return os.path.join(dir, "node"+str(n_node), "regtest", "debug.log")
Expand All @@ -168,9 +180,111 @@ def wait_bitcoinds():
del bitcoind_processes[:]

def connect_nodes(from_connection, node_num):
ip_port = "127.0.0.1:"+str(START_P2P_PORT+node_num)
ip_port = "127.0.0.1:"+str(p2p_port(node_num))
from_connection.addnode(ip_port, "onetry")

def find_output(node, txid, amount):
"""
Return index to output of txid with value amount
Raises exception if there is none.
"""
txdata = node.getrawtransaction(txid, 1)
for i in range(len(txdata["vout"])):
if txdata["vout"][i]["value"] == amount:
return i
raise RuntimeError("find_output txid %s : %s not found"%(txid,str(amount)))

def gather_inputs(from_node, amount_needed):
"""
Return a random set of unspent txouts that are enough to pay amount_needed
"""
utxo = from_node.listunspent(1)
random.shuffle(utxo)
inputs = []
total_in = Decimal("0.00000000")
while total_in < amount_needed and len(utxo) > 0:
t = utxo.pop()
total_in += t["amount"]
inputs.append({ "txid" : t["txid"], "vout" : t["vout"], "address" : t["address"] } )
if total_in < amount_needed:
raise RuntimeError("Insufficient funds: need %d, have %d"%(amount+fee*2, total_in))
return (total_in, inputs)

def make_change(from_node, amount_in, amount_out, fee):
"""
Create change output(s), return them
"""
outputs = {}
amount = amount_out+fee
change = amount_in - amount
if change > amount*2:
# Create an extra change output to break up big inputs
outputs[from_node.getnewaddress()] = float(change/2)
change = change/2
if change > 0:
outputs[from_node.getnewaddress()] = float(change)
return outputs

def send_zeropri_transaction(from_node, to_node, amount, fee):
"""
Create&broadcast a zero-priority transaction.
Returns (txid, hex-encoded-txdata)
Ensures transaction is zero-priority by first creating a send-to-self,
then using it's output
"""

# Create a send-to-self with confirmed inputs:
self_address = from_node.getnewaddress()
(total_in, inputs) = gather_inputs(from_node, amount+fee*2)
outputs = make_change(from_node, total_in, amount+fee, fee)
outputs[self_address] = float(amount+fee)

self_rawtx = from_node.createrawtransaction(inputs, outputs)
self_signresult = from_node.signrawtransaction(self_rawtx)
self_txid = from_node.sendrawtransaction(self_signresult["hex"], True)

vout = find_output(from_node, self_txid, amount+fee)
# Now immediately spend the output to create a 1-input, 1-output
# zero-priority transaction:
inputs = [ { "txid" : self_txid, "vout" : vout } ]
outputs = { to_node.getnewaddress() : float(amount) }

rawtx = from_node.createrawtransaction(inputs, outputs)
signresult = from_node.signrawtransaction(rawtx)
txid = from_node.sendrawtransaction(signresult["hex"], True)

return (txid, signresult["hex"])

def random_zeropri_transaction(nodes, amount, min_fee, fee_increment, fee_variants):
"""
Create a random zero-priority transaction.
Returns (txid, hex-encoded-transaction-data, fee)
"""
from_node = random.choice(nodes)
to_node = random.choice(nodes)
fee = min_fee + fee_increment*random.randint(0,fee_variants)
(txid, txhex) = send_zeropri_transaction(from_node, to_node, amount, fee)
return (txid, txhex, fee)

def random_transaction(nodes, amount, min_fee, fee_increment, fee_variants):
"""
Create a random transaction.
Returns (txid, hex-encoded-transaction-data, fee)
"""
from_node = random.choice(nodes)
to_node = random.choice(nodes)
fee = min_fee + fee_increment*random.randint(0,fee_variants)

(total_in, inputs) = gather_inputs(from_node, amount+fee)
outputs = make_change(from_node, total_in, amount, fee)
outputs[to_node.getnewaddress()] = float(amount)

rawtx = from_node.createrawtransaction(inputs, outputs)
signresult = from_node.signrawtransaction(rawtx)
txid = from_node.sendrawtransaction(signresult["hex"], True)

return (txid, signresult["hex"], fee)

def assert_equal(thing1, thing2):
if thing1 != thing2:
raise AssertionError("%s != %s"%(str(thing1),str(thing2)))
19 changes: 19 additions & 0 deletions src/core.cpp
Expand Up @@ -72,6 +72,25 @@ void CTxOut::print() const
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
{
return SerializeHash(*this);
Expand Down
40 changes: 33 additions & 7 deletions src/core.h
Expand Up @@ -112,6 +112,31 @@ class CTxIn



/** 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:
CFeeRate() : nSatoshisPerK(0) { }
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;

IMPLEMENT_SERIALIZE( READWRITE(nSatoshisPerK); )
};


/** An output of a transaction. It contains the public key that the next input
* must be able to sign with to claim it.
Expand Down Expand Up @@ -148,17 +173,18 @@ class CTxOut

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.
// If you'd pay more than 1/3 in fees
// to spend something, then we consider it dust.
// 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
// with default nMinRelayTxFee.
return ((nValue*1000)/(3*((int)GetSerializeSize(SER_DISK,0)+148)) < nMinRelayTxFee);
// with default minRelayTxFee.
size_t nSize = GetSerializeSize(SER_DISK,0)+148u;
return (nValue < 3*minRelayTxFee.GetFee(nSize));
}

friend bool operator==(const CTxOut& a, const CTxOut& b)
Expand All @@ -183,8 +209,8 @@ class CTxOut
class CTransaction
{
public:
static int64_t nMinTxFee;
static int64_t nMinRelayTxFee;
static CFeeRate minTxFee;
static CFeeRate minRelayTxFee;
static const int CURRENT_VERSION=1;
int nVersion;
std::vector<CTxIn> vin;
Expand Down
28 changes: 22 additions & 6 deletions src/init.cpp
Expand Up @@ -59,6 +59,7 @@ enum BindFlags {
BF_REPORT_ERROR = (1U << 1)
};

static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat";

//////////////////////////////////////////////////////////////////////////////
//
Expand Down Expand Up @@ -121,6 +122,14 @@ void Shutdown()
#endif
StopNode();
UnregisterNodeSignals(GetNodeSignals());

boost::filesystem::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
CAutoFile est_fileout = CAutoFile(fopen(est_path.string().c_str(), "wb"), SER_DISK, CLIENT_VERSION);
if (est_fileout)
mempool.WriteFeeEstimates(est_fileout);
else
LogPrintf("failed to write fee estimates");

{
LOCK(cs_main);
#ifdef ENABLE_WALLET
Expand Down Expand Up @@ -281,8 +290,8 @@ std::string HelpMessage(HelpMessageMode hmm)
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 += " -mintxfee=<amt> " + _("Fees smaller than this are considered zero fee (for transaction creation) (default:") + " " + FormatMoney(CTransaction::nMinTxFee) + ")" + "\n";
strUsage += " -minrelaytxfee=<amt> " + _("Fees smaller than this are considered zero fee (for relaying) (default:") + " " + FormatMoney(CTransaction::nMinRelayTxFee) + ")" + "\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::minRelayTxFee.GetFeePerK()) + ")" + "\n";
strUsage += " -printtoconsole " + _("Send trace/debug info to console instead of debug.log file") + "\n";
if (GetBoolArg("-help-debug", false))
{
Expand Down Expand Up @@ -560,26 +569,28 @@ bool AppInit2(boost::thread_group& threadGroup)
{
int64_t n = 0;
if (ParseMoney(mapArgs["-mintxfee"], n) && n > 0)
CTransaction::nMinTxFee = n;
CTransaction::minTxFee = CFeeRate(n);
else
return InitError(strprintf(_("Invalid amount for -mintxfee=<amount>: '%s'"), mapArgs["-mintxfee"]));
}
if (mapArgs.count("-minrelaytxfee"))
{
int64_t n = 0;
if (ParseMoney(mapArgs["-minrelaytxfee"], n) && n > 0)
CTransaction::nMinRelayTxFee = n;
CTransaction::minRelayTxFee = CFeeRate(n);
else
return InitError(strprintf(_("Invalid amount for -minrelaytxfee=<amount>: '%s'"), mapArgs["-minrelaytxfee"]));
}

#ifdef ENABLE_WALLET
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"]));
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."));
payTxFee = CFeeRate(nFeePerK, 1000);
}
bSpendZeroConfChange = GetArg("-spendzeroconfchange", true);

Expand Down Expand Up @@ -931,6 +942,11 @@ bool AppInit2(boost::thread_group& threadGroup)
return false;
}

boost::filesystem::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
CAutoFile est_filein = CAutoFile(fopen(est_path.string().c_str(), "rb"), SER_DISK, CLIENT_VERSION);
if (est_filein)
mempool.ReadFeeEstimates(est_filein);

// ********************************************************* Step 8: load wallet
#ifdef ENABLE_WALLET
if (fDisableWallet) {
Expand Down
40 changes: 14 additions & 26 deletions src/main.cpp
Expand Up @@ -50,9 +50,9 @@ bool fTxIndex = false;
unsigned int nCoinCacheSize = 5000;

/** 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) */
int64_t CTransaction::nMinRelayTxFee = 1000;
CFeeRate CTransaction::minRelayTxFee = CFeeRate(1000);

struct COrphanBlock {
uint256 hashBlock;
Expand Down Expand Up @@ -543,7 +543,7 @@ bool IsStandardTx(const CTransaction& tx, string& reason)
}
if (whichType == TX_NULL_DATA)
nDataOut++;
else if (txout.IsDust(CTransaction::nMinRelayTxFee)) {
else if (txout.IsDust(CTransaction::minRelayTxFee)) {
reason = "dust";
return false;
}
Expand Down Expand Up @@ -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)
{
// Base fee is either nMinTxFee or nMinRelayTxFee
int64_t nBaseFee = (mode == GMF_RELAY) ? tx.nMinRelayTxFee : tx.nMinTxFee;
// Base fee is either minTxFee or minRelayTxFee
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)
{
Expand All @@ -800,16 +800,6 @@ int64_t GetMinFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree,
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))
nMinFee = MAX_MONEY;
return nMinFee;
Expand Down Expand Up @@ -861,6 +851,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
CCoinsView dummy;
CCoinsViewCache view(dummy);

int64_t nValueIn = 0;
{
LOCK(pool.cs);
CCoinsViewMemPool viewMemPool(*pcoinsTip, pool);
Expand Down Expand Up @@ -889,6 +880,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// Bring the best block into scope
view.GetBestBlock();

nValueIn = view.GetValueIn(tx);

// we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool
view.SetBackend(dummy);
}
Expand All @@ -901,7 +894,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// you should add code here to check that the transaction does a
// reasonable number of ECDSA signature verifications.

int64_t nValueIn = view.GetValueIn(tx);
int64_t nValueOut = tx.GetValueOut();
int64_t nFees = nValueIn-nValueOut;
double dPriority = view.GetPriority(tx, chainActive.Height());
Expand All @@ -916,10 +908,10 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
hash.ToString(), nFees, txMinFee),
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
// 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 double dFreeCount;
Expand All @@ -940,10 +932,10 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
dFreeCount += nSize;
}

if (fRejectInsaneFee && nFees > CTransaction::nMinRelayTxFee * 10000)
if (fRejectInsaneFee && nFees > CTransaction::minRelayTxFee.GetFee(nSize) * 10000)
return error("AcceptToMemoryPool: : insane fees %s, %d > %d",
hash.ToString(),
nFees, CTransaction::nMinRelayTxFee * 10000);
nFees, CTransaction::minRelayTxFee.GetFee(nSize) * 10000);

// Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
Expand Down Expand Up @@ -2027,11 +2019,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew) {
return false;
// Remove conflicting transactions from the mempool.
list<CTransaction> txConflicted;
BOOST_FOREACH(const CTransaction &tx, block.vtx) {
list<CTransaction> unused;
mempool.remove(tx, unused);
mempool.removeConflicts(tx, txConflicted);
}
mempool.removeForBlock(block.vtx, pindexNew->nHeight, txConflicted);
mempool.check(pcoinsTip);
// Update chainActive & related variables.
UpdateTip(pindexNew);
Expand Down
7 changes: 0 additions & 7 deletions src/main.h
Expand Up @@ -292,13 +292,6 @@ unsigned int GetLegacySigOpCount(const CTransaction& tx);
unsigned int GetP2SHSigOpCount(const CTransaction& tx, CCoinsViewCache& mapInputs);


inline bool AllowFree(double dPriority)
{
// Large (in bytes) low-priority (new, small-coin) transactions
// need a fee.
return dPriority > COIN * 144 / 250;
}

// Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts)
// This does not modify the UTXO set. If pvChecks is not NULL, script checks are pushed onto it
// instead of being performed inline.
Expand Down
42 changes: 22 additions & 20 deletions src/miner.cpp
Expand Up @@ -52,25 +52,30 @@ void SHA256Transform(void* pstate, void* pinput, const void* pinit)
((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
{
public:
const CTransaction* ptx;
set<uint256> setDependsOn;
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
{
LogPrintf("COrphan(hash=%s, dPriority=%.1f, dFeePerKb=%.1f)\n",
ptx->GetHash().ToString(), dPriority, dFeePerKb);
LogPrintf("COrphan(hash=%s, dPriority=%.1f, fee=%s)\n",
ptx->GetHash().ToString(), dPriority, feeRate.ToString());
BOOST_FOREACH(uint256 hash, setDependsOn)
LogPrintf(" setDependsOn %s\n", hash.ToString());
}
Expand All @@ -80,8 +85,8 @@ class COrphan
uint64_t nLastBlockTx = 0;
uint64_t nLastBlockSize = 0;

// We want to sort transactions by priority and fee, so:
typedef boost::tuple<double, double, const CTransaction*> TxPriority;
// We want to sort transactions by priority and fee rate, so:
typedef boost::tuple<double, CFeeRate, const CTransaction*> TxPriority;
class TxPriorityCompare
{
bool byFee;
Expand Down Expand Up @@ -210,18 +215,15 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
dPriority = tx.ComputePriority(dPriority, nTxSize);

// This is a more accurate fee-per-kilobyte than is used by the client code, because the
// 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);
CFeeRate feeRate(nTotalIn-tx.GetValueOut(), nTxSize);

if (porphan)
{
porphan->dPriority = dPriority;
porphan->dFeePerKb = dFeePerKb;
porphan->feeRate = feeRate;
}
else
vecPriority.push_back(TxPriority(dPriority, dFeePerKb, &mi->second.GetTx()));
vecPriority.push_back(TxPriority(dPriority, feeRate, &mi->second.GetTx()));
}

// Collect transactions into block
Expand All @@ -237,7 +239,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
{
// Take highest priority transaction off the priority queue:
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>());

std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer);
Expand All @@ -254,7 +256,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
continue;

// 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;

// Prioritize by fee once past the priority size or we run out of high-priority
Expand Down Expand Up @@ -298,8 +300,8 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)

if (fPrintPriority)
{
LogPrintf("priority %.1f feeperkb %.1f txid %s\n",
dPriority, dFeePerKb, tx.GetHash().ToString());
LogPrintf("priority %.1f fee %s txid %s\n",
dPriority, feeRate.ToString(), tx.GetHash().ToString());
}

// Add transactions that depend on this one to the priority queue
Expand All @@ -312,7 +314,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
porphan->setDependsOn.erase(hash);
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);
}
}
Expand Down
29 changes: 7 additions & 22 deletions src/qt/coincontroldialog.cpp
Expand Up @@ -453,7 +453,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)

CTxOut txout(amount, (CScript)vector<unsigned char>(24, 0));
txDummy.vout.push_back(txout);
if (txout.IsDust(CTransaction::nMinRelayTxFee))
if (txout.IsDust(CTransaction::minRelayTxFee))
fDust = true;
}
}
Expand Down Expand Up @@ -525,7 +525,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
sPriorityLabel = CoinControlDialog::getPriorityLabel(dPriority);

// Fee
int64_t nFee = nTransactionFee * (1 + (int64_t)nBytes / 1000);
int64_t nFee = payTxFee.GetFee(nBytes);

// Min Fee
int64_t nMinFee = GetMinFee(txDummy, nBytes, AllowFree(dPriority), GMF_SEND);
Expand All @@ -536,26 +536,11 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
{
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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't we want to keep this block, but do it to prevent dust change rather than subcent change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dust change is handled at line 543.

if (nChange > 0 && nChange < CENT)
{
CTxOut txout(nChange, (CScript)vector<unsigned char>(24, 0));
if (txout.IsDust(CTransaction::nMinRelayTxFee))
if (txout.IsDust(CTransaction::minRelayTxFee))
{
nPayFee += nChange;
nChange = 0;
Expand Down Expand Up @@ -610,19 +595,19 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)

// tool tips
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.");

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 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 />";
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.");

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);
l6->setToolTip(toolTip2);
Expand Down
2 changes: 1 addition & 1 deletion src/qt/guiutil.cpp
Expand Up @@ -210,7 +210,7 @@ bool isDust(const QString& address, qint64 amount)
CTxDestination dest = CBitcoinAddress(address.toStdString()).Get();
CScript script; script.SetDestination(dest);
CTxOut txOut(amount, script);
return txOut.IsDust(CTransaction::nMinRelayTxFee);
return txOut.IsDust(CTransaction::minRelayTxFee);
}

QString HtmlEscape(const QString& str, bool fMultiLine)
Expand Down
4 changes: 2 additions & 2 deletions src/qt/optionsdialog.cpp
Expand Up @@ -14,7 +14,7 @@
#include "monitoreddatamapper.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 "txdb.h" // for -dbcache defaults

Expand Down Expand Up @@ -101,7 +101,7 @@ OptionsDialog::OptionsDialog(QWidget *parent) :
#endif

ui->unit->setModel(new BitcoinUnits(this));
ui->transactionFee->setSingleStep(CTransaction::nMinTxFee);
ui->transactionFee->setSingleStep(CTransaction::minTxFee.GetFeePerK());

/* Widget-to-option mapper */
mapper = new MonitoredDataMapper(this);
Expand Down
21 changes: 12 additions & 9 deletions src/qt/optionsmodel.cpp
Expand Up @@ -94,7 +94,7 @@ void OptionsModel::Init()
#ifdef ENABLE_WALLET
if (!settings.contains("nTransactionFee"))
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"))
addOverriddenOption("-paytxfee");

Expand Down Expand Up @@ -187,15 +187,16 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const
return settings.value("nSocksVersion", 5);

#ifdef ENABLE_WALLET
case Fee:
// Attention: Init() is called before nTransactionFee is set in AppInit2()!
case Fee: {
// Attention: Init() is called before payTxFee is set in AppInit2()!
// To ensure we can change the fee on-the-fly update our QSetting when
// opening OptionsDialog, which queries Fee via the mapper.
if (nTransactionFee != settings.value("nTransactionFee").toLongLong())
settings.setValue("nTransactionFee", (qint64)nTransactionFee);
// Todo: Consider to revert back to use just nTransactionFee here, if we don't want
if (!(payTxFee == CFeeRate(settings.value("nTransactionFee").toLongLong(), 1000)))
settings.setValue("nTransactionFee", (qint64)payTxFee.GetFeePerK());
// Todo: Consider to revert back to use just payTxFee here, if we don't want
// -paytxfee to update our QSettings!
return settings.value("nTransactionFee");
}
case SpendZeroConfChange:
return settings.value("bSpendZeroConfChange");
#endif
Expand Down Expand Up @@ -284,12 +285,14 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
}
break;
#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
nTransactionFee = value.toLongLong();
settings.setValue("nTransactionFee", (qint64)nTransactionFee);
qint64 nTransactionFee = value.toLongLong();
payTxFee = CFeeRate(nTransactionFee, 1000);
settings.setValue("nTransactionFee", nTransactionFee);
emit transactionFeeChanged(nTransactionFee);
break;
}
case SpendZeroConfChange:
if (settings.value("bSpendZeroConfChange") != value) {
settings.setValue("bSpendZeroConfChange", value);
Expand Down
2 changes: 1 addition & 1 deletion src/qt/paymentserver.cpp
Expand Up @@ -551,7 +551,7 @@ bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, SendCoins

// Extract and check amounts
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).")
.arg(BitcoinUnits::formatWithUnit(optionsModel->getDisplayUnit(), sendingTo.second)),
CClientUIInterface::MSG_ERROR);
Expand Down
6 changes: 0 additions & 6 deletions src/qt/walletmodel.cpp
Expand Up @@ -231,12 +231,6 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
return AmountExceedsBalance;
}

if((total + nTransactionFee) > nBalance)
{
transaction.setTransactionFee(nTransactionFee);
return SendCoinsReturn(AmountWithFeeExceedsBalance);
}

{
LOCK2(cs_main, wallet->cs_wallet);

Expand Down
2 changes: 2 additions & 0 deletions src/rpcclient.cpp
Expand Up @@ -176,6 +176,8 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri
if (strMethod == "verifychain" && n > 1) ConvertTo<int64_t>(params[1]);
if (strMethod == "keypoolrefill" && n > 0) ConvertTo<int64_t>(params[0]);
if (strMethod == "getrawmempool" && n > 0) ConvertTo<bool>(params[0]);
if (strMethod == "estimatefee" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "estimatepriority" && n > 0) ConvertTo<boost::int64_t>(params[0]);

return params;
}
Expand Down
61 changes: 61 additions & 0 deletions src/rpcmining.cpp
Expand Up @@ -15,6 +15,7 @@
#endif
#include <stdint.h>

#include <boost/assign/list_of.hpp>
#include "json/json_spirit_utils.h"
#include "json/json_spirit_value.h"

Expand Down Expand Up @@ -626,3 +627,63 @@ Value submitblock(const Array& params, bool fHelp)

return Value::null;
}

Value estimatefee(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw runtime_error(
"estimatefee nblocks\n"
"\nEstimates the approximate fee per kilobyte\n"
"needed for a transaction to get confirmed\n"
"within nblocks blocks.\n"
"\nArguments:\n"
"1. nblocks (numeric)\n"
"\nResult:\n"
"n : (numeric) estimated fee-per-kilobyte\n"
"\n"
"-1.0 is returned if not enough transactions and\n"
"blocks have been observed to make an estimate.\n"
"\nExample:\n"
+ HelpExampleCli("estimatefee", "6")
);

RPCTypeCheck(params, boost::assign::list_of(int_type));

int nBlocks = params[0].get_int();
if (nBlocks < 1)
nBlocks = 1;

CFeeRate feeRate = mempool.estimateFee(nBlocks);
if (feeRate == CFeeRate(0))
return -1.0;

return ValueFromAmount(feeRate.GetFeePerK());
}

Value estimatepriority(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw runtime_error(
"estimatepriority nblocks\n"
"\nEstimates the approximate priority\n"
"a zero-fee transaction needs to get confirmed\n"
"within nblocks blocks.\n"
"\nArguments:\n"
"1. nblocks (numeric)\n"
"\nResult:\n"
"n : (numeric) estimated priority\n"
"\n"
"-1.0 is returned if not enough transactions and\n"
"blocks have been observed to make an estimate.\n"
"\nExample:\n"
+ HelpExampleCli("estimatepriority", "6")
);

RPCTypeCheck(params, boost::assign::list_of(int_type));

int nBlocks = params[0].get_int();
if (nBlocks < 1)
nBlocks = 1;

return mempool.estimatePriority(nBlocks);
}
4 changes: 2 additions & 2 deletions src/rpcmisc.cpp
Expand Up @@ -81,9 +81,9 @@ Value getinfo(const Array& params, bool fHelp)
}
if (pwalletMain && pwalletMain->IsCrypted())
obj.push_back(Pair("unlocked_until", nWalletUnlockTime));
obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK())));
#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")));
return obj;
}
Expand Down
2 changes: 1 addition & 1 deletion src/rpcnet.cpp
Expand Up @@ -368,7 +368,7 @@ Value getnetworkinfo(const Array& params, bool fHelp)
obj.push_back(Pair("timeoffset", GetTimeOffset()));
obj.push_back(Pair("connections", (int)vNodes.size()));
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;
{
LOCK(cs_mapLocalHost);
Expand Down
2 changes: 2 additions & 0 deletions src/rpcserver.cpp
Expand Up @@ -268,6 +268,8 @@ static const CRPCCommand vRPCCommands[] =
{ "createmultisig", &createmultisig, true, true , false },
{ "validateaddress", &validateaddress, true, false, false }, /* uses wallet if enabled */
{ "verifymessage", &verifymessage, false, false, false },
{ "estimatefee", &estimatefee, true, true, false },
{ "estimatepriority", &estimatepriority, true, true, false },

#ifdef ENABLE_WALLET
/* Wallet */
Expand Down
2 changes: 2 additions & 0 deletions src/rpcserver.h
Expand Up @@ -133,6 +133,8 @@ extern json_spirit::Value getmininginfo(const json_spirit::Array& params, bool f
extern json_spirit::Value getwork(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getblocktemplate(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value submitblock(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value estimatefee(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value estimatepriority(const json_spirit::Array& params, bool fHelp);

extern json_spirit::Value getnewaddress(const json_spirit::Array& params, bool fHelp); // in rpcwallet.cpp
extern json_spirit::Value getaccountaddress(const json_spirit::Array& params, bool fHelp);
Expand Down
2 changes: 1 addition & 1 deletion src/rpcwallet.cpp
Expand Up @@ -1883,7 +1883,7 @@ Value settxfee(const Array& params, bool fHelp)
if (params[0].get_real() != 0.0)
nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts

nTransactionFee = nAmount;
payTxFee = CFeeRate(nAmount, 1000);
return true;
}

Expand Down
370 changes: 370 additions & 0 deletions src/txmempool.cpp

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions src/txmempool.h
Expand Up @@ -11,6 +11,13 @@
#include "core.h"
#include "sync.h"

inline bool AllowFree(double dPriority)
{
// Large (in bytes) low-priority (new, small-coin) transactions
// need a fee.
return dPriority > COIN * 144 / 250;
}

/** Fake height value used in CCoins to signify they are only in the memory pool (since 0.8) */
static const unsigned int MEMPOOL_HEIGHT = 0x7FFFFFFF;

Expand Down Expand Up @@ -41,6 +48,8 @@ class CTxMemPoolEntry
unsigned int GetHeight() const { return nHeight; }
};

class CMinerPolicyEstimator;

/*
* CTxMemPool stores valid-according-to-the-current-best-chain
* transactions that may be included in the next block.
Expand All @@ -56,13 +65,15 @@ class CTxMemPool
private:
bool fSanityCheck; // Normally false, true if -checkmempool or -regtest
unsigned int nTransactionsUpdated;
CMinerPolicyEstimator* minerPolicyEstimator;

public:
mutable CCriticalSection cs;
std::map<uint256, CTxMemPoolEntry> mapTx;
std::map<COutPoint, CInPoint> mapNextTx;

CTxMemPool();
~CTxMemPool();

/*
* If sanity-checking is turned on, check makes sure the pool is
Expand All @@ -76,6 +87,8 @@ class CTxMemPool
bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry);
void remove(const CTransaction &tx, std::list<CTransaction>& removed, bool fRecursive = false);
void removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed);
void removeForBlock(const std::vector<CTransaction>& vtx, unsigned int nBlockHeight,
std::list<CTransaction>& conflicts);
void clear();
void queryHashes(std::vector<uint256>& vtxid);
void pruneSpent(const uint256& hash, CCoins &coins);
Expand All @@ -95,6 +108,16 @@ class CTxMemPool
}

bool lookup(uint256 hash, CTransaction& result) const;

// Estimate fee rate needed to get into the next
// nBlocks
CFeeRate estimateFee(int nBlocks) const;
// Estimate priority needed to get into the next
// nBlocks
double estimatePriority(int nBlocks) const;
// Write/Read estimates to disk
bool WriteFeeEstimates(CAutoFile& fileout) const;
bool ReadFeeEstimates(CAutoFile& filein);
};

/** CCoinsView that brings transactions from a memorypool into view.
Expand Down
22 changes: 6 additions & 16 deletions src/wallet.cpp
Expand Up @@ -16,7 +16,7 @@
using namespace std;

// Settings
int64_t nTransactionFee = DEFAULT_TRANSACTION_FEE;
CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE);
bool bSpendZeroConfChange = true;

//////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -1233,7 +1233,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend,
{
LOCK2(cs_main, cs_wallet);
{
nFeeRet = nTransactionFee;
nFeeRet = payTxFee.GetFeePerK();
while (true)
{
wtxNew.vin.clear();
Expand All @@ -1246,7 +1246,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend,
BOOST_FOREACH (const PAIRTYPE(CScript, int64_t)& s, vecSend)
{
CTxOut txout(s.second, s.first);
if (txout.IsDust(CTransaction::nMinRelayTxFee))
if (txout.IsDust(CTransaction::minRelayTxFee))
{
strFailReason = _("Transaction amount too small");
return false;
Expand All @@ -1272,16 +1272,6 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend,
}

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)
{
Expand Down Expand Up @@ -1317,7 +1307,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend,

// Never create dust outputs; if we would, just
// add the dust to the fee.
if (newTxOut.IsDust(CTransaction::nMinRelayTxFee))
if (newTxOut.IsDust(CTransaction::minRelayTxFee))
{
nFeeRet += nChange;
reservekey.ReturnKey();
Expand Down Expand Up @@ -1355,7 +1345,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend,
dPriority = wtxNew.ComputePriority(dPriority, nBytes);

// 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);
int64_t nMinFee = GetMinFee(wtxNew, nBytes, fAllowFree, GMF_SEND);
if (nFeeRet < max(nPayFee, nMinFee))
Expand Down Expand Up @@ -1464,7 +1454,7 @@ string CWallet::SendMoneyToDestination(const CTxDestination& address, int64_t nV
// Check amount
if (nValue <= 0)
return _("Invalid amount");
if (nValue + nTransactionFee > GetBalance())
if (nValue > GetBalance())
return _("Insufficient funds");

// Parse Bitcoin address
Expand Down
2 changes: 1 addition & 1 deletion src/wallet.h
Expand Up @@ -24,7 +24,7 @@
#include <vector>

// Settings
extern int64_t nTransactionFee;
extern CFeeRate payTxFee;
extern bool bSpendZeroConfChange;

// -paytxfee default
Expand Down