aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/httprpc.cpp2
-rw-r--r--src/init.cpp6
-rw-r--r--src/interfaces/wallet.cpp2
-rw-r--r--src/net_processing.cpp111
-rw-r--r--src/net_processing.h30
-rw-r--r--src/outputtype.cpp101
-rw-r--r--src/outputtype.h49
-rw-r--r--src/rpc/misc.cpp21
-rw-r--r--src/test/denialofservice_tests.cpp2
-rw-r--r--src/test/test_bitcoin.cpp2
-rw-r--r--src/validation.cpp9
-rw-r--r--src/wallet/init.cpp1
-rw-r--r--src/wallet/rpcwallet.cpp69
-rw-r--r--src/wallet/wallet.cpp219
-rw-r--r--src/wallet/wallet.h67
16 files changed, 362 insertions, 331 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 5d7eafb3f1..1dd202085a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -139,6 +139,7 @@ BITCOIN_CORE_H = \
netbase.h \
netmessagemaker.h \
noui.h \
+ outputtype.h \
policy/feerate.h \
policy/fees.h \
policy/policy.h \
@@ -230,6 +231,7 @@ libbitcoin_server_a_SOURCES = \
net.cpp \
net_processing.cpp \
noui.cpp \
+ outputtype.cpp \
policy/fees.cpp \
policy/policy.cpp \
policy/rbf.cpp \
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index c49ad12283..97b152b11b 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -223,7 +223,7 @@ static bool InitRPCAuthentication()
return false;
}
} else {
- LogPrintf("Config options rpcuser and rpcpassword will soon be deprecated. Locally-run instances may remove rpcuser to use cookie-based auth, or may be replaced with rpcauth. Please see share/rpcuser for rpcauth auth generation.\n");
+ LogPrintf("Config options rpcuser and rpcpassword will soon be deprecated. Locally-run instances may remove rpcuser to use cookie-based auth, or may be replaced with rpcauth. Please see share/rpcauth for rpcauth auth generation.\n");
strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", "");
}
if (gArgs.GetArg("-rpcauth","") != "")
diff --git a/src/init.cpp b/src/init.cpp
index 66b0b65eb4..c7ea40817d 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -501,7 +501,7 @@ void SetupServerArgs()
gArgs.AddArg("-rest", strprintf("Accept public REST requests (default: %u)", DEFAULT_REST_ENABLE), false, OptionsCategory::RPC);
gArgs.AddArg("-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", false, OptionsCategory::RPC);
- gArgs.AddArg("-rpcauth=<userpw>", "Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times", false, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcauth=<userpw>", "Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcauth. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times", false, OptionsCategory::RPC);
gArgs.AddArg("-rpcbind=<addr>[:port]", "Bind to given address to listen for JSON-RPC connections. This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost, or if -rpcallowip has been specified, 0.0.0.0 and :: i.e., all addresses)", false, OptionsCategory::RPC);
gArgs.AddArg("-rpccookiefile=<loc>", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", false, OptionsCategory::RPC);
gArgs.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", false, OptionsCategory::RPC);
@@ -1122,8 +1122,6 @@ bool AppInitParameterInteraction()
if (gArgs.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS))
nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM);
- g_enable_bip61 = gArgs.GetBoolArg("-enablebip61", DEFAULT_ENABLE_BIP61);
-
if (gArgs.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) < 0)
return InitError("rpcserialversion must be non-negative.");
@@ -1308,7 +1306,7 @@ bool AppInitMain()
g_connman = std::unique_ptr<CConnman>(new CConnman(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max())));
CConnman& connman = *g_connman;
- peerLogic.reset(new PeerLogicValidation(&connman, scheduler));
+ peerLogic.reset(new PeerLogicValidation(&connman, scheduler, gArgs.GetBoolArg("-enablebip61", DEFAULT_ENABLE_BIP61)));
RegisterValidationInterface(peerLogic.get());
// sanitize comments per BIP-0014, format user agent and check total size
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp
index aade4b2df3..b42c9b63df 100644
--- a/src/interfaces/wallet.cpp
+++ b/src/interfaces/wallet.cpp
@@ -338,7 +338,7 @@ public:
result.immature_balance = m_wallet.GetImmatureBalance();
result.have_watch_only = m_wallet.HaveWatchOnly();
if (result.have_watch_only) {
- result.watch_only_balance = m_wallet.GetWatchOnlyBalance();
+ result.watch_only_balance = m_wallet.GetBalance(ISMINE_WATCH_ONLY);
result.unconfirmed_watch_only_balance = m_wallet.GetUnconfirmedWatchOnlyBalance();
result.immature_watch_only_balance = m_wallet.GetImmatureWatchOnlyBalance();
}
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 2f3a604064..0bc508980e 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -35,17 +35,34 @@
# error "Bitcoin cannot be compiled without assertions."
#endif
-std::atomic<int64_t> nTimeBestReceived(0); // Used only to inform the wallet of when we last received a block
-bool g_enable_bip61 = DEFAULT_ENABLE_BIP61;
-
-struct IteratorComparator
-{
- template<typename I>
- bool operator()(const I& a, const I& b) const
- {
- return &(*a) < &(*b);
- }
-};
+/** Expiration time for orphan transactions in seconds */
+static constexpr int64_t ORPHAN_TX_EXPIRE_TIME = 20 * 60;
+/** Minimum time between orphan transactions expire time checks in seconds */
+static constexpr int64_t ORPHAN_TX_EXPIRE_INTERVAL = 5 * 60;
+/** Headers download timeout expressed in microseconds
+ * Timeout = base + per_header * (expected number of headers) */
+static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_BASE = 15 * 60 * 1000000; // 15 minutes
+static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1000; // 1ms/header
+/** Protect at least this many outbound peers from disconnection due to slow/
+ * behind headers chain.
+ */
+static constexpr int32_t MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT = 4;
+/** Timeout for (unprotected) outbound peers to sync to our chainwork, in seconds */
+static constexpr int64_t CHAIN_SYNC_TIMEOUT = 20 * 60; // 20 minutes
+/** How frequently to check for stale tips, in seconds */
+static constexpr int64_t STALE_CHECK_INTERVAL = 10 * 60; // 10 minutes
+/** How frequently to check for extra outbound peers and disconnect, in seconds */
+static constexpr int64_t EXTRA_PEER_CHECK_INTERVAL = 45;
+/** Minimum time an outbound-peer-eviction candidate must be connected for, in order to evict, in seconds */
+static constexpr int64_t MINIMUM_CONNECT_TIME = 30;
+/** SHA256("main address relay")[0:8] */
+static constexpr uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL;
+/// Age after which a stale block will no longer be served if requested as
+/// protection against fingerprinting. Set to one month, denominated in seconds.
+static constexpr int STALE_RELAY_AGE_LIMIT = 30 * 24 * 60 * 60;
+/// Age after which a block is considered historical for purposes of rate
+/// limiting block relay. Set to one week, denominated in seconds.
+static constexpr int HISTORICAL_BLOCK_AGE = 7 * 24 * 60 * 60;
struct COrphanTx {
// When modifying, adapt the copy of this definition in tests/DoS_tests.
@@ -55,21 +72,11 @@ struct COrphanTx {
};
static CCriticalSection g_cs_orphans;
std::map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(g_cs_orphans);
-std::map<COutPoint, std::set<std::map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(g_cs_orphans);
-void EraseOrphansFor(NodeId peer);
-
-static size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0;
-static std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUARDED_BY(g_cs_orphans);
-static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; // SHA256("main address relay")[0:8]
-
-/// Age after which a stale block will no longer be served if requested as
-/// protection against fingerprinting. Set to one month, denominated in seconds.
-static const int STALE_RELAY_AGE_LIMIT = 30 * 24 * 60 * 60;
+void EraseOrphansFor(NodeId peer);
-/// Age after which a block is considered historical for purposes of rate
-/// limiting block relay. Set to one week, denominated in seconds.
-static const int HISTORICAL_BLOCK_AGE = 7 * 24 * 60 * 60;
+/** Increase a node's misbehavior score. */
+void Misbehaving(NodeId nodeid, int howmuch, const std::string& message="");
// Internal stuff
namespace {
@@ -137,10 +144,24 @@ namespace {
MapRelay mapRelay;
/** Expiration-time ordered list of (expire time, relay map entry) pairs, protected by cs_main). */
std::deque<std::pair<int64_t, MapRelay::iterator>> vRelayExpiration;
+
+ std::atomic<int64_t> nTimeBestReceived(0); // Used only to inform the wallet of when we last received a block
+
+ struct IteratorComparator
+ {
+ template<typename I>
+ bool operator()(const I& a, const I& b) const
+ {
+ return &(*a) < &(*b);
+ }
+ };
+ std::map<COutPoint, std::set<std::map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(g_cs_orphans);
+
+ static size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0;
+ static std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUARDED_BY(g_cs_orphans);
} // namespace
namespace {
-
struct CBlockReject {
unsigned char chRejectCode;
std::string strRejectReason;
@@ -267,10 +288,10 @@ struct CNodeState {
};
/** Map maintaining per-node state. Requires cs_main. */
-std::map<NodeId, CNodeState> mapNodeState;
+static std::map<NodeId, CNodeState> mapNodeState;
// Requires cs_main.
-CNodeState *State(NodeId pnode) {
+static CNodeState *State(NodeId pnode) {
std::map<NodeId, CNodeState>::iterator it = mapNodeState.find(pnode);
if (it == mapNodeState.end())
return nullptr;
@@ -809,7 +830,9 @@ static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Para
(GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT);
}
-PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, CScheduler &scheduler) : connman(connmanIn), m_stale_tip_check_time(0) {
+PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, CScheduler &scheduler, bool enable_bip61)
+ : connman(connmanIn), m_stale_tip_check_time(0), m_enable_bip61(enable_bip61) {
+
// Initialize global variables that cannot be constructed at startup.
recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
@@ -866,7 +889,7 @@ static uint256 most_recent_block_hash;
static bool fWitnessesPresentInMostRecentCompactBlock;
/**
- * Maintain state about the best-seen block and fast-announce a compact block
+ * Maintain state about the best-seen block and fast-announce a compact block
* to compatible peers.
*/
void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) {
@@ -911,7 +934,7 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std:
}
/**
- * Update our best height and announce any block hashes which weren't previously
+ * Update our best height and announce any block hashes which weren't previously
* in chainActive to our peers.
*/
void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
@@ -947,7 +970,7 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB
}
/**
- * Handle invalid block rejection and consequent peer banning, maintain which
+ * Handle invalid block rejection and consequent peer banning, maintain which
* peers announce compact blocks.
*/
void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationState& state) {
@@ -1534,7 +1557,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
return true;
}
-bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman* connman, const std::atomic<bool>& interruptMsgProc)
+bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman* connman, const std::atomic<bool>& interruptMsgProc, bool enable_bip61)
{
LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->GetId());
if (gArgs.IsArgSet("-dropmessagestest") && GetRand(gArgs.GetArg("-dropmessagestest", 0)) == 0)
@@ -1588,7 +1611,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// Each connection can only send one version message
if (pfrom->nVersion != 0)
{
- if (g_enable_bip61) {
+ if (enable_bip61) {
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, std::string("Duplicate version message")));
}
LOCK(cs_main);
@@ -1619,7 +1642,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (!pfrom->fInbound && !pfrom->fFeeler && !pfrom->m_manual_connection && !HasAllDesirableServiceFlags(nServices))
{
LogPrint(BCLog::NET, "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->GetId(), nServices, GetDesirableServiceFlags(nServices));
- if (g_enable_bip61) {
+ if (enable_bip61) {
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD,
strprintf("Expected to offer services %08x", GetDesirableServiceFlags(nServices))));
}
@@ -1642,7 +1665,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
{
// disconnect from peers older than this proto version
LogPrint(BCLog::NET, "peer=%d using obsolete version %i; disconnecting\n", pfrom->GetId(), nVersion);
- if (g_enable_bip61) {
+ if (enable_bip61) {
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE,
strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION)));
}
@@ -2340,7 +2363,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LogPrint(BCLog::MEMPOOLREJ, "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(),
pfrom->GetId(),
FormatStateMessage(state));
- if (g_enable_bip61 && state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) { // Never send AcceptToMemoryPool's internal codes over P2P
+ if (enable_bip61 && state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) { // Never send AcceptToMemoryPool's internal codes over P2P
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(),
state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash));
}
@@ -2525,7 +2548,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
} // cs_main
if (fProcessBLOCKTXN)
- return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, nTimeReceived, chainparams, connman, interruptMsgProc);
+ return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, nTimeReceived, chainparams, connman, interruptMsgProc, enable_bip61);
if (fRevertToHeaderProcessing) {
// Headers received from HB compact block peers are permitted to be
@@ -2911,12 +2934,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
return true;
}
-static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman* connman)
+static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman* connman, bool enable_bip61)
{
AssertLockHeld(cs_main);
CNodeState &state = *State(pnode->GetId());
- if (g_enable_bip61) {
+ if (enable_bip61) {
for (const CBlockReject& reject : state.rejects) {
connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, std::string(NetMsgType::BLOCK), reject.chRejectCode, reject.strRejectReason, reject.hashBlock));
}
@@ -3018,7 +3041,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
bool fRet = false;
try
{
- fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime, chainparams, connman, interruptMsgProc);
+ fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime, chainparams, connman, interruptMsgProc, m_enable_bip61);
if (interruptMsgProc)
return false;
if (!pfrom->vRecvGetData.empty())
@@ -3026,7 +3049,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
}
catch (const std::ios_base::failure& e)
{
- if (g_enable_bip61) {
+ if (m_enable_bip61) {
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, std::string("error parsing message")));
}
if (strstr(e.what(), "end of data"))
@@ -3060,7 +3083,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
}
LOCK(cs_main);
- SendRejectsAndCheckIfBanned(pfrom, connman);
+ SendRejectsAndCheckIfBanned(pfrom, connman, m_enable_bip61);
return fMoreWork;
}
@@ -3195,6 +3218,7 @@ void PeerLogicValidation::CheckForStaleTipAndEvictPeers(const Consensus::Params
}
}
+namespace {
class CompareInvMempoolOrder
{
CTxMemPool *mp;
@@ -3211,6 +3235,7 @@ public:
return mp->CompareDepthAndScore(*b, *a);
}
};
+}
bool PeerLogicValidation::SendMessages(CNode* pto)
{
@@ -3256,7 +3281,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
if (!lockMain)
return true;
- if (SendRejectsAndCheckIfBanned(pto, connman))
+ if (SendRejectsAndCheckIfBanned(pto, connman, m_enable_bip61))
return true;
CNodeState &state = *State(pto->GetId());
diff --git a/src/net_processing.h b/src/net_processing.h
index 0d97b316eb..4dab0ada2b 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -12,40 +12,17 @@
/** Default for -maxorphantx, maximum number of orphan transactions kept in memory */
static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100;
-/** Expiration time for orphan transactions in seconds */
-static const int64_t ORPHAN_TX_EXPIRE_TIME = 20 * 60;
-/** Minimum time between orphan transactions expire time checks in seconds */
-static const int64_t ORPHAN_TX_EXPIRE_INTERVAL = 5 * 60;
/** Default number of orphan+recently-replaced txn to keep around for block reconstruction */
static const unsigned int DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN = 100;
-/** Headers download timeout expressed in microseconds
- * Timeout = base + per_header * (expected number of headers) */
-static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_BASE = 15 * 60 * 1000000; // 15 minutes
-static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1000; // 1ms/header
-/** Protect at least this many outbound peers from disconnection due to slow/
- * behind headers chain.
- */
-static constexpr int32_t MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT = 4;
-/** Timeout for (unprotected) outbound peers to sync to our chainwork, in seconds */
-static constexpr int64_t CHAIN_SYNC_TIMEOUT = 20 * 60; // 20 minutes
-/** How frequently to check for stale tips, in seconds */
-static constexpr int64_t STALE_CHECK_INTERVAL = 10 * 60; // 10 minutes
-/** How frequently to check for extra outbound peers and disconnect, in seconds */
-static constexpr int64_t EXTRA_PEER_CHECK_INTERVAL = 45;
-/** Minimum time an outbound-peer-eviction candidate must be connected for, in order to evict, in seconds */
-static constexpr int64_t MINIMUM_CONNECT_TIME = 30;
-
/** Default for BIP61 (sending reject messages) */
static constexpr bool DEFAULT_ENABLE_BIP61 = true;
-/** Enable BIP61 (sending reject messages) */
-extern bool g_enable_bip61;
class PeerLogicValidation final : public CValidationInterface, public NetEventsInterface {
private:
CConnman* const connman;
public:
- explicit PeerLogicValidation(CConnman* connman, CScheduler &scheduler);
+ explicit PeerLogicValidation(CConnman* connman, CScheduler &scheduler, bool enable_bip61);
/**
* Overridden from CValidationInterface.
@@ -92,6 +69,9 @@ public:
private:
int64_t m_stale_tip_check_time; //! Next time to check for stale tip
+
+ /** Enable BIP61 (sending reject messages) */
+ const bool m_enable_bip61;
};
struct CNodeStateStats {
@@ -103,7 +83,5 @@ struct CNodeStateStats {
/** Get statistics from node state */
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats);
-/** Increase a node's misbehavior score. */
-void Misbehaving(NodeId nodeid, int howmuch, const std::string& message="");
#endif // BITCOIN_NET_PROCESSING_H
diff --git a/src/outputtype.cpp b/src/outputtype.cpp
new file mode 100644
index 0000000000..3ff28bf9c2
--- /dev/null
+++ b/src/outputtype.cpp
@@ -0,0 +1,101 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2017 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <outputtype.h>
+
+#include <keystore.h>
+#include <pubkey.h>
+#include <script/script.h>
+#include <script/standard.h>
+
+#include <assert.h>
+#include <string>
+
+static const std::string OUTPUT_TYPE_STRING_LEGACY = "legacy";
+static const std::string OUTPUT_TYPE_STRING_P2SH_SEGWIT = "p2sh-segwit";
+static const std::string OUTPUT_TYPE_STRING_BECH32 = "bech32";
+
+bool ParseOutputType(const std::string& type, OutputType& output_type)
+{
+ if (type == OUTPUT_TYPE_STRING_LEGACY) {
+ output_type = OutputType::LEGACY;
+ return true;
+ } else if (type == OUTPUT_TYPE_STRING_P2SH_SEGWIT) {
+ output_type = OutputType::P2SH_SEGWIT;
+ return true;
+ } else if (type == OUTPUT_TYPE_STRING_BECH32) {
+ output_type = OutputType::BECH32;
+ return true;
+ }
+ return false;
+}
+
+const std::string& FormatOutputType(OutputType type)
+{
+ switch (type) {
+ case OutputType::LEGACY: return OUTPUT_TYPE_STRING_LEGACY;
+ case OutputType::P2SH_SEGWIT: return OUTPUT_TYPE_STRING_P2SH_SEGWIT;
+ case OutputType::BECH32: return OUTPUT_TYPE_STRING_BECH32;
+ default: assert(false);
+ }
+}
+
+CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type)
+{
+ switch (type) {
+ case OutputType::LEGACY: return key.GetID();
+ case OutputType::P2SH_SEGWIT:
+ case OutputType::BECH32: {
+ if (!key.IsCompressed()) return key.GetID();
+ CTxDestination witdest = WitnessV0KeyHash(key.GetID());
+ CScript witprog = GetScriptForDestination(witdest);
+ if (type == OutputType::P2SH_SEGWIT) {
+ return CScriptID(witprog);
+ } else {
+ return witdest;
+ }
+ }
+ default: assert(false);
+ }
+}
+
+std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key)
+{
+ CKeyID keyid = key.GetID();
+ if (key.IsCompressed()) {
+ CTxDestination segwit = WitnessV0KeyHash(keyid);
+ CTxDestination p2sh = CScriptID(GetScriptForDestination(segwit));
+ return std::vector<CTxDestination>{std::move(keyid), std::move(p2sh), std::move(segwit)};
+ } else {
+ return std::vector<CTxDestination>{std::move(keyid)};
+ }
+}
+
+CTxDestination AddAndGetDestinationForScript(CKeyStore& keystore, const CScript& script, OutputType type)
+{
+ // Add script to keystore
+ keystore.AddCScript(script);
+ // Note that scripts over 520 bytes are not yet supported.
+ switch (type) {
+ case OutputType::LEGACY:
+ return CScriptID(script);
+ case OutputType::P2SH_SEGWIT:
+ case OutputType::BECH32: {
+ CTxDestination witdest = WitnessV0ScriptHash(script);
+ CScript witprog = GetScriptForDestination(witdest);
+ // Check if the resulting program is solvable (i.e. doesn't use an uncompressed key)
+ if (!IsSolvable(keystore, witprog)) return CScriptID(script);
+ // Add the redeemscript, so that P2WSH and P2SH-P2WSH outputs are recognized as ours.
+ keystore.AddCScript(witprog);
+ if (type == OutputType::BECH32) {
+ return witdest;
+ } else {
+ return CScriptID(witprog);
+ }
+ }
+ default: assert(false);
+ }
+}
+
diff --git a/src/outputtype.h b/src/outputtype.h
new file mode 100644
index 0000000000..21623e3b49
--- /dev/null
+++ b/src/outputtype.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2017 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_OUTPUTTYPE_H
+#define BITCOIN_OUTPUTTYPE_H
+
+#include <keystore.h>
+#include <script/standard.h>
+
+#include <string>
+#include <vector>
+
+enum class OutputType {
+ LEGACY,
+ P2SH_SEGWIT,
+ BECH32,
+
+ /**
+ * Special output type for change outputs only. Automatically choose type
+ * based on address type setting and the types other of non-change outputs
+ * (see -changetype option documentation and implementation in
+ * CWallet::TransactionChangeType for details).
+ */
+ CHANGE_AUTO,
+};
+
+bool ParseOutputType(const std::string& str, OutputType& output_type);
+const std::string& FormatOutputType(OutputType type);
+
+/**
+ * Get a destination of the requested type (if possible) to the specified key.
+ * The caller must make sure LearnRelatedScripts has been called beforehand.
+ */
+CTxDestination GetDestinationForKey(const CPubKey& key, OutputType);
+
+/** Get all destinations (potentially) supported by the wallet for the given key. */
+std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key);
+
+/**
+ * Get a destination of the requested type (if possible) to the specified script.
+ * This function will automatically add the script (and any other
+ * necessary scripts) to the keystore.
+ */
+CTxDestination AddAndGetDestinationForScript(CKeyStore& keystore, const CScript& script, OutputType);
+
+#endif // BITCOIN_OUTPUTTYPE_H
+
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 4eeb7f29d2..09812bb980 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -12,6 +12,7 @@
#include <httpserver.h>
#include <net.h>
#include <netbase.h>
+#include <outputtype.h>
#include <rpc/blockchain.h>
#include <rpc/server.h>
#include <rpc/util.h>
@@ -91,9 +92,9 @@ class CWallet;
static UniValue createmultisig(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 2 || request.params.size() > 2)
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
{
- std::string msg = "createmultisig nrequired [\"key\",...]\n"
+ std::string msg = "createmultisig nrequired [\"key\",...] ( \"address_type\" )\n"
"\nCreates a multi-signature address with n signature of m keys required.\n"
"It returns a json object with the address and redeemScript.\n"
"\nArguments:\n"
@@ -103,6 +104,7 @@ static UniValue createmultisig(const JSONRPCRequest& request)
" \"key\" (string) The hex-encoded public key\n"
" ,...\n"
" ]\n"
+ "3. \"address_type\" (string, optional) The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is legacy.\n"
"\nResult:\n"
"{\n"
@@ -133,12 +135,21 @@ static UniValue createmultisig(const JSONRPCRequest& request)
}
}
+ // Get the output type
+ OutputType output_type = OutputType::LEGACY;
+ if (!request.params[2].isNull()) {
+ if (!ParseOutputType(request.params[2].get_str(), output_type)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[2].get_str()));
+ }
+ }
+
// Construct using pay-to-script-hash:
- CScript inner = CreateMultisigRedeemscript(required, pubkeys);
- CScriptID innerID(inner);
+ const CScript inner = CreateMultisigRedeemscript(required, pubkeys);
+ CBasicKeyStore keystore;
+ const CTxDestination dest = AddAndGetDestinationForScript(keystore, inner, output_type);
UniValue result(UniValue::VOBJ);
- result.pushKV("address", EncodeDestination(innerID));
+ result.pushKV("address", EncodeDestination(dest));
result.pushKV("redeemScript", HexStr(inner.begin(), inner.end()));
return result;
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index cc871726fd..49037adb9a 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -24,6 +24,8 @@
extern bool AddOrphanTx(const CTransactionRef& tx, NodeId peer);
extern void EraseOrphansFor(NodeId peer);
extern unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans);
+extern void Misbehaving(NodeId nodeid, int howmuch, const std::string& message="");
+
struct COrphanTx {
CTransactionRef tx;
NodeId fromPeer;
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index 6ede65c23a..b35b21335e 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -107,7 +107,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
threadGroup.create_thread(&ThreadScriptCheck);
g_connman = std::unique_ptr<CConnman>(new CConnman(0x1337, 0x1337)); // Deterministic randomness for tests.
connman = g_connman.get();
- peerLogic.reset(new PeerLogicValidation(connman, scheduler));
+ peerLogic.reset(new PeerLogicValidation(connman, scheduler, /*enable_bip61=*/true));
}
TestingSetup::~TestingSetup()
diff --git a/src/validation.cpp b/src/validation.cpp
index 9921063a52..811530d40e 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -3729,6 +3729,15 @@ static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfte
int count=0;
if (nCurrentUsage + nBuffer >= nPruneTarget) {
+ // On a prune event, the chainstate DB is flushed.
+ // To avoid excessive prune events negating the benefit of high dbcache
+ // values, we should not prune too rapidly.
+ // So when pruning in IBD, increase the buffer a bit to avoid a re-prune too soon.
+ if (IsInitialBlockDownload()) {
+ // Since this is only relevant during IBD, we use a fixed 10%
+ nBuffer += nPruneTarget / 10;
+ }
+
for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) {
nBytesToPrune = vinfoBlockFile[fileNumber].nSize + vinfoBlockFile[fileNumber].nUndoSize;
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 74312b7124..076134cdd1 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -7,6 +7,7 @@
#include <init.h>
#include <net.h>
#include <scheduler.h>
+#include <outputtype.h>
#include <util.h>
#include <utilmoneystr.h>
#include <validation.h>
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index b1d2532d86..893310cf2f 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -11,6 +11,7 @@
#include <validation.h>
#include <key_io.h>
#include <net.h>
+#include <outputtype.h>
#include <policy/feerate.h>
#include <policy/fees.h>
#include <policy/policy.h>
@@ -852,8 +853,9 @@ static UniValue getbalance(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || (request.params.size() > 3 && IsDeprecatedRPCEnabled("accounts")) || (request.params.size() != 0 && !IsDeprecatedRPCEnabled("accounts")))
+ if (request.fHelp || (request.params.size() > 3 ))
throw std::runtime_error(
+ (IsDeprecatedRPCEnabled("accounts") ? std::string(
"getbalance ( \"account\" minconf include_watchonly )\n"
"\nIf account is not specified, returns the server's total available balance.\n"
"The available balance is what the wallet considers currently spendable, and is\n"
@@ -875,8 +877,17 @@ static UniValue getbalance(const JSONRPCRequest& request)
" balances. In general, account balance calculation is not considered\n"
" reliable and has resulted in confusing outcomes, so it is recommended to\n"
" avoid passing this argument.\n"
- "2. minconf (numeric, optional, default=1) DEPRECATED. Only valid when an account is specified. This argument will be removed in V0.18. To use this deprecated argument, start bitcoind with -deprecatedrpc=accounts. Only include transactions confirmed at least this many times.\n"
- "3. include_watchonly (bool, optional, default=false) DEPRECATED. Only valid when an account is specified. This argument will be removed in V0.18. To use this deprecated argument, start bitcoind with -deprecatedrpc=accounts. Also include balance in watch-only addresses (see 'importaddress')\n"
+ "2. minconf (numeric, optional) Only include transactions confirmed at least this many times. \n"
+ " The default is 1 if an account is provided or 0 if no account is provided\n")
+ : std::string(
+ "getbalance ( \"(dummy)\" minconf include_watchonly )\n"
+ "\nReturns the total available balance.\n"
+ "The available balance is what the wallet considers currently spendable, and is\n"
+ "thus affected by options which limit spendability such as -spendzeroconfchange.\n"
+ "\nArguments:\n"
+ "1. (dummy) (string, optional) Remains for backward compatibility. Must be excluded or set to \"*\".\n"
+ "2. minconf (numeric, optional, default=0) Only include transactions confirmed at least this many times.\n")) +
+ "3. include_watchonly (bool, optional, default=false) Also include balance in watch-only addresses (see 'importaddress')\n"
"\nResult:\n"
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this account.\n"
"\nExamples:\n"
@@ -894,38 +905,35 @@ static UniValue getbalance(const JSONRPCRequest& request)
LOCK2(cs_main, pwallet->cs_wallet);
- if (IsDeprecatedRPCEnabled("accounts")) {
- const UniValue& account_value = request.params[0];
- const UniValue& minconf = request.params[1];
- const UniValue& include_watchonly = request.params[2];
+ const UniValue& account_value = request.params[0];
- if (account_value.isNull()) {
- if (!minconf.isNull()) {
- throw JSONRPCError(RPC_INVALID_PARAMETER,
- "getbalance minconf option is only currently supported if an account is specified");
- }
- if (!include_watchonly.isNull()) {
- throw JSONRPCError(RPC_INVALID_PARAMETER,
- "getbalance include_watchonly option is only currently supported if an account is specified");
- }
- return ValueFromAmount(pwallet->GetBalance());
- }
+ int min_depth = 0;
+ if (IsDeprecatedRPCEnabled("accounts") && !account_value.isNull()) {
+ // Default min_depth to 1 when an account is provided.
+ min_depth = 1;
+ }
+ if (!request.params[1].isNull()) {
+ min_depth = request.params[1].get_int();
+ }
+
+ isminefilter filter = ISMINE_SPENDABLE;
+ if (!request.params[2].isNull() && request.params[2].get_bool()) {
+ filter = filter | ISMINE_WATCH_ONLY;
+ }
+
+ if (!account_value.isNull()) {
const std::string& account_param = account_value.get_str();
const std::string* account = account_param != "*" ? &account_param : nullptr;
- int nMinDepth = 1;
- if (!minconf.isNull())
- nMinDepth = minconf.get_int();
- isminefilter filter = ISMINE_SPENDABLE;
- if(!include_watchonly.isNull())
- if(include_watchonly.get_bool())
- filter = filter | ISMINE_WATCH_ONLY;
-
- return ValueFromAmount(pwallet->GetLegacyBalance(filter, nMinDepth, account));
+ if (!IsDeprecatedRPCEnabled("accounts") && account_param != "*") {
+ throw JSONRPCError(RPC_METHOD_DEPRECATED, "dummy first argument must be excluded or set to \"*\".");
+ } else if (IsDeprecatedRPCEnabled("accounts")) {
+ return ValueFromAmount(pwallet->GetLegacyBalance(filter, min_depth, account));
+ }
}
- return ValueFromAmount(pwallet->GetBalance());
+ return ValueFromAmount(pwallet->GetBalance(filter, min_depth));
}
static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
@@ -1362,8 +1370,7 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
// Construct using pay-to-script-hash:
CScript inner = CreateMultisigRedeemscript(required, pubkeys);
- pwallet->AddCScript(inner);
- CTxDestination dest = pwallet->AddAndGetDestinationForScript(inner, output_type);
+ CTxDestination dest = AddAndGetDestinationForScript(*pwallet, inner, output_type);
pwallet->SetAddressBook(dest, label, "send");
UniValue result(UniValue::VOBJ);
@@ -4421,7 +4428,7 @@ static const CRPCCommand commands[] =
{ "wallet", "dumpwallet", &dumpwallet, {"filename"} },
{ "wallet", "encryptwallet", &encryptwallet, {"passphrase"} },
{ "wallet", "getaddressinfo", &getaddressinfo, {"address"} },
- { "wallet", "getbalance", &getbalance, {"account","minconf","include_watchonly"} },
+ { "wallet", "getbalance", &getbalance, {"account|dummy","minconf","include_watchonly"} },
{ "wallet", "getnewaddress", &getnewaddress, {"label|account","address_type"} },
{ "wallet", "getrawchangeaddress", &getrawchangeaddress, {"address_type"} },
{ "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf"} },
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index b8cbaeeec7..067015c006 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1028,19 +1028,6 @@ bool CWallet::LoadToWallet(const CWalletTx& wtxIn)
return true;
}
-/**
- * Add a transaction to the wallet, or update it. pIndex and posInBlock should
- * be set when the transaction was known to be included in a block. When
- * pIndex == nullptr, then wallet state is not updated in AddToWallet, but
- * notifications happen and cached balances are marked dirty.
- *
- * If fUpdate is true, existing transactions will be updated.
- * TODO: One exception to this is that the abandoned state is cleared under the
- * assumption that any further notification of a transaction that was considered
- * abandoned is an indication that it is not safe to be considered abandoned.
- * Abandoned state should probably be more carefully tracked via different
- * posInBlock signals or by checking mempool presence when necessary.
- */
bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate)
{
const CTransaction& tx = *ptx;
@@ -1107,6 +1094,16 @@ bool CWallet::TransactionCanBeAbandoned(const uint256& hashTx) const
return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() == 0 && !wtx->InMempool();
}
+void CWallet::MarkInputsDirty(const CTransactionRef& tx)
+{
+ for (const CTxIn& txin : tx->vin) {
+ auto it = mapWallet.find(txin.prevout.hash);
+ if (it != mapWallet.end()) {
+ it->second.MarkDirty();
+ }
+ }
+}
+
bool CWallet::AbandonTransaction(const uint256& hashTx)
{
LOCK2(cs_main, cs_wallet);
@@ -1155,13 +1152,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
}
// If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be recomputed
- for (const CTxIn& txin : wtx.tx->vin)
- {
- auto it = mapWallet.find(txin.prevout.hash);
- if (it != mapWallet.end()) {
- it->second.MarkDirty();
- }
- }
+ MarkInputsDirty(wtx.tx);
}
}
@@ -1217,31 +1208,19 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
}
// If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be recomputed
- for (const CTxIn& txin : wtx.tx->vin) {
- auto it = mapWallet.find(txin.prevout.hash);
- if (it != mapWallet.end()) {
- it->second.MarkDirty();
- }
- }
+ MarkInputsDirty(wtx.tx);
}
}
}
-void CWallet::SyncTransaction(const CTransactionRef& ptx, const CBlockIndex *pindex, int posInBlock) {
- const CTransaction& tx = *ptx;
-
- if (!AddToWalletIfInvolvingMe(ptx, pindex, posInBlock, true))
+void CWallet::SyncTransaction(const CTransactionRef& ptx, const CBlockIndex *pindex, int posInBlock, bool update_tx) {
+ if (!AddToWalletIfInvolvingMe(ptx, pindex, posInBlock, update_tx))
return; // Not one of ours
// If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be
// recomputed, also:
- for (const CTxIn& txin : tx.vin) {
- auto it = mapWallet.find(txin.prevout.hash);
- if (it != mapWallet.end()) {
- it->second.MarkDirty();
- }
- }
+ MarkInputsDirty(ptx);
}
void CWallet::TransactionAddedToMempool(const CTransactionRef& ptx) {
@@ -1758,7 +1737,7 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
break;
}
for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
- AddToWalletIfInvolvingMe(block.vtx[posInBlock], pindex, posInBlock, fUpdate);
+ SyncTransaction(block.vtx[posInBlock], pindex, posInBlock, fUpdate);
}
} else {
ret = pindex;
@@ -1929,7 +1908,7 @@ CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const
return 0;
}
-CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
+CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter) const
{
if (pwallet == nullptr)
return 0;
@@ -1938,8 +1917,20 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
if (IsCoinBase() && GetBlocksToMaturity() > 0)
return 0;
- if (fUseCache && fAvailableCreditCached)
- return nAvailableCreditCached;
+ CAmount* cache = nullptr;
+ bool* cache_used = nullptr;
+
+ if (filter == ISMINE_SPENDABLE) {
+ cache = &nAvailableCreditCached;
+ cache_used = &fAvailableCreditCached;
+ } else if (filter == ISMINE_WATCH_ONLY) {
+ cache = &nAvailableWatchCreditCached;
+ cache_used = &fAvailableWatchCreditCached;
+ }
+
+ if (fUseCache && cache_used && *cache_used) {
+ return *cache;
+ }
CAmount nCredit = 0;
uint256 hashTx = GetHash();
@@ -1948,14 +1939,16 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
if (!pwallet->IsSpent(hashTx, i))
{
const CTxOut &txout = tx->vout[i];
- nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE);
+ nCredit += pwallet->GetCredit(txout, filter);
if (!MoneyRange(nCredit))
throw std::runtime_error(std::string(__func__) + " : value out of range");
}
}
- nAvailableCreditCached = nCredit;
- fAvailableCreditCached = true;
+ if (cache) {
+ *cache = nCredit;
+ *cache_used = true;
+ }
return nCredit;
}
@@ -1973,35 +1966,6 @@ CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool fUseCache) const
return 0;
}
-CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool fUseCache) const
-{
- if (pwallet == nullptr)
- return 0;
-
- // Must wait until coinbase is safely deep enough in the chain before valuing it
- if (IsCoinBase() && GetBlocksToMaturity() > 0)
- return 0;
-
- if (fUseCache && fAvailableWatchCreditCached)
- return nAvailableWatchCreditCached;
-
- CAmount nCredit = 0;
- for (unsigned int i = 0; i < tx->vout.size(); i++)
- {
- if (!pwallet->IsSpent(GetHash(), i))
- {
- const CTxOut &txout = tx->vout[i];
- nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY);
- if (!MoneyRange(nCredit))
- throw std::runtime_error(std::string(__func__) + ": value out of range");
- }
- }
-
- nAvailableWatchCreditCached = nCredit;
- fAvailableWatchCreditCached = true;
- return nCredit;
-}
-
CAmount CWalletTx::GetChange() const
{
if (fChangeCached)
@@ -2115,7 +2079,7 @@ void CWallet::ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman
*/
-CAmount CWallet::GetBalance() const
+CAmount CWallet::GetBalance(const isminefilter& filter, const int min_depth) const
{
CAmount nTotal = 0;
{
@@ -2123,8 +2087,9 @@ CAmount CWallet::GetBalance() const
for (const auto& entry : mapWallet)
{
const CWalletTx* pcoin = &entry.second;
- if (pcoin->IsTrusted())
- nTotal += pcoin->GetAvailableCredit();
+ if (pcoin->IsTrusted() && pcoin->GetDepthInMainChain() >= min_depth) {
+ nTotal += pcoin->GetAvailableCredit(true, filter);
+ }
}
}
@@ -2160,22 +2125,6 @@ CAmount CWallet::GetImmatureBalance() const
return nTotal;
}
-CAmount CWallet::GetWatchOnlyBalance() const
-{
- CAmount nTotal = 0;
- {
- LOCK2(cs_main, cs_wallet);
- for (const auto& entry : mapWallet)
- {
- const CWalletTx* pcoin = &entry.second;
- if (pcoin->IsTrusted())
- nTotal += pcoin->GetAvailableWatchOnlyCredit();
- }
- }
-
- return nTotal;
-}
-
CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const
{
CAmount nTotal = 0;
@@ -2185,7 +2134,7 @@ CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const
{
const CWalletTx* pcoin = &entry.second;
if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool())
- nTotal += pcoin->GetAvailableWatchOnlyCredit();
+ nTotal += pcoin->GetAvailableCredit(true, ISMINE_WATCH_ONLY);
}
}
return nTotal;
@@ -4377,7 +4326,7 @@ void CMerkleTx::SetMerkleBranch(const CBlockIndex* pindex, int posInBlock)
nIndex = posInBlock;
}
-int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const
+int CMerkleTx::GetDepthInMainChain() const
{
if (hashUnset())
return 0;
@@ -4389,7 +4338,6 @@ int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const
if (!pindex || !chainActive.Contains(pindex))
return 0;
- pindexRet = pindex;
return ((nIndex == -1) ? (-1) : 1) * (chainActive.Height() - pindex->nHeight + 1);
}
@@ -4414,35 +4362,6 @@ bool CWalletTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState&
return ret;
}
-static const std::string OUTPUT_TYPE_STRING_LEGACY = "legacy";
-static const std::string OUTPUT_TYPE_STRING_P2SH_SEGWIT = "p2sh-segwit";
-static const std::string OUTPUT_TYPE_STRING_BECH32 = "bech32";
-
-bool ParseOutputType(const std::string& type, OutputType& output_type)
-{
- if (type == OUTPUT_TYPE_STRING_LEGACY) {
- output_type = OutputType::LEGACY;
- return true;
- } else if (type == OUTPUT_TYPE_STRING_P2SH_SEGWIT) {
- output_type = OutputType::P2SH_SEGWIT;
- return true;
- } else if (type == OUTPUT_TYPE_STRING_BECH32) {
- output_type = OutputType::BECH32;
- return true;
- }
- return false;
-}
-
-const std::string& FormatOutputType(OutputType type)
-{
- switch (type) {
- case OutputType::LEGACY: return OUTPUT_TYPE_STRING_LEGACY;
- case OutputType::P2SH_SEGWIT: return OUTPUT_TYPE_STRING_P2SH_SEGWIT;
- case OutputType::BECH32: return OUTPUT_TYPE_STRING_BECH32;
- default: assert(false);
- }
-}
-
void CWallet::LearnRelatedScripts(const CPubKey& key, OutputType type)
{
if (key.IsCompressed() && (type == OutputType::P2SH_SEGWIT || type == OutputType::BECH32)) {
@@ -4460,57 +4379,3 @@ void CWallet::LearnAllRelatedScripts(const CPubKey& key)
LearnRelatedScripts(key, OutputType::P2SH_SEGWIT);
}
-CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type)
-{
- switch (type) {
- case OutputType::LEGACY: return key.GetID();
- case OutputType::P2SH_SEGWIT:
- case OutputType::BECH32: {
- if (!key.IsCompressed()) return key.GetID();
- CTxDestination witdest = WitnessV0KeyHash(key.GetID());
- CScript witprog = GetScriptForDestination(witdest);
- if (type == OutputType::P2SH_SEGWIT) {
- return CScriptID(witprog);
- } else {
- return witdest;
- }
- }
- default: assert(false);
- }
-}
-
-std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key)
-{
- CKeyID keyid = key.GetID();
- if (key.IsCompressed()) {
- CTxDestination segwit = WitnessV0KeyHash(keyid);
- CTxDestination p2sh = CScriptID(GetScriptForDestination(segwit));
- return std::vector<CTxDestination>{std::move(keyid), std::move(p2sh), std::move(segwit)};
- } else {
- return std::vector<CTxDestination>{std::move(keyid)};
- }
-}
-
-CTxDestination CWallet::AddAndGetDestinationForScript(const CScript& script, OutputType type)
-{
- // Note that scripts over 520 bytes are not yet supported.
- switch (type) {
- case OutputType::LEGACY:
- return CScriptID(script);
- case OutputType::P2SH_SEGWIT:
- case OutputType::BECH32: {
- CTxDestination witdest = WitnessV0ScriptHash(script);
- CScript witprog = GetScriptForDestination(witdest);
- // Check if the resulting program is solvable (i.e. doesn't use an uncompressed key)
- if (!IsSolvable(*this, witprog)) return CScriptID(script);
- // Add the redeemscript, so that P2WSH and P2SH-P2WSH outputs are recognized as ours.
- AddCScript(witprog);
- if (type == OutputType::BECH32) {
- return witdest;
- } else {
- return CScriptID(witprog);
- }
- }
- default: assert(false);
- }
-}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index ef03a1eaed..2e53ca0c55 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -7,6 +7,7 @@
#define BITCOIN_WALLET_WALLET_H
#include <amount.h>
+#include <outputtype.h>
#include <policy/feerate.h>
#include <streams.h>
#include <tinyformat.h>
@@ -93,20 +94,6 @@ enum WalletFeature
FEATURE_LATEST = FEATURE_PRE_SPLIT_KEYPOOL
};
-enum class OutputType {
- LEGACY,
- P2SH_SEGWIT,
- BECH32,
-
- /**
- * Special output type for change outputs only. Automatically choose type
- * based on address type setting and the types other of non-change outputs
- * (see -changetype option documentation and implementation in
- * CWallet::TransactionChangeType for details).
- */
- CHANGE_AUTO,
-};
-
//! Default for -addresstype
constexpr OutputType DEFAULT_ADDRESS_TYPE{OutputType::P2SH_SEGWIT};
@@ -267,9 +254,8 @@ public:
* 0 : in memory pool, waiting to be included in a block
* >=1 : this many blocks deep in the main chain
*/
- int GetDepthInMainChain(const CBlockIndex* &pindexRet) const;
- int GetDepthInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); }
- bool IsInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet) > 0; }
+ int GetDepthInMainChain() const;
+ bool IsInMainChain() const { return GetDepthInMainChain() > 0; }
int GetBlocksToMaturity() const;
bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); }
bool isAbandoned() const { return (hashBlock == ABANDON_HASH); }
@@ -460,9 +446,8 @@ public:
CAmount GetDebit(const isminefilter& filter) const;
CAmount GetCredit(const isminefilter& filter) const;
CAmount GetImmatureCredit(bool fUseCache=true) const;
- CAmount GetAvailableCredit(bool fUseCache=true) const;
+ CAmount GetAvailableCredit(bool fUseCache=true, const isminefilter& filter=ISMINE_SPENDABLE) const;
CAmount GetImmatureWatchOnlyCredit(const bool fUseCache=true) const;
- CAmount GetAvailableWatchOnlyCredit(const bool fUseCache=true) const;
CAmount GetChange() const;
// Get the marginal bytes if spending the specified output from this transaction
@@ -703,14 +688,32 @@ private:
void AddToSpends(const COutPoint& outpoint, const uint256& wtxid);
void AddToSpends(const uint256& wtxid);
+ /**
+ * Add a transaction to the wallet, or update it. pIndex and posInBlock should
+ * be set when the transaction was known to be included in a block. When
+ * pIndex == nullptr, then wallet state is not updated in AddToWallet, but
+ * notifications happen and cached balances are marked dirty.
+ *
+ * If fUpdate is true, existing transactions will be updated.
+ * TODO: One exception to this is that the abandoned state is cleared under the
+ * assumption that any further notification of a transaction that was considered
+ * abandoned is an indication that it is not safe to be considered abandoned.
+ * Abandoned state should probably be more carefully tracked via different
+ * posInBlock signals or by checking mempool presence when necessary.
+ */
+ bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+
/* Mark a transaction (and its in-wallet descendants) as conflicting with a particular block. */
void MarkConflicted(const uint256& hashBlock, const uint256& hashTx);
+ /* Mark a transaction's inputs dirty, thus forcing the outputs to be recomputed */
+ void MarkInputsDirty(const CTransactionRef& tx);
+
void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>);
- /* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected.
+ /* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected/ScanForWalletTransactions.
* Should be called with pindexBlock and posInBlock if this is for a transaction that is included in a block. */
- void SyncTransaction(const CTransactionRef& tx, const CBlockIndex *pindex = nullptr, int posInBlock = 0) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void SyncTransaction(const CTransactionRef& tx, const CBlockIndex *pindex = nullptr, int posInBlock = 0, bool update_tx = true) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/* the HD chain data model (external chain counters) */
CHDChain hdChain;
@@ -934,7 +937,6 @@ public:
void TransactionAddedToMempool(const CTransactionRef& tx) override;
void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) override;
void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) override;
- bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
int64_t RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver, bool update);
CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, const WalletRescanReserver& reserver, bool fUpdate = false);
void TransactionRemovedFromMempool(const CTransactionRef &ptx) override;
@@ -942,10 +944,9 @@ public:
void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override;
// ResendWalletTransactionsBefore may only be called if fBroadcastTransactions!
std::vector<uint256> ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman);
- CAmount GetBalance() const;
+ CAmount GetBalance(const isminefilter& filter=ISMINE_SPENDABLE, const int min_depth=0) const;
CAmount GetUnconfirmedBalance() const;
CAmount GetImmatureBalance() const;
- CAmount GetWatchOnlyBalance() const;
CAmount GetUnconfirmedWatchOnlyBalance() const;
CAmount GetImmatureWatchOnlyBalance() const;
CAmount GetLegacyBalance(const isminefilter& filter, int minDepth, const std::string* account) const;
@@ -1182,12 +1183,6 @@ public:
*/
void LearnAllRelatedScripts(const CPubKey& key);
- /**
- * Get a destination of the requested type (if possible) to the specified script.
- * This function will automatically add the necessary scripts to the wallet.
- */
- CTxDestination AddAndGetDestinationForScript(const CScript& script, OutputType);
-
/** Whether a given output is spendable by this wallet */
bool OutputEligibleForSpending(const COutput& output, const CoinEligibilityFilter& eligibility_filter) const;
};
@@ -1254,18 +1249,6 @@ public:
}
};
-bool ParseOutputType(const std::string& str, OutputType& output_type);
-const std::string& FormatOutputType(OutputType type);
-
-/**
- * Get a destination of the requested type (if possible) to the specified key.
- * The caller must make sure LearnRelatedScripts has been called beforehand.
- */
-CTxDestination GetDestinationForKey(const CPubKey& key, OutputType);
-
-/** Get all destinations (potentially) supported by the wallet for the given key. */
-std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key);
-
/** RAII object to check and reserve a wallet rescan */
class WalletRescanReserver
{