aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore7
-rw-r--r--configure.ac2
-rw-r--r--doc/gitian-building.md35
-rw-r--r--doc/release-notes.md20
-rw-r--r--doc/release-process.md4
-rw-r--r--src/core.cpp2
-rw-r--r--src/core.h9
-rw-r--r--src/init.cpp37
-rw-r--r--src/main.cpp197
-rw-r--r--src/main.h16
-rw-r--r--src/miner.cpp2
-rw-r--r--src/net.cpp16
-rw-r--r--src/net.h12
-rw-r--r--src/netbase.cpp60
-rw-r--r--src/qt/bitcoingui.cpp78
-rw-r--r--src/qt/bitcoingui.h37
-rw-r--r--src/qt/bitcoinunits.cpp10
-rw-r--r--src/qt/bitcoinunits.h2
-rw-r--r--src/qt/coincontroldialog.cpp60
-rw-r--r--src/qt/coincontroldialog.h3
-rw-r--r--src/qt/guiutil.cpp3
-rw-r--r--src/qt/optionsdialog.cpp9
-rw-r--r--src/qt/optionsmodel.cpp17
-rw-r--r--src/qt/optionsmodel.h2
-rw-r--r--src/qt/paymentserver.cpp2
-rw-r--r--src/qt/recentrequeststablemodel.cpp27
-rw-r--r--src/qt/recentrequeststablemodel.h6
-rw-r--r--src/qt/transactiontablemodel.cpp11
-rw-r--r--src/qt/transactiontablemodel.h2
-rw-r--r--src/qt/transactionview.cpp2
-rw-r--r--src/rpcmisc.cpp2
-rw-r--r--src/rpcnet.cpp2
-rw-r--r--src/rpcserver.cpp2
-rw-r--r--src/txmempool.cpp35
-rw-r--r--src/txmempool.h4
-rw-r--r--src/util.cpp1
-rw-r--r--src/util.h1
-rw-r--r--src/wallet.cpp51
-rw-r--r--src/wallet.h6
39 files changed, 563 insertions, 231 deletions
diff --git a/.gitignore b/.gitignore
index 564fe68fd9..4169a2d96c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,13 @@ src/config/stamp-h1
src/build-aux/
share/setup.nsi
share/qt/Info.plist
+# Libtool
+libtool
+src/m4/libtool.m4
+src/m4/ltoptions.m4
+src/m4/ltsugar.m4
+src/m4/ltversion.m4
+src/m4/lt~obsolete.m4
src/qt/*.moc
src/qt/moc_*.cpp
diff --git a/configure.ac b/configure.ac
index 2a4636e360..dcaec0d4b0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -370,6 +370,8 @@ if test x$TARGET_OS = xdarwin; then
fi
AC_CHECK_HEADERS([endian.h stdio.h stdlib.h unistd.h strings.h sys/types.h sys/stat.h sys/select.h])
+AC_SEARCH_LIBS([getaddrinfo_a], [anl], [AC_DEFINE(HAVE_GETADDRINFO_A, 1, [Define this symbol if you have getaddrinfo_a])])
+AC_SEARCH_LIBS([inet_pton], [nsl resolv], [AC_DEFINE(HAVE_INET_PTON, 1, [Define this symbol if you have inet_pton])])
AC_CHECK_DECLS([le32toh, le64toh, htole32, htole64, be32toh, be64toh, htobe32, htobe64],,,
[#if HAVE_ENDIAN_H
diff --git a/doc/gitian-building.md b/doc/gitian-building.md
index 378a45eda5..b356a5d88d 100644
--- a/doc/gitian-building.md
+++ b/doc/gitian-building.md
@@ -4,8 +4,8 @@ Gitian building
*Setup instructions for a gitian build of Bitcoin using a Debian VM or physical system.*
Gitian is the deterministic build process that is used to build the Bitcoin
-Core executables [1]. It provides a way to be reasonably sure that the
-executables are really built from source on github. It also makes sure that
+Core executables. It provides a way to be reasonably sure that the
+executables are really built from source on GitHub. It also makes sure that
the same, tested dependencies are used and statically built into the executable.
Multiple developers build the source code by following a specific descriptor
@@ -17,9 +17,6 @@ More independent gitian builders are needed, which is why I wrote this
guide. It is preferred to follow these steps yourself instead of using someone else's
VM image to avoid 'contaminating' the build.
-[1] For all platforms except for MacOSX, at this point. Work for deterministic
-builds for Mac is under way here: https://github.com/theuni/osx-cross-depends .
-
Table of Contents
------------------
@@ -38,7 +35,7 @@ Preparing the Gitian builder host
---------------------------------
The first step is to prepare the host environment that will be used to perform the Gitian builds.
-In this guide it is explained how to set up the environment, and how to get the builds started.
+This guide explains how to set up the environment, and how to start the builds.
Debian Linux was chosen as the host distribution because it has a lightweight install (in contrast to Ubuntu) and is readily available.
Any kind of virtualization can be used, for example:
@@ -134,7 +131,7 @@ and proceed, just press `Enter`. To select a different button, press `Tab`.
![](gitian-building/debian_install_5_configure_the_network.png)
-- Choose a root password and enter it twice (and remember it for later)
+- Choose a root password and enter it twice (remember it for later)
![](gitian-building/debian_install_6a_set_up_root_password.png)
@@ -143,7 +140,7 @@ and proceed, just press `Enter`. To select a different button, press `Tab`.
![](gitian-building/debian_install_7_set_up_user_fullname.png)
![](gitian-building/debian_install_8_set_up_username.png)
-- Choose a user password and enter it twice (and remember it for later)
+- Choose a user password and enter it twice (remember it for later)
![](gitian-building/debian_install_9_user_password.png)
@@ -236,7 +233,7 @@ adduser debian sudo
When you get a colorful screen with a question about the 'LXC directory', just
go with the default (`/var/lib/lxc`).
-Then set up LXC and the rest with the following is a complex jumble of settings and workarounds:
+Then set up LXC and the rest with the following, which is a complex jumble of settings and workarounds:
```bash
# the version of lxc-start in Debian 7.4 needs to run as root, so make sure
@@ -280,7 +277,7 @@ cd ..
**Note**: When sudo asks for a password, enter the password for the user *debian* not for *root*.
-Clone the git repositories for bitcoin and gitian and then checkout the bitcoin version that you are willing to build.
+Clone the git repositories for bitcoin and gitian and then checkout the bitcoin version that you want to build.
```bash
git clone https://github.com/devrandom/gitian-builder.git
@@ -319,10 +316,10 @@ you will find a list of `wget` commands that can be executed to get the dependen
I needed to add `--no-check-certificate` to the OpenSSL wget line to make it work.
Likely this is because the ca-certificates in Debian 7.4 is fairly old. This does not create a
-security issue as the gitian descriptors check integrity of the input archives and refuse to work
+security issue as the gitian descriptors check the integrity of the input archives and refuse to work
if any one is corrupted.
-After downloading the archives, execute the `gbuild` commends to build the dependencies.
+After downloading the archives, execute the `gbuild` commands to build the dependencies.
This can take a long time, but only has to be done when the dependencies change, for example
to upgrade the used version.
@@ -339,7 +336,7 @@ tail -f var/build.log
Building Bitcoin
----------------
-To build Bitcoin (for Linux and/or Windows) just follow the steps under 'perform
+To build Bitcoin (for Linux, OSX and Windows) just follow the steps under 'perform
gitian builds' in [doc/release-process.md](release-process.md) in the bitcoin repository.
Output from `gbuild` will look something like
@@ -372,7 +369,7 @@ can be inspected in `var/install.log` and `var/build.log`.
Building an alternative repository
-----------------------------------
-If you want to do a test build of a pull on github it can be useful to point
+If you want to do a test build of a pull on GitHub it can be useful to point
the gitian builder at an alternative repository, using the same descriptors
and inputs.
@@ -382,13 +379,14 @@ URL=https://github.com/laanwj/bitcoin.git
COMMIT=2014_03_windows_unicode_path
./bin/gbuild --commit bitcoin=${COMMIT} --url bitcoin=${URL} ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml
./bin/gbuild --commit bitcoin=${COMMIT} --url bitcoin=${URL} ../bitcoin/contrib/gitian-descriptors/gitian-win.yml
+./bin/gbuild --commit bitcoin=${COMMIT} --url bitcoin=${URL} ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml
```
Signing externally
-------------------
-If you want to do the PGP signing on another device that's possible too; just define `SIGNER` as mentioned
-and follow the steps in the build process as normally.
+If you want to do the PGP signing on another device that's also possible; just define `SIGNER` as mentioned
+and follow the steps in the build process as normal.
gpg: skipped "laanwj": secret key not available
@@ -396,8 +394,9 @@ When you execute `gsign` you will get an error from GPG, which can be ignored. C
in `gitian.sigs` to your signing machine and do
```bash
- gpg --detach-sign ${VERSION}/${SIGNER}/bitcoin-build.assert
+ gpg --detach-sign ${VERSION}-linux/${SIGNER}/bitcoin-build.assert
gpg --detach-sign ${VERSION}-win/${SIGNER}/bitcoin-build.assert
+ gpg --detach-sign ${VERSION}-osx/${SIGNER}/bitcoin-build.assert
```
This will create the `.sig` files that can be committed together with the `.assert` files to assert your
@@ -407,5 +406,5 @@ Uploading signatures
---------------------
After building and signing you can push your signatures (both the `.assert` and `.assert.sig` files) to the
-[bitcoin/gitian.sigs](https://github.com/bitcoin/gitian.sigs/) repository, or if not possible create a pull
+[bitcoin/gitian.sigs](https://github.com/bitcoin/gitian.sigs/) repository, or if that's not possible create a pull
request. You can also mail the files to me (laanwj@gmail.com) and I'll commit them.
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 3a4079e437..66059800b6 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -1,6 +1,26 @@
(note: this is a temporary file, to be added-to by anybody, and moved to
release-notes at release time)
+Transaction fee changes
+=======================
+
+This release automatically estimates how high a transaction fee (or how
+high a priority) transactions require to be confirmed quickly. The default
+settings will create transactions that confirm quickly; see the new
+'txconfirmtarget' setting to control the tradeoff between fees and
+confirmation times.
+
+Prior releases used hard-coded fees (and priorities), and would
+sometimes create transactions that took a very long time to confirm.
+
+
+New Command Line Options
+========================
+
+-txconfirmtarget=n : create transactions that have enough fees (or priority)
+so they are likely to confirm within n blocks (default: 1). This setting
+is over-ridden by the -paytxfee option.
+
New RPC methods
===============
diff --git a/doc/release-process.md b/doc/release-process.md
index 324b8c43ab..c588381411 100644
--- a/doc/release-process.md
+++ b/doc/release-process.md
@@ -23,7 +23,7 @@ Release Process
* * *
-##perform gitian builds
+###perform gitian builds
From a directory containing the bitcoin source, gitian-builder and gitian.sigs
@@ -34,7 +34,7 @@ Release Process
popd
pushd ./gitian-builder
- ###Fetch and build inputs: (first time, or when dependency versions change)
+###fetch and build inputs: (first time, or when dependency versions change)
mkdir -p inputs; cd inputs/
diff --git a/src/core.cpp b/src/core.cpp
index ca28624529..47f3b2a015 100644
--- a/src/core.cpp
+++ b/src/core.cpp
@@ -80,7 +80,7 @@ CFeeRate::CFeeRate(int64_t nFeePaid, size_t nSize)
nSatoshisPerK = 0;
}
-int64_t CFeeRate::GetFee(size_t nSize)
+int64_t CFeeRate::GetFee(size_t nSize) const
{
return nSatoshisPerK*nSize / 1000;
}
diff --git a/src/core.h b/src/core.h
index 8606831575..0387336c98 100644
--- a/src/core.h
+++ b/src/core.h
@@ -125,13 +125,14 @@ public:
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
+ int64_t GetFee(size_t size) const; // unit returned is satoshis
+ int64_t GetFeePerK() const { 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; }
-
+ 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); )
@@ -216,8 +217,6 @@ private:
void UpdateHash() const;
public:
- static CFeeRate minTxFee;
- static CFeeRate minRelayTxFee;
static const int CURRENT_VERSION=1;
// The local variables are made const to prevent unintended modification
diff --git a/src/init.cpp b/src/init.cpp
index da13218a94..c514facbbe 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -253,14 +253,16 @@ std::string HelpMessage(HelpMessageMode mode)
#ifdef ENABLE_WALLET
strUsage += "\n" + _("Wallet options:") + "\n";
strUsage += " -disablewallet " + _("Do not load the wallet and disable wallet RPC calls") + "\n";
+ strUsage += " -mintxfee=<amt> " + strprintf(_("Fees (in BTC/Kb) smaller than this are considered zero fee for transaction creation (default: %s)"), FormatMoney(CWallet::minTxFee.GetFeePerK())) + "\n";
strUsage += " -paytxfee=<amt> " + strprintf(_("Fee (in BTC/kB) to add to transactions you send (default: %s)"), FormatMoney(payTxFee.GetFeePerK())) + "\n";
strUsage += " -rescan " + _("Rescan the block chain for missing wallet transactions") + " " + _("on startup") + "\n";
+ strUsage += " -respendnotify=<cmd> " + _("Execute command when a network tx respends wallet tx input (%s=respend TxID, %t=wallet TxID)") + "\n";
strUsage += " -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + " " + _("on startup") + "\n";
strUsage += " -spendzeroconfchange " + _("Spend unconfirmed change when sending transactions (default: 1)") + "\n";
+ strUsage += " -txconfirmtarget=<n> " + _("If paytxfee is not set, include enough fee so transactions are confirmed on average within n blocks (default: 1)") + "\n";
strUsage += " -upgradewallet " + _("Upgrade wallet to latest format") + " " + _("on startup") + "\n";
strUsage += " -wallet=<file> " + _("Specify wallet file (within data directory)") + " " + _("(default: wallet.dat)") + "\n";
strUsage += " -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n";
- strUsage += " -respendnotify=<cmd> " + _("Execute command when a network tx respends wallet tx input (%s=respend TxID, %t=wallet TxID)") + "\n";
strUsage += " -zapwallettxes=<mode> " + _("Delete all wallet transactions and only recover those part of the blockchain through -rescan on startup") + "\n";
strUsage += " " + _("(default: 1, 1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)") + "\n";
#endif
@@ -288,14 +290,14 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += " -gen " + _("Generate coins (default: 0)") + "\n";
strUsage += " -genproclimit=<n> " + _("Set the processor limit for when generation is on (-1 = unlimited, default: -1)") + "\n";
strUsage += " -help-debug " + _("Show all debugging options (usage: --help -help-debug)") + "\n";
+ strUsage += " -logips " + _("Include IP addresses in debug output (default: 0)") + "\n";
strUsage += " -logtimestamps " + _("Prepend debug output with timestamp (default: 1)") + "\n";
if (GetBoolArg("-help-debug", false))
{
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> " + strprintf(_("Fees (in BTC/Kb) smaller than this are considered zero fee for transaction creation (default: %s)"), FormatMoney(CTransaction::minTxFee.GetFeePerK())) + "\n";
- strUsage += " -minrelaytxfee=<amt> " + strprintf(_("Fees (in BTC/Kb) smaller than this are considered zero fee for relaying (default: %s)"), FormatMoney(CTransaction::minRelayTxFee.GetFeePerK())) + "\n";
+ strUsage += " -minrelaytxfee=<amt> " + strprintf(_("Fees (in BTC/Kb) smaller than this are considered zero fee for relaying (default: %s)"), FormatMoney(::minRelayTxFee.GetFeePerK())) + "\n";
strUsage += " -printtoconsole " + _("Send trace/debug info to console instead of debug.log file") + "\n";
if (GetBoolArg("-help-debug", false))
{
@@ -323,7 +325,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += " -rpcuser=<user> " + _("Username for JSON-RPC connections") + "\n";
strUsage += " -rpcpassword=<pw> " + _("Password for JSON-RPC connections") + "\n";
strUsage += " -rpcport=<port> " + _("Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)") + "\n";
- strUsage += " -rpcallowip=<ip> " + _("Allow JSON-RPC connections from specified IP address. This option can be specified multiple times") + "\n";
+ strUsage += " -rpcallowip=<ip> " + _("Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times") + "\n";
strUsage += " -rpcthreads=<n> " + _("Set the number of threads to service RPC calls (default: 4)") + "\n";
strUsage += "\n" + _("RPC SSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n";
@@ -585,6 +587,7 @@ bool AppInit2(boost::thread_group& threadGroup)
fServer = GetBoolArg("-server", false);
fPrintToConsole = GetBoolArg("-printtoconsole", false);
fLogTimestamps = GetBoolArg("-logtimestamps", true);
+ fLogIPs = GetBoolArg("-logips", false);
setvbuf(stdout, NULL, _IOLBF, 0);
#ifdef ENABLE_WALLET
bool fDisableWallet = GetBoolArg("-disablewallet", false);
@@ -609,24 +612,24 @@ bool AppInit2(boost::thread_group& threadGroup)
// a transaction spammer can cheaply fill blocks using
// 1-satoshi-fee transactions. It should be set above the real
// cost to you of processing a transaction.
- if (mapArgs.count("-mintxfee"))
- {
- int64_t n = 0;
- if (ParseMoney(mapArgs["-mintxfee"], n) && n > 0)
- 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::minRelayTxFee = CFeeRate(n);
+ ::minRelayTxFee = CFeeRate(n);
else
return InitError(strprintf(_("Invalid amount for -minrelaytxfee=<amount>: '%s'"), mapArgs["-minrelaytxfee"]));
}
#ifdef ENABLE_WALLET
+ if (mapArgs.count("-mintxfee"))
+ {
+ int64_t n = 0;
+ if (ParseMoney(mapArgs["-mintxfee"], n) && n > 0)
+ CWallet::minTxFee = CFeeRate(n);
+ else
+ return InitError(strprintf(_("Invalid amount for -mintxfee=<amount>: '%s'"), mapArgs["-mintxfee"]));
+ }
if (mapArgs.count("-paytxfee"))
{
int64_t nFeePerK = 0;
@@ -635,7 +638,13 @@ bool AppInit2(boost::thread_group& threadGroup)
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);
+ if (payTxFee < ::minRelayTxFee)
+ {
+ return InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"),
+ mapArgs["-paytxfee"], ::minRelayTxFee.ToString()));
+ }
}
+ nTxConfirmTarget = GetArg("-txconfirmtarget", 1);
bSpendZeroConfChange = GetArg("-spendzeroconfchange", true);
std::string strWalletFile = GetArg("-wallet", "wallet.dat");
@@ -1176,7 +1185,7 @@ bool AppInit2(boost::thread_group& threadGroup)
LogPrintf("mapAddressBook.size() = %u\n", pwalletMain ? pwalletMain->mapAddressBook.size() : 0);
#endif
- RegisterInternalSignals();
+ InitRespendFilter();
StartNode(threadGroup);
if (fServer)
StartRPCThreads();
diff --git a/src/main.cpp b/src/main.cpp
index 04d9523e26..e08b794184 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -38,8 +38,6 @@ using namespace boost;
CCriticalSection cs_main;
-CTxMemPool mempool;
-
map<uint256, CBlockIndex*> mapBlockIndex;
CChain chainActive;
int64_t nTimeBestReceived = 0;
@@ -50,10 +48,10 @@ bool fBenchmark = false;
bool fTxIndex = false;
unsigned int nCoinCacheSize = 5000;
-/** Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) */
-CFeeRate CTransaction::minTxFee = CFeeRate(10000); // Override with -mintxfee
/** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */
-CFeeRate CTransaction::minRelayTxFee = CFeeRate(1000);
+CFeeRate minRelayTxFee = CFeeRate(1000);
+
+CTxMemPool mempool(::minRelayTxFee);
struct COrphanBlock {
uint256 hashBlock;
@@ -125,9 +123,14 @@ namespace {
} // anon namespace
-// Forward reference functions defined here:
+// Bloom filter to limit respend relays to one
static const unsigned int MAX_DOUBLESPEND_BLOOM = 1000;
-static void RelayDoubleSpend(const COutPoint& outPoint, const CTransaction& doubleSpend, bool fInBlock, CBloomFilter& filter);
+static CBloomFilter doubleSpendFilter;
+void InitRespendFilter() {
+ seed_insecure_rand();
+ doubleSpendFilter = CBloomFilter(MAX_DOUBLESPEND_BLOOM, 0.01, insecure_rand(), BLOOM_UPDATE_NONE);
+}
+
//////////////////////////////////////////////////////////////////////////////
//
@@ -151,24 +154,10 @@ struct CMainSignals {
boost::signals2::signal<void (const uint256 &)> Inventory;
// Tells listeners to broadcast their data.
boost::signals2::signal<void ()> Broadcast;
- // Notifies listeners of detection of a double-spent transaction. Arguments are outpoint that is
- // double-spent, first transaction seen, double-spend transaction, and whether the second double-spend
- // transaction was first seen in a block.
- // Note: only notifies if the previous transaction is in the memory pool; if previous transction was in a block,
- // then the double-spend simply fails when we try to lookup the inputs in the current UTXO set.
- boost::signals2::signal<void (const COutPoint&, const CTransaction&, bool)> DetectedDoubleSpend;
} g_signals;
} // anon namespace
-void RegisterInternalSignals() {
- static CBloomFilter doubleSpendFilter;
- seed_insecure_rand();
- doubleSpendFilter = CBloomFilter(MAX_DOUBLESPEND_BLOOM, 0.01, insecure_rand(), BLOOM_UPDATE_NONE);
-
- g_signals.DetectedDoubleSpend.connect(boost::bind(RelayDoubleSpend, _1, _2, _3, doubleSpendFilter));
-}
-
void RegisterWallet(CWalletInterface* pwalletIn) {
g_signals.SyncTransaction.connect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2));
@@ -617,7 +606,7 @@ bool IsStandardTx(const CTransaction& tx, string& reason)
}
if (whichType == TX_NULL_DATA)
nDataOut++;
- else if (txout.IsDust(CTransaction::minRelayTxFee)) {
+ else if (txout.IsDust(::minRelayTxFee)) {
reason = "dust";
return false;
}
@@ -858,7 +847,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
return true;
}
-int64_t GetMinFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree, enum GetMinFee_mode mode)
+int64_t GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree)
{
{
LOCK(mempool.cs);
@@ -870,10 +859,7 @@ int64_t GetMinFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree,
return 0;
}
- // Base fee is either minTxFee or minRelayTxFee
- CFeeRate baseFeeRate = (mode == GMF_RELAY) ? tx.minRelayTxFee : tx.minTxFee;
-
- int64_t nMinFee = baseFeeRate.GetFee(nBytes);
+ int64_t nMinFee = ::minRelayTxFee.GetFee(nBytes);
if (fAllowFree)
{
@@ -881,9 +867,7 @@ int64_t GetMinFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree,
// * If we are relaying we allow transactions up to DEFAULT_BLOCK_PRIORITY_SIZE - 1000
// to be considered to fall into this category. We don't want to encourage sending
// multiple transactions instead of one big transaction to avoid fees.
- // * If we are creating a transaction we allow transactions up to 1,000 bytes
- // to be considered safe and assume they can likely make it into this section.
- if (nBytes < (mode == GMF_SEND ? 1000 : (DEFAULT_BLOCK_PRIORITY_SIZE - 1000)))
+ if (nBytes < (DEFAULT_BLOCK_PRIORITY_SIZE - 1000))
nMinFee = 0;
}
@@ -908,6 +892,45 @@ bool RateLimitExceeded(double& dCount, int64_t& nLastTime, int64_t nLimit, unsig
return false;
}
+static bool RelayableRespend(const COutPoint& outPoint, const CTransaction& doubleSpend, bool fInBlock, CBloomFilter& filter)
+{
+ // Relaying double-spend attempts to our peers lets them detect when
+ // somebody might be trying to cheat them. However, blindly relaying
+ // every double-spend across the entire network gives attackers
+ // a denial-of-service attack: just generate a stream of double-spends
+ // re-spending the same (limited) set of outpoints owned by the attacker.
+ // So, we use a bloom filter and only relay (at most) the first double
+ // spend for each outpoint. False-positives ("we have already relayed")
+ // are OK, because if the peer doesn't hear about the double-spend
+ // from us they are very likely to hear about it from another peer, since
+ // each peer uses a different, randomized bloom filter.
+
+ if (fInBlock || filter.contains(outPoint)) return false;
+
+ // Apply an independent rate limit to double-spend relays
+ static double dRespendCount;
+ static int64_t nLastRespendTime;
+ static int64_t nRespendLimit = GetArg("-limitrespendrelay", 100);
+ unsigned int nSize = ::GetSerializeSize(doubleSpend, SER_NETWORK, PROTOCOL_VERSION);
+
+ if (RateLimitExceeded(dRespendCount, nLastRespendTime, nRespendLimit, nSize))
+ {
+ LogPrint("mempool", "Double-spend relay rejected by rate limiter\n");
+ return false;
+ }
+
+ LogPrint("mempool", "Rate limit dRespendCount: %g => %g\n", dRespendCount, dRespendCount+nSize);
+
+ // Clear the filter on average every MAX_DOUBLE_SPEND_BLOOM
+ // insertions
+ if (insecure_rand()%MAX_DOUBLESPEND_BLOOM == 0)
+ filter.clear();
+
+ filter.insert(outPoint);
+
+ return true;
+}
+
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
bool* pfMissingInputs, bool fRejectInsaneFee)
{
@@ -936,6 +959,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
return false;
// Check for conflicts with in-memory transactions
+ bool relayableRespend = false;
{
LOCK(pool.cs); // protect pool.mapNextTx
for (unsigned int i = 0; i < tx.vin.size(); i++)
@@ -944,8 +968,9 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// Does tx conflict with a member of the pool, and is it not equivalent to that member?
if (pool.mapNextTx.count(outpoint) && !tx.IsEquivalentTo(*pool.mapNextTx[outpoint].ptx))
{
- g_signals.DetectedDoubleSpend(outpoint, tx, false);
- return false;
+ relayableRespend = RelayableRespend(outpoint, tx, false, doubleSpendFilter);
+ if (!relayableRespend)
+ return false;
}
}
}
@@ -1005,7 +1030,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
unsigned int nSize = entry.GetTxSize();
// Don't accept it if it can't get into a block
- int64_t txMinFee = GetMinFee(tx, nSize, true, GMF_RELAY);
+ int64_t txMinFee = GetMinRelayFee(tx, nSize, true);
if (fLimitFree && nFees < txMinFee)
return state.DoS(0, error("AcceptToMemoryPool : not enough fees %s, %d < %d",
hash.ToString(), nFees, txMinFee),
@@ -1014,7 +1039,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// 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::minRelayTxFee.GetFee(nSize))
+ if (fLimitFree && nFees < ::minRelayTxFee.GetFee(nSize))
{
static double dFreeCount;
static int64_t nLastFreeTime;
@@ -1027,10 +1052,10 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize);
}
- if (fRejectInsaneFee && nFees > CTransaction::minRelayTxFee.GetFee(nSize) * 10000)
+ if (fRejectInsaneFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000)
return error("AcceptToMemoryPool: : insane fees %s, %d > %d",
hash.ToString(),
- nFees, CTransaction::minRelayTxFee.GetFee(nSize) * 10000);
+ nFees, ::minRelayTxFee.GetFee(nSize) * 10000);
// Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
@@ -1038,55 +1063,21 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
{
return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString());
}
- // Store transaction in memory
- pool.addUnchecked(hash, entry);
- }
-
- g_signals.SyncTransaction(tx, NULL);
-
- return true;
-}
-
-static void RelayDoubleSpend(const COutPoint& outPoint, const CTransaction& doubleSpend, bool fInBlock, CBloomFilter& filter)
-{
- // Relaying double-spend attempts to our peers lets them detect when
- // somebody might be trying to cheat them. However, blindly relaying
- // every double-spend across the entire network gives attackers
- // a denial-of-service attack: just generate a stream of double-spends
- // re-spending the same (limited) set of outpoints owned by the attacker.
- // So, we use a bloom filter and only relay (at most) the first double
- // spend for each outpoint. False-positives ("we have already relayed")
- // are OK, because if the peer doesn't hear about the double-spend
- // from us they are very likely to hear about it from another peer, since
- // each peer uses a different, randomized bloom filter.
-
- if (fInBlock || filter.contains(outPoint)) return;
-
- // Apply an independent rate limit to double-spend relays
- static double dRespendCount;
- static int64_t nLastRespendTime;
- static int64_t nRespendLimit = GetArg("-limitrespendrelay", 100);
- unsigned int nSize = ::GetSerializeSize(doubleSpend, SER_NETWORK, PROTOCOL_VERSION);
- if (RateLimitExceeded(dRespendCount, nLastRespendTime, nRespendLimit, nSize))
- {
- LogPrint("mempool", "Double-spend relay rejected by rate limiter\n");
- return;
+ if (relayableRespend)
+ {
+ RelayTransaction(tx);
+ }
+ else
+ {
+ // Store transaction in memory
+ pool.addUnchecked(hash, entry);
+ }
}
- LogPrint("mempool", "Rate limit dRespendCount: %g => %g\n", dRespendCount, dRespendCount+nSize);
-
- // Clear the filter on average every MAX_DOUBLE_SPEND_BLOOM
- // insertions
- if (insecure_rand()%MAX_DOUBLESPEND_BLOOM == 0)
- filter.clear();
-
- filter.insert(outPoint);
-
- RelayTransaction(doubleSpend);
+ g_signals.SyncTransaction(tx, NULL);
- // Share conflict with wallet
- g_signals.SyncTransaction(doubleSpend, NULL);
+ return !relayableRespend;
}
@@ -1134,10 +1125,10 @@ int CMerkleTx::GetBlocksToMaturity() const
}
-bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree)
+bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectInsaneFee)
{
CValidationState state;
- return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL);
+ return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, fRejectInsaneFee);
}
@@ -3544,10 +3535,10 @@ void static ProcessGetData(CNode* pfrom)
}
}
-bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
+bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived)
{
RandAddSeedPerfmon();
- LogPrint("net", "received: %s (%u bytes)\n", strCommand, vRecv.size());
+ LogPrint("net", "received: %s (%u bytes) peer=%d\n", strCommand, vRecv.size(), pfrom->id);
if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0)
{
LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n");
@@ -3579,7 +3570,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
if (pfrom->nVersion < MIN_PEER_PROTO_VERSION)
{
// disconnect from peers older than this proto version
- LogPrintf("partner %s using obsolete version %i; disconnecting\n", pfrom->addr.ToString(), pfrom->nVersion);
+ LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion);
pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE,
strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION));
pfrom->fDisconnect = true;
@@ -3660,7 +3651,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
pfrom->fSuccessfullyConnected = true;
- LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", pfrom->cleanSubVer, pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString(), addrFrom.ToString(), pfrom->addr.ToString());
+ LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, peer=%d\n", pfrom->cleanSubVer, pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString(), pfrom->id);
AddTimeData(pfrom->addr, nTime);
}
@@ -3767,7 +3758,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
pfrom->AddInventoryKnown(inv);
bool fAlreadyHave = AlreadyHave(inv);
- LogPrint("net", " got inventory: %s %s\n", inv.ToString(), fAlreadyHave ? "have" : "new");
+ LogPrint("net", "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->id);
if (!fAlreadyHave) {
if (!fImporting && !fReindex) {
@@ -3800,10 +3791,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
}
if (fDebug || (vInv.size() != 1))
- LogPrint("net", "received getdata (%u invsz)\n", vInv.size());
+ LogPrint("net", "received getdata (%u invsz) peer=%d\n", vInv.size(), pfrom->id);
if ((fDebug && vInv.size() > 0) || (vInv.size() == 1))
- LogPrint("net", "received getdata for: %s\n", vInv[0].ToString());
+ LogPrint("net", "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->id);
pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end());
ProcessGetData(pfrom);
@@ -3825,7 +3816,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
if (pindex)
pindex = chainActive.Next(pindex);
int nLimit = 500;
- LogPrint("net", "getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop==uint256(0) ? "end" : hashStop.ToString(), nLimit);
+ LogPrint("net", "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop==uint256(0) ? "end" : hashStop.ToString(), nLimit, pfrom->id);
for (; pindex; pindex = chainActive.Next(pindex))
{
if (pindex->GetBlockHash() == hashStop)
@@ -3908,8 +3899,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
vEraseQueue.push_back(inv.hash);
- LogPrint("mempool", "AcceptToMemoryPool: %s %s : accepted %s (poolsz %u)\n",
- pfrom->addr.ToString(), pfrom->cleanSubVer,
+ LogPrint("mempool", "AcceptToMemoryPool: peer=%d %s : accepted %s (poolsz %u)\n",
+ pfrom->id, pfrom->cleanSubVer,
tx.GetHash().ToString(),
mempool.mapTx.size());
@@ -3962,8 +3953,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
int nDoS = 0;
if (state.IsInvalid(nDoS))
{
- LogPrint("mempool", "%s from %s %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(),
- pfrom->addr.ToString(), pfrom->cleanSubVer,
+ LogPrint("mempool", "%s from peer=%d %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(),
+ pfrom->id, pfrom->cleanSubVer,
state.GetRejectReason());
pfrom->PushMessage("reject", strCommand, state.GetRejectCode(),
state.GetRejectReason(), inv.hash);
@@ -3978,7 +3969,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
CBlock block;
vRecv >> block;
- LogPrint("net", "received block %s\n", block.GetHash().ToString());
+ LogPrint("net", "received block %s peer=%d\n", block.GetHash().ToString(), pfrom->id);
// block.print();
CInv inv(MSG_BLOCK, block.GetHash());
@@ -4054,7 +4045,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
else if (strCommand == "pong")
{
- int64_t pingUsecEnd = GetTimeMicros();
+ int64_t pingUsecEnd = nTimeReceived;
uint64_t nonce = 0;
size_t nAvail = vRecv.in_avail();
bool bPingFinished = false;
@@ -4095,8 +4086,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
}
if (!(sProblem.empty())) {
- LogPrint("net", "pong %s %s: %s, %x expected, %x received, %u bytes\n",
- pfrom->addr.ToString(),
+ LogPrint("net", "pong peer=%d %s: %s, %x expected, %x received, %u bytes\n",
+ pfrom->id,
pfrom->cleanSubVer,
sProblem,
pfrom->nPingNonceSent,
@@ -4305,7 +4296,7 @@ bool ProcessMessages(CNode* pfrom)
bool fRet = false;
try
{
- fRet = ProcessMessage(pfrom, strCommand, vRecv);
+ fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime);
boost::this_thread::interruption_point();
}
catch (std::ios_base::failure& e)
@@ -4336,7 +4327,7 @@ bool ProcessMessages(CNode* pfrom)
}
if (!fRet)
- LogPrintf("ProcessMessage(%s, %u bytes) FAILED\n", strCommand, nMessageSize);
+ LogPrintf("ProcessMessage(%s, %u bytes) FAILED peer=%d\n", strCommand, nMessageSize, pfrom->id);
break;
}
@@ -4540,7 +4531,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
uint256 hash = state.vBlocksToDownload.front();
vGetData.push_back(CInv(MSG_BLOCK, hash));
MarkBlockAsInFlight(pto->GetId(), hash);
- LogPrint("net", "Requesting block %s from %s\n", hash.ToString(), state.name);
+ LogPrint("net", "Requesting block %s peer=%d\n", hash.ToString(), pto->id);
if (vGetData.size() >= 1000)
{
pto->PushMessage("getdata", vGetData);
@@ -4557,7 +4548,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
if (!AlreadyHave(inv))
{
if (fDebug)
- LogPrint("net", "sending getdata: %s\n", inv.ToString());
+ LogPrint("net", "Requesting %s peer=%d\n", inv.ToString(), pto->id);
vGetData.push_back(inv);
if (vGetData.size() >= 1000)
{
diff --git a/src/main.h b/src/main.h
index cc7dea75a1..f6bac889be 100644
--- a/src/main.h
+++ b/src/main.h
@@ -93,6 +93,7 @@ extern bool fBenchmark;
extern int nScriptCheckThreads;
extern bool fTxIndex;
extern unsigned int nCoinCacheSize;
+extern CFeeRate minRelayTxFee;
// Minimum disk space required - used in CheckDiskSpace()
static const uint64_t nMinDiskSpace = 52428800;
@@ -108,8 +109,8 @@ struct CNodeStateStats;
struct CBlockTemplate;
-/** Set up internal signal handlers **/
-void RegisterInternalSignals();
+/** Initialize respend bloom filter **/
+void InitRespendFilter();
/** Register a wallet to receive updates from core */
void RegisterWallet(CWalletInterface* pwalletIn);
@@ -245,14 +246,7 @@ struct CDiskTxPos : public CDiskBlockPos
};
-
-enum GetMinFee_mode
-{
- GMF_RELAY,
- GMF_SEND,
-};
-
-int64_t GetMinFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree, enum GetMinFee_mode mode);
+int64_t GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree);
//
// Check transaction inputs, and make sure any
@@ -459,7 +453,7 @@ public:
int GetDepthInMainChain() const { CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); }
bool IsInMainChain() const { CBlockIndex *pindexRet; return GetDepthInMainChainINTERNAL(pindexRet) > 0; }
int GetBlocksToMaturity() const;
- bool AcceptToMemoryPool(bool fLimitFree=true);
+ bool AcceptToMemoryPool(bool fLimitFree=true, bool fRejectInsaneFee=true);
};
diff --git a/src/miner.cpp b/src/miner.cpp
index 69e53756e0..17918a1280 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -236,7 +236,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
double dPriorityDelta = 0;
int64_t nFeeDelta = 0;
mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta);
- if (fSortedByFee && (dPriorityDelta <= 0) && (nFeeDelta <= 0) && (feeRate < CTransaction::minRelayTxFee) && (nBlockSize + nTxSize >= nBlockMinSize))
+ if (fSortedByFee && (dPriorityDelta <= 0) && (nFeeDelta <= 0) && (feeRate < ::minRelayTxFee) && (nBlockSize + nTxSize >= nBlockMinSize))
continue;
// Prioritise by fee once past the priority size or we run out of high-priority
diff --git a/src/net.cpp b/src/net.cpp
index 934c45ca4c..6a660dc9bd 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -491,8 +491,6 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest)
{
addrman.Attempt(addrConnect);
- LogPrint("net", "connected %s\n", pszDest ? pszDest : addrConnect.ToString());
-
// Set to non-blocking
#ifdef WIN32
u_long nOne = 1;
@@ -513,6 +511,7 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest)
}
pnode->nTimeConnected = GetTime();
+
return pnode;
}
@@ -524,7 +523,7 @@ void CNode::CloseSocketDisconnect()
fDisconnect = true;
if (hSocket != INVALID_SOCKET)
{
- LogPrint("net", "disconnecting node %s\n", addrName);
+ LogPrint("net", "disconnecting peer=%d\n", id);
closesocket(hSocket);
hSocket = INVALID_SOCKET;
}
@@ -548,7 +547,10 @@ void CNode::PushVersion()
CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0",0)));
CAddress addrMe = GetLocalAddress(&addr);
RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce));
- LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), addr.ToString());
+ if (fLogIPs)
+ LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), id);
+ else
+ LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), id);
PushMessage("version", PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe,
nLocalHostNonce, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<string>()), nBestHeight, true);
}
@@ -653,6 +655,9 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes)
pch += handled;
nBytes -= handled;
+
+ if (msg.complete())
+ msg.nTime = GetTimeMicros();
}
return true;
@@ -950,7 +955,6 @@ void ThreadSocketHandler()
}
else
{
- LogPrint("net", "accepted connection %s\n", addr.ToString());
CNode* pnode = new CNode(hSocket, addr, "", true);
pnode->AddRef();
@@ -1040,7 +1044,7 @@ void ThreadSocketHandler()
{
if (pnode->nLastRecv == 0 || pnode->nLastSend == 0)
{
- LogPrint("net", "socket no message in first 60 seconds, %d %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0);
+ LogPrint("net", "socket no message in first 60 seconds, %d %d from %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0, pnode->id);
pnode->fDisconnect = true;
}
else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL)
diff --git a/src/net.h b/src/net.h
index 2ee798d468..c2a0416455 100644
--- a/src/net.h
+++ b/src/net.h
@@ -173,11 +173,14 @@ public:
CDataStream vRecv; // received message data
unsigned int nDataPos;
+ int64_t nTime; // time (in microseconds) of message receipt.
+
CNetMessage(int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), vRecv(nTypeIn, nVersionIn) {
hdrbuf.resize(24);
in_data = false;
nHdrPos = 0;
nDataPos = 0;
+ nTime = 0;
}
bool complete() const
@@ -330,6 +333,11 @@ public:
id = nLastNodeId++;
}
+ if (fLogIPs)
+ LogPrint("net", "Added connection to %s peer=%d\n", addrName, id);
+ else
+ LogPrint("net", "Added connection peer=%d\n", id);
+
// Be shy and don't send version until we hear
if (hSocket != INVALID_SOCKET && !fInbound)
PushVersion();
@@ -446,7 +454,7 @@ public:
nRequestTime = it->second;
else
nRequestTime = 0;
- LogPrint("net", "askfor %s %d (%s)\n", inv.ToString(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000).c_str());
+ LogPrint("net", "askfor %s %d (%s) peer=%d\n", inv.ToString(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000).c_str(), id);
// Make sure not to reuse time indexes to keep things in the same order
int64_t nNow = GetTimeMicros() - 1000000;
@@ -514,7 +522,7 @@ public:
assert(ssSend.size () >= CMessageHeader::CHECKSUM_OFFSET + sizeof(nChecksum));
memcpy((char*)&ssSend[CMessageHeader::CHECKSUM_OFFSET], &nChecksum, sizeof(nChecksum));
- LogPrint("net", "(%d bytes)\n", nSize);
+ LogPrint("net", "(%d bytes) peer=%d\n", nSize, id);
std::deque<CSerializeData>::iterator it = vSendMsg.insert(vSendMsg.end(), CSerializeData());
ssSend.GetAndClear(*it);
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 3c50174e75..f47b0f3563 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -3,6 +3,18 @@
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#ifdef HAVE_CONFIG_H
+#include "bitcoin-config.h"
+#endif
+
+#ifdef HAVE_INET_PTON
+#include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_GETADDRINFO_A
+#include <netdb.h>
+#endif
+
#include "netbase.h"
#include "hash.h"
@@ -71,9 +83,30 @@ bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsign
}
}
+#ifdef HAVE_GETADDRINFO_A
+ struct in_addr ipv4_addr;
+#ifdef HAVE_INET_PTON
+ if (inet_pton(AF_INET, pszName, &ipv4_addr) > 0) {
+ vIP.push_back(CNetAddr(ipv4_addr));
+ return true;
+ }
+
+ struct in6_addr ipv6_addr;
+ if (inet_pton(AF_INET6, pszName, &ipv6_addr) > 0) {
+ vIP.push_back(CNetAddr(ipv6_addr));
+ return true;
+ }
+#else
+ ipv4_addr.s_addr = inet_addr(pszName);
+ if (ipv4_addr.s_addr != INADDR_NONE) {
+ vIP.push_back(CNetAddr(ipv4_addr));
+ return true;
+ }
+#endif
+#endif
+
struct addrinfo aiHint;
memset(&aiHint, 0, sizeof(struct addrinfo));
-
aiHint.ai_socktype = SOCK_STREAM;
aiHint.ai_protocol = IPPROTO_TCP;
aiHint.ai_family = AF_UNSPEC;
@@ -82,8 +115,33 @@ bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsign
#else
aiHint.ai_flags = fAllowLookup ? AI_ADDRCONFIG : AI_NUMERICHOST;
#endif
+
struct addrinfo *aiRes = NULL;
+#ifdef HAVE_GETADDRINFO_A
+ struct gaicb gcb, *query = &gcb;
+ memset(query, 0, sizeof(struct gaicb));
+ gcb.ar_name = pszName;
+ gcb.ar_request = &aiHint;
+ int nErr = getaddrinfo_a(GAI_NOWAIT, &query, 1, NULL);
+ if (nErr)
+ return false;
+
+ do {
+ // Should set the timeout limit to a resonable value to avoid
+ // generating unnecessary checking call during the polling loop,
+ // while it can still response to stop request quick enough.
+ // 2 seconds looks fine in our situation.
+ struct timespec ts = { 2, 0 };
+ gai_suspend(&query, 1, &ts);
+ boost::this_thread::interruption_point();
+
+ nErr = gai_error(query);
+ if (0 == nErr)
+ aiRes = query->ar_result;
+ } while (nErr == EAI_INPROGRESS);
+#else
int nErr = getaddrinfo(pszName, NULL, &aiHint, &aiRes);
+#endif
if (nErr)
return false;
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 30f5ec8939..f2fb8c877e 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -28,6 +28,7 @@
#include <iostream>
+#include <QAction>
#include <QApplication>
#include <QDateTime>
#include <QDesktopWidget>
@@ -39,6 +40,7 @@
#include <QMenuBar>
#include <QMessageBox>
#include <QMimeData>
+#include <QPoint>
#include <QProgressBar>
#include <QProgressDialog>
#include <QSettings>
@@ -49,6 +51,8 @@
#include <QToolBar>
#include <QVBoxLayout>
+
+
#if QT_VERSION < 0x050000
#include <QUrl>
#include <QTextDocument>
@@ -156,10 +160,13 @@ BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) :
QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks);
frameBlocksLayout->setContentsMargins(3,0,3,0);
frameBlocksLayout->setSpacing(3);
+ unitDisplayControl = new UnitDisplayStatusBarControl();
labelEncryptionIcon = new QLabel();
labelConnectionsIcon = new QLabel();
labelBlocksIcon = new QLabel();
frameBlocksLayout->addStretch();
+ frameBlocksLayout->addWidget(unitDisplayControl);
+ frameBlocksLayout->addStretch();
frameBlocksLayout->addWidget(labelEncryptionIcon);
frameBlocksLayout->addStretch();
frameBlocksLayout->addWidget(labelConnectionsIcon);
@@ -420,6 +427,8 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel)
walletFrame->setClientModel(clientModel);
}
#endif
+
+ this->unitDisplayControl->setOptionsModel(clientModel->getOptionsModel());
}
}
@@ -1000,3 +1009,72 @@ void BitcoinGUI::unsubscribeFromCoreSignals()
// Disconnect signals from client
uiInterface.ThreadSafeMessageBox.disconnect(boost::bind(ThreadSafeMessageBox, this, _1, _2, _3));
}
+
+UnitDisplayStatusBarControl::UnitDisplayStatusBarControl():QLabel()
+{
+ optionsModel = 0;
+ createContextMenu();
+ setStyleSheet("font:11pt; color: #333333");
+ setToolTip(tr("Unit to show amounts in. Click to select another unit."));
+}
+
+/** So that it responds to left-button clicks */
+void UnitDisplayStatusBarControl::mousePressEvent(QMouseEvent *event)
+{
+ onDisplayUnitsClicked(event->pos());
+}
+
+/** Creates context menu, its actions, and wires up all the relevant signals for mouse events. */
+void UnitDisplayStatusBarControl::createContextMenu()
+{
+ menu = new QMenu();
+ foreach(BitcoinUnits::Unit u, BitcoinUnits::availableUnits())
+ {
+ QAction *menuAction = new QAction(QString(BitcoinUnits::name(u)), this);
+ menuAction->setData(QVariant(u));
+ menu->addAction(menuAction);
+ }
+ connect(menu,SIGNAL(triggered(QAction*)),this,SLOT(onMenuSelection(QAction*)));
+
+ // what happens on right click.
+ setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(this,SIGNAL(customContextMenuRequested(const QPoint&)),this,SLOT(onDisplayUnitsClicked(const QPoint&)));
+}
+
+/** Lets the control know about the Options Model (and its signals) */
+void UnitDisplayStatusBarControl::setOptionsModel(OptionsModel *optionsModel)
+{
+ if (optionsModel)
+ {
+ this->optionsModel = optionsModel;
+
+ // be aware of a display unit change reported by the OptionsModel object.
+ connect(optionsModel,SIGNAL(displayUnitChanged(int)),this,SLOT(updateDisplayUnit(int)));
+
+ // initialize the display units label with the current value in the model.
+ updateDisplayUnit(optionsModel->getDisplayUnit());
+ }
+}
+
+/** When Display Units are changed on OptionsModel it will refresh the display text of the control on the status bar */
+void UnitDisplayStatusBarControl::updateDisplayUnit(int newUnits)
+{
+ setText(BitcoinUnits::name(newUnits));
+}
+
+/** Shows context menu with Display Unit options by the mouse coordinates */
+void UnitDisplayStatusBarControl::onDisplayUnitsClicked(const QPoint& point)
+{
+ QPoint globalPos = mapToGlobal(point);
+ menu->exec(globalPos);
+}
+
+/** Tells underlying optionsModel to update its current display unit. */
+void UnitDisplayStatusBarControl::onMenuSelection(QAction* action)
+{
+ if (action)
+ {
+ optionsModel->setDisplayUnit(action->data());
+ }
+}
+
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index e7a842df99..705e629a69 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -9,12 +9,16 @@
#include "config/bitcoin-config.h"
#endif
+#include <QLabel>
#include <QMainWindow>
#include <QMap>
+#include <QMenu>
+#include <QPoint>
#include <QSystemTrayIcon>
class ClientModel;
class Notificator;
+class OptionsModel;
class RPCConsole;
class SendCoinsRecipient;
class WalletFrame;
@@ -22,9 +26,13 @@ class WalletModel;
class CWallet;
+class UnitDisplayStatusBarControl;
+
QT_BEGIN_NAMESPACE
class QAction;
class QLabel;
+class QMenu;
+class QPoint;
class QProgressBar;
class QProgressDialog;
QT_END_NAMESPACE
@@ -69,6 +77,7 @@ private:
ClientModel *clientModel;
WalletFrame *walletFrame;
+ UnitDisplayStatusBarControl *unitDisplayControl;
QLabel *labelEncryptionIcon;
QLabel *labelConnectionsIcon;
QLabel *labelBlocksIcon;
@@ -198,4 +207,32 @@ private slots:
void showProgress(const QString &title, int nProgress);
};
+class UnitDisplayStatusBarControl : public QLabel
+{
+ Q_OBJECT
+
+public:
+ explicit UnitDisplayStatusBarControl();
+ /** Lets the control know about the Options Model (and its signals) */
+ void setOptionsModel(OptionsModel *optionsModel);
+
+protected:
+ /** So that it responds to left-button clicks */
+ void mousePressEvent(QMouseEvent *event);
+
+private:
+ OptionsModel *optionsModel;
+ QMenu* menu;
+ /** Shows context menu with Display Unit options by the mouse coordinates */
+ void onDisplayUnitsClicked(const QPoint& point);
+ /** Creates context menu, its actions, and wires up all the relevant signals for mouse events. */
+ void createContextMenu();
+
+private slots:
+ /** When Display Units are changed on OptionsModel it will refresh the display text of the control on the status bar */
+ void updateDisplayUnit(int newUnits);
+ /** Tells underlying optionsModel to update its current display unit. */
+ void onMenuSelection(QAction* action);
+};
+
#endif // BITCOINGUI_H
diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp
index 2fed443cf2..4ba6aba551 100644
--- a/src/qt/bitcoinunits.cpp
+++ b/src/qt/bitcoinunits.cpp
@@ -169,6 +169,16 @@ bool BitcoinUnits::parse(int unit, const QString &value, qint64 *val_out)
return ok;
}
+QString BitcoinUnits::getAmountColumnTitle(int unit)
+{
+ QString amountTitle = QObject::tr("Amount");
+ if (BitcoinUnits::valid(unit))
+ {
+ amountTitle += " ("+BitcoinUnits::name(unit) + ")";
+ }
+ return amountTitle;
+}
+
int BitcoinUnits::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h
index 46517fc07b..451b52ee21 100644
--- a/src/qt/bitcoinunits.h
+++ b/src/qt/bitcoinunits.h
@@ -54,6 +54,8 @@ public:
static QString formatWithUnit(int unit, qint64 amount, bool plussign=false);
//! Parse string to coin amount
static bool parse(int unit, const QString &value, qint64 *val_out);
+ //! Gets title for amount column including current display unit if optionsModel reference available */
+ static QString getAmountColumnTitle(int unit);
///@}
//! @name AbstractListModel implementation
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index c6a6150392..e0a524a55e 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -16,6 +16,8 @@
#include "main.h"
#include "wallet.h"
+#include <boost/assign/list_of.hpp> // for 'map_list_of()'
+
#include <QApplication>
#include <QCheckBox>
#include <QCursor>
@@ -400,23 +402,24 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column)
}
// return human readable label for priority number
-QString CoinControlDialog::getPriorityLabel(double dPriority)
+QString CoinControlDialog::getPriorityLabel(const CTxMemPool& pool, double dPriority)
{
- if (AllowFree(dPriority)) // at least medium
+ // confirmations -> textual description
+ typedef std::map<unsigned int, QString> PriorityDescription;
+ const static PriorityDescription priorityDescriptions = boost::assign::map_list_of
+ (1, tr("highest"))(2, tr("higher"))(3, tr("high"))
+ (5, tr("medium-high"))(6, tr("medium"))
+ (10, tr("low-medium"))(15, tr("low"))
+ (20, tr("lower"));
+
+ BOOST_FOREACH(const PriorityDescription::value_type& i, priorityDescriptions)
{
- if (AllowFree(dPriority / 1000000)) return tr("highest");
- else if (AllowFree(dPriority / 100000)) return tr("higher");
- else if (AllowFree(dPriority / 10000)) return tr("high");
- else if (AllowFree(dPriority / 1000)) return tr("medium-high");
- else return tr("medium");
- }
- else
- {
- if (AllowFree(dPriority * 10)) return tr("low-medium");
- else if (AllowFree(dPriority * 100)) return tr("low");
- else if (AllowFree(dPriority * 1000)) return tr("lower");
- else return tr("lowest");
+ double p = mempool.estimatePriority(i.first);
+ if (p > 0 && dPriority >= p) return i.second;
}
+ // Note: if mempool hasn't accumulated enough history (estimatePriority
+ // returns -1) we're conservative and classify as "lowest"
+ return tr("lowest");
}
// shows count of locked unspent outputs
@@ -449,7 +452,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::minRelayTxFee))
+ if (txout.IsDust(::minRelayTxFee))
fDust = true;
}
}
@@ -518,15 +521,20 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
// Priority
dPriority = dPriorityInputs / (nBytes - nBytesInputs + (nQuantityUncompressed * 29)); // 29 = 180 - 151 (uncompressed public keys are over the limit. max 151 bytes of the input are ignored for priority)
- sPriorityLabel = CoinControlDialog::getPriorityLabel(dPriority);
+ sPriorityLabel = CoinControlDialog::getPriorityLabel(mempool, dPriority);
// Fee
int64_t nFee = payTxFee.GetFee(max((unsigned int)1000, nBytes));
// Min Fee
- int64_t nMinFee = GetMinFee(txDummy, nBytes, AllowFree(dPriority), GMF_SEND);
+ nPayFee = CWallet::GetMinimumFee(nBytes, nTxConfirmTarget, mempool);
+
+ double dPriorityNeeded = mempool.estimatePriority(nTxConfirmTarget);
+ if (dPriorityNeeded <= 0) // Not enough mempool history: never send free
+ dPriorityNeeded = std::numeric_limits<double>::max();
- nPayFee = max(nFee, nMinFee);
+ if (nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE && dPriority >= dPriorityNeeded)
+ nPayFee = 0;
if (nPayAmount > 0)
{
@@ -536,7 +544,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
if (nChange > 0 && nChange < CENT)
{
CTxOut txout(nChange, (CScript)vector<unsigned char>(24, 0));
- if (txout.IsDust(CTransaction::minRelayTxFee))
+ if (txout.IsDust(::minRelayTxFee))
{
nPayFee += nChange;
nChange = 0;
@@ -591,23 +599,23 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
}
// turn labels "red"
- l5->setStyleSheet((nBytes >= 1000) ? "color:red;" : ""); // Bytes >= 1000
+ l5->setStyleSheet((nBytes >= MAX_FREE_TRANSACTION_CREATE_SIZE) ? "color:red;" : "");// Bytes >= 1000
l6->setStyleSheet((dPriority > 0 && !AllowFree(dPriority)) ? "color:red;" : ""); // Priority < "medium"
l7->setStyleSheet((fDust) ? "color:red;" : ""); // Dust = "yes"
// 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::minTxFee.GetFeePerK())) + "<br /><br />";
+ toolTip1 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CWallet::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::minTxFee.GetFeePerK()));
+ toolTip2 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CWallet::minTxFee.GetFeePerK()));
- QString toolTip3 = tr("This label turns red, if any recipient receives an amount smaller than %1.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::minRelayTxFee.GetFee(546)));
+ QString toolTip3 = tr("This label turns red, if any recipient receives an amount smaller than %1.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, ::minRelayTxFee.GetFee(546)));
// how many satoshis the estimated fee can vary per byte we guess wrong
- double dFeeVary = (double)std::max(CTransaction::minTxFee.GetFeePerK(), payTxFee.GetFeePerK()) / 1000;
+ double dFeeVary = (double)std::max(CWallet::minTxFee.GetFeePerK(), payTxFee.GetFeePerK()) / 1000;
QString toolTip4 = tr("Can vary +/- %1 satoshi(s) per input.").arg(dFeeVary);
l3->setToolTip(toolTip4);
@@ -732,7 +740,7 @@ void CoinControlDialog::updateView()
// priority
double dPriority = ((double)out.tx->vout[out.i].nValue / (nInputSize + 78)) * (out.nDepth+1); // 78 = 2 * 34 + 10
- itemOutput->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPriority));
+ itemOutput->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(mempool, dPriority));
itemOutput->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64_t)dPriority), 20, " "));
dPrioritySum += (double)out.tx->vout[out.i].nValue * (out.nDepth+1);
nInputSum += nInputSize;
@@ -765,7 +773,7 @@ void CoinControlDialog::updateView()
itemWalletAddress->setText(COLUMN_CHECKBOX, "(" + QString::number(nChildren) + ")");
itemWalletAddress->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum));
itemWalletAddress->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(nSum), 15, " "));
- itemWalletAddress->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPrioritySum));
+ itemWalletAddress->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(mempool, dPrioritySum));
itemWalletAddress->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64_t)dPrioritySum), 20, " "));
}
}
diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h
index 465e2a009d..4f7422642f 100644
--- a/src/qt/coincontroldialog.h
+++ b/src/qt/coincontroldialog.h
@@ -19,6 +19,7 @@ namespace Ui {
}
class WalletModel;
class CCoinControl;
+class CTxMemPool;
class CoinControlDialog : public QDialog
{
@@ -32,7 +33,7 @@ public:
// static because also called from sendcoinsdialog
static void updateLabels(WalletModel*, QDialog*);
- static QString getPriorityLabel(double);
+ static QString getPriorityLabel(const CTxMemPool& pool, double);
static QList<qint64> payAmounts;
static CCoinControl *coinControl;
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 81b9054252..60a131df7e 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -11,6 +11,7 @@
#include "core.h"
#include "init.h"
+#include "main.h"
#include "protocol.h"
#include "util.h"
@@ -212,7 +213,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::minRelayTxFee);
+ return txOut.IsDust(::minRelayTxFee);
}
QString HtmlEscape(const QString& str, bool fMultiLine)
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 12d54dff64..9502dba904 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -14,7 +14,10 @@
#include "monitoreddatamapper.h"
#include "optionsmodel.h"
-#include "main.h" // for CTransaction::minTxFee and MAX_SCRIPTCHECK_THREADS
+#include "main.h" // for MAX_SCRIPTCHECK_THREADS
+#ifdef ENABLE_WALLET
+#include "wallet.h" // for CWallet::minTxFee
+#endif
#include "netbase.h"
#include "txdb.h" // for -dbcache defaults
@@ -101,7 +104,9 @@ OptionsDialog::OptionsDialog(QWidget *parent) :
#endif
ui->unit->setModel(new BitcoinUnits(this));
- ui->transactionFee->setSingleStep(CTransaction::minTxFee.GetFeePerK());
+#ifdef ENABLE_WALLET
+ ui->transactionFee->setSingleStep(CWallet::minTxFee.GetFeePerK());
+#endif
/* Widget-to-option mapper */
mapper = new MonitoredDataMapper(this);
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index 6d985abaf8..09553a2587 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -308,9 +308,7 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
break;
#endif
case DisplayUnit:
- nDisplayUnit = value.toInt();
- settings.setValue("nDisplayUnit", nDisplayUnit);
- emit displayUnitChanged(nDisplayUnit);
+ setDisplayUnit(value);
break;
case DisplayAddresses:
bDisplayAddresses = value.toBool();
@@ -356,11 +354,24 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
break;
}
}
+
emit dataChanged(index, index);
return successful;
}
+/** Updates current unit in memory, settings and emits displayUnitChanged(newUnit) signal */
+void OptionsModel::setDisplayUnit(const QVariant &value)
+{
+ if (!value.isNull())
+ {
+ QSettings settings;
+ nDisplayUnit = value.toInt();
+ settings.setValue("nDisplayUnit", nDisplayUnit);
+ emit displayUnitChanged(nDisplayUnit);
+ }
+}
+
bool OptionsModel::getProxySettings(QNetworkProxy& proxy) const
{
// Directly query current base proxy, because
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index 2596682d07..89c2ec7453 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -52,6 +52,8 @@ public:
int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole);
+ /** Updates current unit in memory, settings and emits displayUnitChanged(newUnit) signal */
+ void setDisplayUnit(const QVariant &value);
/* Explicit getters */
bool getMinimizeToTray() { return fMinimizeToTray; }
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index 6ca90f0513..53db2c5cd9 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -551,7 +551,7 @@ bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, SendCoins
// Extract and check amounts
CTxOut txOut(sendingTo.second, sendingTo.first);
- if (txOut.IsDust(CTransaction::minRelayTxFee)) {
+ if (txOut.IsDust(::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);
diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp
index 844d62518c..b5a998f9f5 100644
--- a/src/qt/recentrequeststablemodel.cpp
+++ b/src/qt/recentrequeststablemodel.cpp
@@ -21,7 +21,9 @@ RecentRequestsTableModel::RecentRequestsTableModel(CWallet *wallet, WalletModel
addNewRequest(request);
/* These columns must match the indices in the ColumnIndex enumeration */
- columns << tr("Date") << tr("Label") << tr("Message") << tr("Amount");
+ columns << tr("Date") << tr("Label") << tr("Message") << getAmountTitle();
+
+ connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
}
RecentRequestsTableModel::~RecentRequestsTableModel()
@@ -101,6 +103,24 @@ QVariant RecentRequestsTableModel::headerData(int section, Qt::Orientation orien
return QVariant();
}
+/** Updates the column title to "Amount (DisplayUnit)" and emits headerDataChanged() signal for table headers to react. */
+void RecentRequestsTableModel::updateAmountColumnTitle()
+{
+ columns[Amount] = getAmountTitle();
+ emit headerDataChanged(Qt::Horizontal,Amount,Amount);
+}
+
+/** Gets title for amount column including current display unit if optionsModel reference available. */
+QString RecentRequestsTableModel::getAmountTitle()
+{
+ QString amountTitle = tr("Amount");
+ if (this->walletModel->getOptionsModel() != NULL)
+ {
+ amountTitle += " ("+BitcoinUnits::name(this->walletModel->getOptionsModel()->getDisplayUnit()) + ")";
+ }
+ return amountTitle;
+}
+
QModelIndex RecentRequestsTableModel::index(int row, int column, const QModelIndex &parent) const
{
Q_UNUSED(parent);
@@ -185,6 +205,11 @@ void RecentRequestsTableModel::sort(int column, Qt::SortOrder order)
emit dataChanged(index(0, 0, QModelIndex()), index(list.size() - 1, NUMBER_OF_COLUMNS - 1, QModelIndex()));
}
+void RecentRequestsTableModel::updateDisplayUnit()
+{
+ updateAmountColumnTitle();
+}
+
bool RecentRequestEntryLessThan::operator()(RecentRequestEntry &left, RecentRequestEntry &right) const
{
RecentRequestEntry *pLeft = &left;
diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h
index d4cc5078aa..4f0b241259 100644
--- a/src/qt/recentrequeststablemodel.h
+++ b/src/qt/recentrequeststablemodel.h
@@ -91,12 +91,18 @@ public:
public slots:
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
+ void updateDisplayUnit();
private:
WalletModel *walletModel;
QStringList columns;
QList<RecentRequestEntry> list;
int64_t nReceiveRequestsMaxId;
+
+ /** Updates the column title to "Amount (DisplayUnit)" and emits headerDataChanged() signal for table headers to react. */
+ void updateAmountColumnTitle();
+ /** Gets title for amount column including current display unit if optionsModel reference available. */
+ QString getAmountTitle();
};
#endif
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index a935752246..6c1a6c3579 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -235,8 +235,7 @@ TransactionTableModel::TransactionTableModel(CWallet* wallet, WalletModel *paren
walletModel(parent),
priv(new TransactionTablePriv(wallet, this))
{
- columns << QString() << tr("Date") << tr("Type") << tr("Address") << tr("Amount");
-
+ columns << QString() << tr("Date") << tr("Type") << tr("Address") << BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit());
priv->refreshWallet();
connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
@@ -247,6 +246,13 @@ TransactionTableModel::~TransactionTableModel()
delete priv;
}
+/** Updates the column title to "Amount (DisplayUnit)" and emits headerDataChanged() signal for table headers to react. */
+void TransactionTableModel::updateAmountColumnTitle()
+{
+ columns[Amount] = BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit());
+ emit headerDataChanged(Qt::Horizontal,Amount,Amount);
+}
+
void TransactionTableModel::updateTransaction(const QString &hash, int status)
{
uint256 updated;
@@ -632,5 +638,6 @@ QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex
void TransactionTableModel::updateDisplayUnit()
{
// emit dataChanged to update Amount column with the current unit
+ updateAmountColumnTitle();
emit dataChanged(index(0, Amount), index(priv->size()-1, Amount));
}
diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h
index 333e6bc6ed..e8b6ed065d 100644
--- a/src/qt/transactiontablemodel.h
+++ b/src/qt/transactiontablemodel.h
@@ -87,6 +87,8 @@ public slots:
void updateTransaction(const QString &hash, int status);
void updateConfirmations();
void updateDisplayUnit();
+ /** Updates the column title to "Amount (DisplayUnit)" and emits headerDataChanged() signal for table headers to react. */
+ void updateAmountColumnTitle();
friend class TransactionTablePriv;
};
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index d4d29416ca..d6d210a561 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -309,7 +309,7 @@ void TransactionView::exportClicked()
writer.addColumn(tr("Type"), TransactionTableModel::Type, Qt::EditRole);
writer.addColumn(tr("Label"), 0, TransactionTableModel::LabelRole);
writer.addColumn(tr("Address"), 0, TransactionTableModel::AddressRole);
- writer.addColumn(tr("Amount"), 0, TransactionTableModel::FormattedAmountRole);
+ writer.addColumn(BitcoinUnits::getAmountColumnTitle(model->getOptionsModel()->getDisplayUnit()), 0, TransactionTableModel::FormattedAmountRole);
writer.addColumn(tr("ID"), 0, TransactionTableModel::TxIDRole);
if(!writer.write()) {
diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp
index 5b470516a1..9ed6b52513 100644
--- a/src/rpcmisc.cpp
+++ b/src/rpcmisc.cpp
@@ -84,7 +84,7 @@ Value getinfo(const Array& params, bool fHelp)
obj.push_back(Pair("unlocked_until", nWalletUnlockTime));
obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK())));
#endif
- obj.push_back(Pair("relayfee", ValueFromAmount(CTransaction::minRelayTxFee.GetFeePerK())));
+ obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())));
obj.push_back(Pair("errors", GetWarnings("statusbar")));
return obj;
}
diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp
index 2d7abb2d58..cd3bd59f8f 100644
--- a/src/rpcnet.cpp
+++ b/src/rpcnet.cpp
@@ -372,7 +372,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::minRelayTxFee.GetFeePerK())));
+ obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())));
Array localAddresses;
{
LOCK(cs_mapLocalHost);
diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp
index f47b3385da..5c4d307497 100644
--- a/src/rpcserver.cpp
+++ b/src/rpcserver.cpp
@@ -512,7 +512,7 @@ void StartRPCThreads()
if(!subnet.IsValid())
{
uiInterface.ThreadSafeMessageBox(
- strprintf("Invalid -rpcallowip subnet specification: %s", strAllow),
+ strprintf("Invalid -rpcallowip subnet specification: %s. Valid are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24).", strAllow),
"", CClientUIInterface::MSG_ERROR);
StartShutdown();
return;
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 97a426dd35..a852de5da8 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -80,13 +80,13 @@ public:
// Used as belt-and-suspenders check when reading to detect
// file corruption
- bool AreSane(const std::vector<CFeeRate>& vecFee)
+ bool AreSane(const std::vector<CFeeRate>& vecFee, const CFeeRate& minRelayFee)
{
BOOST_FOREACH(CFeeRate fee, vecFee)
{
if (fee < CFeeRate(0))
return false;
- if (fee.GetFee(1000) > CTransaction::minRelayTxFee.GetFee(1000) * 10000)
+ if (fee.GetFeePerK() > minRelayFee.GetFeePerK() * 10000)
return false;
}
return true;
@@ -109,10 +109,10 @@ public:
fileout << vecPriority;
}
- void Read(CAutoFile& filein) {
+ void Read(CAutoFile& filein, const CFeeRate& minRelayFee) {
std::vector<CFeeRate> vecFee;
filein >> vecFee;
- if (AreSane(vecFee))
+ if (AreSane(vecFee, minRelayFee))
feeSamples.insert(feeSamples.end(), vecFee.begin(), vecFee.end());
else
throw runtime_error("Corrupt fee value in estimates file.");
@@ -141,7 +141,7 @@ private:
// nBlocksAgo is 0 based, i.e. transactions that confirmed in the highest seen block are
// nBlocksAgo == 0, transactions in the block before that are nBlocksAgo == 1 etc.
- void seenTxConfirm(CFeeRate feeRate, double dPriority, int nBlocksAgo)
+ void seenTxConfirm(const CFeeRate& feeRate, const CFeeRate& minRelayFee, double dPriority, int nBlocksAgo)
{
// Last entry records "everything else".
int nBlocksTruncated = min(nBlocksAgo, (int) history.size() - 1);
@@ -149,7 +149,7 @@ private:
// We need to guess why the transaction was included in a block-- either
// because it is high-priority or because it has sufficient fees.
- bool sufficientFee = (feeRate > CTransaction::minRelayTxFee);
+ bool sufficientFee = (feeRate > minRelayFee);
bool sufficientPriority = AllowFree(dPriority);
const char* assignedTo = "unassigned";
if (sufficientFee && !sufficientPriority)
@@ -177,7 +177,7 @@ public:
history.resize(nEntries);
}
- void seenBlock(const std::vector<CTxMemPoolEntry>& entries, int nBlockHeight)
+ void seenBlock(const std::vector<CTxMemPoolEntry>& entries, int nBlockHeight, const CFeeRate minRelayFee)
{
if (nBlockHeight <= nBestSeenHeight)
{
@@ -222,7 +222,7 @@ public:
// Fees are stored and reported as BTC-per-kb:
CFeeRate feeRate(entry->GetFee(), entry->GetTxSize());
double dPriority = entry->GetPriority(entry->GetHeight()); // Want priority when it went IN
- seenTxConfirm(feeRate, dPriority, i);
+ seenTxConfirm(feeRate, minRelayFee, dPriority, i);
}
}
for (size_t i = 0; i < history.size(); i++) {
@@ -251,8 +251,13 @@ public:
std::sort(sortedFeeSamples.begin(), sortedFeeSamples.end(),
std::greater<CFeeRate>());
}
- if (sortedFeeSamples.size() == 0)
+ if (sortedFeeSamples.size() < 11)
+ {
+ // Eleven is Gavin's Favorite Number
+ // ... but we also take a maximum of 10 samples per block so eleven means
+ // we're getting samples from at least two different blocks
return CFeeRate(0);
+ }
int nBucketSize = history.at(nBlocksToConfirm).FeeSamples();
@@ -281,7 +286,7 @@ public:
std::sort(sortedPrioritySamples.begin(), sortedPrioritySamples.end(),
std::greater<double>());
}
- if (sortedPrioritySamples.size() == 0)
+ if (sortedPrioritySamples.size() < 11)
return -1.0;
int nBucketSize = history.at(nBlocksToConfirm).PrioritySamples();
@@ -308,7 +313,7 @@ public:
}
}
- void Read(CAutoFile& filein)
+ void Read(CAutoFile& filein, const CFeeRate& minRelayFee)
{
filein >> nBestSeenHeight;
size_t numEntries;
@@ -317,14 +322,14 @@ public:
for (size_t i = 0; i < numEntries; i++)
{
CBlockAverage entry;
- entry.Read(filein);
+ entry.Read(filein, minRelayFee);
history.push_back(entry);
}
}
};
-CTxMemPool::CTxMemPool()
+CTxMemPool::CTxMemPool(const CFeeRate& _minRelayFee) : minRelayFee(_minRelayFee)
{
// Sanity checks off by default for performance, because otherwise
// accepting transactions becomes O(N^2) where N is the number
@@ -440,7 +445,7 @@ void CTxMemPool::removeForBlock(const std::vector<CTransaction>& vtx, unsigned i
if (mapTx.count(hash))
entries.push_back(mapTx[hash]);
}
- minerPolicyEstimator->seenBlock(entries, nBlockHeight);
+ minerPolicyEstimator->seenBlock(entries, nBlockHeight, minRelayFee);
BOOST_FOREACH(const CTransaction& tx, vtx)
{
std::list<CTransaction> dummy;
@@ -555,7 +560,7 @@ CTxMemPool::ReadFeeEstimates(CAutoFile& filein)
return error("CTxMemPool::ReadFeeEstimates() : up-version (%d) fee estimate file", nVersionRequired);
LOCK(cs);
- minerPolicyEstimator->Read(filein);
+ minerPolicyEstimator->Read(filein, minRelayFee);
}
catch (std::exception &e) {
LogPrintf("CTxMemPool::ReadFeeEstimates() : unable to read policy estimator data (non-fatal)");
diff --git a/src/txmempool.h b/src/txmempool.h
index f7dbb126a0..41b2c52f39 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -67,13 +67,15 @@ private:
unsigned int nTransactionsUpdated;
CMinerPolicyEstimator* minerPolicyEstimator;
+ CFeeRate minRelayFee; // Passed to constructor to avoid dependency on main
+
public:
mutable CCriticalSection cs;
std::map<uint256, CTxMemPoolEntry> mapTx;
std::map<COutPoint, CInPoint> mapNextTx;
std::map<uint256, std::pair<double, int64_t> > mapDeltas;
- CTxMemPool();
+ CTxMemPool(const CFeeRate& _minRelayFee);
~CTxMemPool();
/*
diff --git a/src/util.cpp b/src/util.cpp
index 5a8f85ade7..081484c37d 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -95,6 +95,7 @@ bool fDaemon = false;
bool fServer = false;
string strMiscWarning;
bool fLogTimestamps = false;
+bool fLogIPs = false;
volatile bool fReopenDebugLog = false;
CClientUIInterface uiInterface;
diff --git a/src/util.h b/src/util.h
index 707b8f2d76..60db71bfd0 100644
--- a/src/util.h
+++ b/src/util.h
@@ -100,6 +100,7 @@ extern bool fPrintToDebugLog;
extern bool fServer;
extern std::string strMiscWarning;
extern bool fLogTimestamps;
+extern bool fLogIPs;
extern volatile bool fReopenDebugLog;
void RandAddSeed();
diff --git a/src/wallet.cpp b/src/wallet.cpp
index daca7ac04b..318a1388d2 100644
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -18,8 +18,12 @@ using namespace std;
// Settings
CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE);
+unsigned int nTxConfirmTarget = 1;
bool bSpendZeroConfChange = true;
+/** Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) */
+CFeeRate CWallet::minTxFee = CFeeRate(10000); // Override with -mintxfee
+
//////////////////////////////////////////////////////////////////////////////
//
// mapWallet
@@ -1273,6 +1277,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend,
return false;
}
+ wtxNew.fTimeReceivedIsTxTime = true;
wtxNew.BindWallet(this);
CMutableTransaction txNew;
@@ -1292,7 +1297,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::minRelayTxFee))
+ if (txout.IsDust(::minRelayTxFee))
{
strFailReason = _("Transaction amount too small");
return false;
@@ -1353,7 +1358,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::minRelayTxFee))
+ if (newTxOut.IsDust(::minRelayTxFee))
{
nFeeRet += nChange;
reservekey.ReturnKey();
@@ -1393,19 +1398,31 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend,
}
dPriority = wtxNew.ComputePriority(dPriority, nBytes);
- // Check that enough fee is included
- int64_t nPayFee = payTxFee.GetFee(nBytes);
- bool fAllowFree = AllowFree(dPriority);
- int64_t nMinFee = GetMinFee(wtxNew, nBytes, fAllowFree, GMF_SEND);
- if (nFeeRet < max(nPayFee, nMinFee))
+ int64_t nFeeNeeded = GetMinimumFee(nBytes, nTxConfirmTarget, mempool);
+
+ if (nFeeRet >= nFeeNeeded)
+ break; // Done, enough fee included.
+
+ // Too big to send for free? Include more fee and try again:
+ if (nBytes > MAX_FREE_TRANSACTION_CREATE_SIZE)
{
- nFeeRet = max(nPayFee, nMinFee);
+ nFeeRet = nFeeNeeded;
continue;
}
- wtxNew.fTimeReceivedIsTxTime = true;
+ // Not enough fee: enough priority?
+ double dPriorityNeeded = mempool.estimatePriority(nTxConfirmTarget);
+ // Not enough mempool history to estimate: use hard-coded AllowFree.
+ if (dPriorityNeeded <= 0 && AllowFree(dPriority))
+ break;
+
+ // Small enough, and priority high enough, to send for free
+ if (dPriority >= dPriorityNeeded)
+ break;
- break;
+ // Include more fee and try again.
+ nFeeRet = nFeeNeeded;
+ continue;
}
}
}
@@ -1513,6 +1530,20 @@ string CWallet::SendMoneyToDestination(const CTxDestination& address, int64_t nV
return SendMoney(scriptPubKey, nValue, wtxNew);
}
+int64_t CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool)
+{
+ // payTxFee is user-set "I want to pay this much"
+ int64_t nFeeNeeded = payTxFee.GetFee(nTxBytes);
+ // User didn't set: use -txconfirmtarget to estimate...
+ if (nFeeNeeded == 0)
+ nFeeNeeded = pool.estimateFee(nConfirmTarget).GetFee(nTxBytes);
+ // ... unless we don't have enough mempool data, in which case fall
+ // back to a hard-coded fee
+ if (nFeeNeeded == 0)
+ nFeeNeeded = minTxFee.GetFee(nTxBytes);
+ return nFeeNeeded;
+}
+
diff --git a/src/wallet.h b/src/wallet.h
index 1c2512d678..a5162bb838 100644
--- a/src/wallet.h
+++ b/src/wallet.h
@@ -25,12 +25,15 @@
// Settings
extern CFeeRate payTxFee;
+extern unsigned int nTxConfirmTarget;
extern bool bSpendZeroConfChange;
// -paytxfee default
static const int64_t DEFAULT_TRANSACTION_FEE = 0;
// -paytxfee will warn if called with a higher fee than this amount (in satoshis) per KB
static const int nHighTransactionFeeWarning = 0.01 * COIN;
+// Largest (in bytes) free transaction we're willing to create
+static const unsigned int MAX_FREE_TRANSACTION_CREATE_SIZE = 1000;
class CAccountingEntry;
class CCoinControl;
@@ -265,6 +268,9 @@ public:
std::string SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew);
std::string SendMoneyToDestination(const CTxDestination &address, int64_t nValue, CWalletTx& wtxNew);
+ static CFeeRate minTxFee;
+ static int64_t GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool);
+
bool NewKeyPool();
bool TopUpKeyPool(unsigned int kpSize = 0);
void ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool);