Merge pull request #3202 from codablock/pr_v14_backports

[v0.14.0.x] Backport pending PRs from develop into v14
This commit is contained in:
UdjinM6 2019-11-21 12:54:58 +03:00 committed by GitHub
commit c7630f0f4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 276 additions and 168 deletions

View File

@ -10,8 +10,8 @@ build_darwin_SHA256SUM = shasum -a 256
build_darwin_DOWNLOAD = curl --location --fail --connect-timeout $(DOWNLOAD_CONNECT_TIMEOUT) -o
#darwin host on darwin builder. overrides darwin host preferences.
darwin_CC=$(shell xcrun -f clang) -mmacosx-version-min=$(OSX_MIN_VERSION)
darwin_CXX:=$(shell xcrun -f clang++) -mmacosx-version-min=$(OSX_MIN_VERSION) -stdlib=libc++ -fvisibility=hidden
darwin_CC=$(shell xcrun -f clang) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(shell xcrun --show-sdk-path)
darwin_CXX:=$(shell xcrun -f clang++) -mmacosx-version-min=$(OSX_MIN_VERSION) -stdlib=libc++ -fvisibility=hidden --sysroot $(shell xcrun --show-sdk-path)
darwin_AR:=$(shell xcrun -f ar)
darwin_RANLIB:=$(shell xcrun -f ranlib)
darwin_STRIP:=$(shell xcrun -f strip)

View File

@ -194,6 +194,23 @@ class DIP3Test(BitcoinTestFramework):
self.log.info("testing instant send with replaced MNs")
self.test_instantsend(10, 3, timeout=20)
self.log.info("testing masternode status updates")
# change voting address and see if changes are reflected in `masternode status` rpc output
mn = mns[0]
node = self.nodes[0]
old_dmnState = mn.node.masternode("status")["dmnState"]
old_voting_address = old_dmnState["votingAddress"]
new_voting_address = node.getnewaddress()
assert(old_voting_address != new_voting_address)
# also check if funds from payout address are used when no fee source address is specified
node.sendtoaddress(mn.rewards_address, 0.001)
node.protx('update_registrar', mn.protx_hash, "", new_voting_address, mn.rewards_address)
node.generate(1)
self.sync_all()
new_dmnState = mn.node.masternode("status")["dmnState"]
new_voting_address_from_rpc = new_dmnState["votingAddress"]
assert(new_voting_address_from_rpc == new_voting_address)
def prepare_mn(self, node, idx, alias):
mn = Masternode()
mn.idx = idx

View File

@ -670,9 +670,6 @@ class RawTransactionsTest(BitcoinTestFramework):
# Make sure there is exactly one input so coin selection can't skew the result
assert_equal(len(self.nodes[3].listunspent(1)), 1)
# Disable BIP69 sorting of inputs and outputs
self.nodes[3].setbip69enabled(False)
inputs = []
outputs = {self.nodes[2].getnewaddress(): 1}
rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
@ -701,9 +698,10 @@ class RawTransactionsTest(BitcoinTestFramework):
keys = list(outputs.keys())
rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
result = [self.nodes[3].fundrawtransaction(rawtx),
# Add changePosition=4 to circumvent BIP69 input/output sorting
result = [self.nodes[3].fundrawtransaction(rawtx, {"changePosition": 4}),
# split the fee between outputs 0, 2, and 3, but not output 1
self.nodes[3].fundrawtransaction(rawtx, {"subtractFeeFromOutputs": [0, 2, 3]})]
self.nodes[3].fundrawtransaction(rawtx, {"subtractFeeFromOutputs": [0, 2, 3], "changePosition": 4})]
dec_tx = [self.nodes[3].decoderawtransaction(result[0]['hex']),
self.nodes[3].decoderawtransaction(result[1]['hex'])]
@ -736,8 +734,5 @@ class RawTransactionsTest(BitcoinTestFramework):
# the total subtracted from the outputs is equal to the fee
assert_equal(share[0] + share[2] + share[3], result[0]['fee'])
# Reenable BIP69 sorting of inputs and outputs
self.nodes[3].setbip69enabled(True)
if __name__ == '__main__':
RawTransactionsTest().main()

View File

@ -19,8 +19,8 @@ class MempoolPackagesTest(BitcoinTestFramework):
def setup_network(self):
self.nodes = []
self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000"]))
self.nodes.append(start_node(1, self.options.tmpdir, ["-maxorphantx=1000", "-limitancestorcount=5"]))
self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantxsize=1000"]))
self.nodes.append(start_node(1, self.options.tmpdir, ["-maxorphantxsize=1000", "-limitancestorcount=5"]))
connect_nodes(self.nodes[0], 1)
self.is_network_split = False
self.sync_all()

View File

@ -78,6 +78,9 @@ class InstantSendTest(DashTestFramework):
assert (res['hash'] != wrong_block)
# wait for long time only for first node
timeout = 1
# send coins back to the controller node without waiting for confirmations
receiver.sendtoaddress(self.nodes[0].getnewaddress(), 0.9, "", "", True)
assert_equal(receiver.getwalletinfo()["balance"], 0)
# mine more blocks
# TODO: mine these blocks on an isolated node
set_mocktime(get_mocktime() + 1)
@ -116,6 +119,9 @@ class InstantSendTest(DashTestFramework):
for node in self.nodes:
self.wait_for_instantlock(is_id, node)
assert_raises_jsonrpc(-5, "No such mempool or blockchain transaction", isolated.getrawtransaction, dblspnd_txid)
# send coins back to the controller node without waiting for confirmations
receiver.sendtoaddress(self.nodes[0].getnewaddress(), 0.9, "", "", True)
assert_equal(receiver.getwalletinfo()["balance"], 0)
if __name__ == '__main__':
InstantSendTest().main()

View File

@ -156,7 +156,7 @@ class EstimateFeeTest(BitcoinTestFramework):
"""
self.nodes = []
# Use node0 to mine blocks for input splitting
self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000",
self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantxsize=1000",
"-whitelist=127.0.0.1"]))
self.log.info("This test is time consuming, please be patient")
@ -194,12 +194,12 @@ class EstimateFeeTest(BitcoinTestFramework):
# (17k is room enough for 110 or so transactions)
self.nodes.append(start_node(1, self.options.tmpdir,
["-blockmaxsize=17000",
"-maxorphantx=1000"]))
"-maxorphantxsize=1000"]))
connect_nodes(self.nodes[1], 0)
# Node2 is a stingy miner, that
# produces too small blocks (room for only 55 or so transactions)
node2args = ["-blockmaxsize=8000", "-maxorphantx=1000"]
node2args = ["-blockmaxsize=8000", "-maxorphantxsize=1000"]
self.nodes.append(start_node(2, self.options.tmpdir, node2args))
connect_nodes(self.nodes[0], 2)

View File

@ -16,14 +16,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleGetInfoString</key>
<string>@CLIENT_VERSION_MAJOR@.@CLIENT_VERSION_MINOR@.@CLIENT_VERSION_REVISION@.@CLIENT_VERSION_BUILD@, Copyright © 2009-@COPYRIGHT_YEAR@ The Bitcoin Core developers, 2014-@COPYRIGHT_YEAR@ @COPYRIGHT_HOLDERS_FINAL@</string>
<key>CFBundleShortVersionString</key>
<string>@CLIENT_VERSION_MAJOR@.@CLIENT_VERSION_MINOR@.@CLIENT_VERSION_REVISION@</string>
<string>@CLIENT_VERSION_MAJOR@.@CLIENT_VERSION_MINOR@.@CLIENT_VERSION_REVISION@.@CLIENT_VERSION_BUILD@</string>
<key>CFBundleVersion</key>
<string>@CLIENT_VERSION_MAJOR@.@CLIENT_VERSION_MINOR@.@CLIENT_VERSION_REVISION@</string>
<string>@CLIENT_VERSION_MAJOR@.@CLIENT_VERSION_MINOR@.@CLIENT_VERSION_REVISION@.@CLIENT_VERSION_BUILD@</string>
<key>CFBundleSignature</key>
<string>????</string>
@ -100,6 +97,9 @@
<key>NSRequiresAquaSystemAppearance</key>
<string>True</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2009-@COPYRIGHT_YEAR@ The Bitcoin Core developers, 2014-@COPYRIGHT_YEAR@ @COPYRIGHT_HOLDERS_FINAL@</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.finance</string>
</dict>

View File

@ -26,6 +26,8 @@ std::string CActiveMasternodeManager::GetStateString() const
return "REMOVED";
case MASTERNODE_OPERATOR_KEY_CHANGED:
return "OPERATOR_KEY_CHANGED";
case MASTERNODE_PROTX_IP_CHANGED:
return "PROTX_IP_CHANGED";
case MASTERNODE_READY:
return "READY";
case MASTERNODE_ERROR:
@ -46,6 +48,8 @@ std::string CActiveMasternodeManager::GetStatus() const
return "Masternode removed from list";
case MASTERNODE_OPERATOR_KEY_CHANGED:
return "Operator key changed or revoked";
case MASTERNODE_PROTX_IP_CHANGED:
return "IP address specified in ProTx changed";
case MASTERNODE_READY:
return "Ready";
case MASTERNODE_ERROR:
@ -94,11 +98,9 @@ void CActiveMasternodeManager::Init()
return;
}
mnListEntry = dmn;
LogPrintf("CActiveMasternodeManager::Init -- proTxHash=%s, proTx=%s\n", dmn->proTxHash.ToString(), dmn->ToString());
LogPrintf("CActiveMasternodeManager::Init -- proTxHash=%s, proTx=%s\n", mnListEntry->proTxHash.ToString(), mnListEntry->ToString());
if (activeMasternodeInfo.service != mnListEntry->pdmnState->addr) {
if (activeMasternodeInfo.service != dmn->pdmnState->addr) {
state = MASTERNODE_ERROR;
strError = "Local address does not match the address from ProTx";
LogPrintf("CActiveMasternodeManager::Init -- ERROR: %s", strError);
@ -120,8 +122,8 @@ void CActiveMasternodeManager::Init()
}
}
activeMasternodeInfo.proTxHash = mnListEntry->proTxHash;
activeMasternodeInfo.outpoint = mnListEntry->collateralOutpoint;
activeMasternodeInfo.proTxHash = dmn->proTxHash;
activeMasternodeInfo.outpoint = dmn->collateralOutpoint;
state = MASTERNODE_READY;
}
@ -134,24 +136,41 @@ void CActiveMasternodeManager::UpdatedBlockTip(const CBlockIndex* pindexNew, con
if (!deterministicMNManager->IsDIP3Enforced(pindexNew->nHeight)) return;
if (state == MASTERNODE_READY) {
auto mnList = deterministicMNManager->GetListForBlock(pindexNew);
if (!mnList.IsMNValid(mnListEntry->proTxHash)) {
auto oldMNList = deterministicMNManager->GetListForBlock(pindexNew->pprev);
auto newMNList = deterministicMNManager->GetListForBlock(pindexNew);
if (!newMNList.IsMNValid(activeMasternodeInfo.proTxHash)) {
// MN disappeared from MN list
state = MASTERNODE_REMOVED;
activeMasternodeInfo.proTxHash = uint256();
activeMasternodeInfo.outpoint.SetNull();
// MN might have reappeared in same block with a new ProTx
Init();
} else if (mnList.GetMN(mnListEntry->proTxHash)->pdmnState->pubKeyOperator != mnListEntry->pdmnState->pubKeyOperator) {
return;
}
auto oldDmn = oldMNList.GetMN(activeMasternodeInfo.proTxHash);
auto newDmn = newMNList.GetMN(activeMasternodeInfo.proTxHash);
if (newDmn->pdmnState->pubKeyOperator != oldDmn->pdmnState->pubKeyOperator) {
// MN operator key changed or revoked
state = MASTERNODE_OPERATOR_KEY_CHANGED;
activeMasternodeInfo.proTxHash = uint256();
activeMasternodeInfo.outpoint.SetNull();
// MN might have reappeared in same block with a new ProTx
Init();
return;
}
if (newDmn->pdmnState->addr != oldDmn->pdmnState->addr) {
// MN IP changed
state = MASTERNODE_PROTX_IP_CHANGED;
activeMasternodeInfo.proTxHash = uint256();
activeMasternodeInfo.outpoint.SetNull();
Init();
return;
}
} else {
// MN might have (re)appeared with a new ProTx or we've found some peers and figured out our local address
// MN might have (re)appeared with a new ProTx or we've found some peers
// and figured out our local address
Init();
}
}

View File

@ -46,12 +46,12 @@ public:
MASTERNODE_POSE_BANNED,
MASTERNODE_REMOVED,
MASTERNODE_OPERATOR_KEY_CHANGED,
MASTERNODE_PROTX_IP_CHANGED,
MASTERNODE_READY,
MASTERNODE_ERROR,
};
private:
CDeterministicMNCPtr mnListEntry;
masternode_state_t state{MASTERNODE_WAITING_FOR_PROTX};
std::string strError;
@ -60,8 +60,6 @@ public:
void Init();
CDeterministicMNCPtr GetDMN() const { return mnListEntry; }
std::string GetStateString() const;
std::string GetStatus() const;

View File

@ -444,7 +444,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
strUsage += HelpMessageOpt("-dbcache=<n>", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache));
strUsage += HelpMessageOpt("-loadblock=<file>", _("Imports blocks from external blk000??.dat file on startup"));
strUsage += HelpMessageOpt("-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS));
strUsage += HelpMessageOpt("-maxorphantxsize=<n>", strprintf(_("Maximum total size of all orphan transactions in megabytes (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS_SIZE));
strUsage += HelpMessageOpt("-maxmempool=<n>", strprintf(_("Keep the transaction memory pool below <n> megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE));
strUsage += HelpMessageOpt("-mempoolexpiry=<n>", strprintf(_("Do not keep transactions in the mempool longer than <n> hours (default: %u)"), DEFAULT_MEMPOOL_EXPIRY));
strUsage += HelpMessageOpt("-blockreconstructionextratxn=<n>", strprintf(_("Extra transactions to keep in memory for compact block reconstructions (default: %u)"), DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN));
@ -1402,6 +1402,10 @@ bool AppInitParameterInteraction()
return InitError("LLMQ type for ChainLocks can only be overridden on devnet.");
}
if (IsArgSet("-maxorphantx")) {
InitWarning("-maxorphantx is not supported anymore. Use -maxorphantxsize instead.");
}
return true;
}

View File

@ -676,18 +676,10 @@ void CSigSharesManager::ProcessSigShare(NodeId nodeId, const CSigShare& sigShare
if (!sigShares.Add(sigShare.GetKey(), sigShare)) {
return;
}
sigSharesToAnnounce.Add(sigShare.GetKey(), true);
auto it = timeSeenForSessions.find(sigShare.GetSignHash());
if (it == timeSeenForSessions.end()) {
auto t = GetTimeMillis();
// insert first-seen and last-seen time
timeSeenForSessions.emplace(sigShare.GetSignHash(), std::make_pair(t, t));
} else {
// update last-seen time
it->second.second = GetTimeMillis();
}
// Update the time we've seen the last sigShare
timeSeenForSessions[sigShare.GetSignHash()] = GetTimeMillis();
if (!quorumNodes.empty()) {
// don't announce and wait for other nodes to request this share and directly send it to them
@ -1215,10 +1207,9 @@ void CSigSharesManager::Cleanup()
std::unordered_set<uint256, StaticSaltedHasher> timeoutSessions;
for (auto& p : timeSeenForSessions) {
auto& signHash = p.first;
int64_t firstSeenTime = p.second.first;
int64_t lastSeenTime = p.second.second;
int64_t lastSeenTime = p.second;
if (now - firstSeenTime >= SESSION_TOTAL_TIMEOUT || now - lastSeenTime >= SESSION_NEW_SHARES_TIMEOUT) {
if (now - lastSeenTime >= SESSION_NEW_SHARES_TIMEOUT) {
timeoutSessions.emplace(signHash);
}
}

View File

@ -330,7 +330,6 @@ public:
class CSigSharesManager : public CRecoveredSigsListener
{
static const int64_t SESSION_NEW_SHARES_TIMEOUT = 60 * 1000;
static const int64_t SESSION_TOTAL_TIMEOUT = 5 * 60 * 1000;
static const int64_t SIG_SHARE_REQUEST_TIMEOUT = 5 * 1000;
// we try to keep total message size below 10k
@ -348,8 +347,8 @@ private:
SigShareMap<CSigShare> sigShares;
// stores time of first and last receivedSigShare. Used to detect timeouts
std::unordered_map<uint256, std::pair<int64_t, int64_t>, StaticSaltedHasher> timeSeenForSessions;
// stores time of last receivedSigShare. Used to detect timeouts
std::unordered_map<uint256, int64_t, StaticSaltedHasher> timeSeenForSessions;
std::unordered_map<NodeId, CSigSharesNodeState> nodeStates;
SigShareMap<std::pair<NodeId, int64_t>> sigSharesRequested;

View File

@ -24,6 +24,7 @@
#include "primitives/transaction.h"
#include "random.h"
#include "tinyformat.h"
#include "txdb.h"
#include "txmempool.h"
#include "ui_interface.h"
#include "util.h"
@ -76,10 +77,12 @@ struct COrphanTx {
CTransactionRef tx;
NodeId fromPeer;
int64_t nTimeExpire;
size_t nTxSize;
};
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);
size_t nMapOrphanTransactionsSize = 0;
void EraseOrphansFor(NodeId peer);
static size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0;
@ -659,7 +662,7 @@ bool AddOrphanTx(const CTransactionRef& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRE
return false;
}
auto ret = mapOrphanTransactions.emplace(hash, COrphanTx{tx, peer, GetTime() + ORPHAN_TX_EXPIRE_TIME});
auto ret = mapOrphanTransactions.emplace(hash, COrphanTx{tx, peer, GetTime() + ORPHAN_TX_EXPIRE_TIME, sz});
assert(ret.second);
BOOST_FOREACH(const CTxIn& txin, tx->vin) {
mapOrphanTransactionsByPrev[txin.prevout].insert(ret.first);
@ -667,6 +670,8 @@ bool AddOrphanTx(const CTransactionRef& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRE
AddToCompactExtraTransactions(tx);
nMapOrphanTransactionsSize += sz;
LogPrint("mempool", "stored orphan tx %s (mapsz %u outsz %u)\n", hash.ToString(),
mapOrphanTransactions.size(), mapOrphanTransactionsByPrev.size());
return true;
@ -686,6 +691,8 @@ int static EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans)
if (itPrev->second.empty())
mapOrphanTransactionsByPrev.erase(itPrev);
}
assert(nMapOrphanTransactionsSize >= it->second.nTxSize);
nMapOrphanTransactionsSize -= it->second.nTxSize;
mapOrphanTransactions.erase(it);
return 1;
}
@ -707,7 +714,7 @@ void EraseOrphansFor(NodeId peer)
}
unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
unsigned int LimitOrphanTxSize(unsigned int nMaxOrphansSize)
{
LOCK(g_cs_orphans);
@ -732,7 +739,7 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
nNextSweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL;
if (nErased > 0) LogPrint("mempool", "Erased %d orphan tx due to expiration\n", nErased);
}
while (mapOrphanTransactions.size() > nMaxOrphans)
while (!mapOrphanTransactions.empty() && nMapOrphanTransactionsSize > nMaxOrphansSize)
{
// Evict a random orphan:
uint256 randomhash = GetRandHash();
@ -970,10 +977,16 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
if (mapOrphanTransactions.count(inv.hash)) return true;
}
return recentRejects->contains(inv.hash) ||
// When we receive an islock for a previously rejected transaction, we have to
// drop the first-seen tx (which such a locked transaction was conflicting with)
// and re-request the locked transaction (which did not make it into the mempool
// previously due to txn-mempool-conflict rule). This means that we must ignore
// recentRejects filter for such locked txes here.
return (recentRejects->contains(inv.hash) && !llmq::quorumInstantSendManager->IsLocked(inv.hash)) ||
mempool.exists(inv.hash) ||
pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 0)) || // Best effort: only try output 0 and 1
pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 1));
pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 1)) ||
(fTxIndex && pblocktree->HasTxIndex(inv.hash));
}
case MSG_BLOCK:
@ -2291,8 +2304,8 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
AddOrphanTx(ptx, pfrom->GetId());
// DoS prevention: do not allow mapOrphanTransactions to grow unbounded
unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS));
unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx);
unsigned int nMaxOrphanTxSize = (unsigned int)std::max((int64_t)0, GetArg("-maxorphantxsize", DEFAULT_MAX_ORPHAN_TRANSACTIONS_SIZE)) * 1000000;
unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTxSize);
if (nEvicted > 0)
LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted);
} else {
@ -3761,5 +3774,6 @@ public:
// orphan transactions
mapOrphanTransactions.clear();
mapOrphanTransactionsByPrev.clear();
nMapOrphanTransactionsSize = 0;
}
} instance_of_cnetprocessingcleanup;

View File

@ -9,8 +9,8 @@
#include "net.h"
#include "validationinterface.h"
/** Default for -maxorphantx, maximum number of orphan transactions kept in memory */
static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100;
/** Default for -maxorphantxsize, maximum size in megabytes the orphan map can grow before entries are removed */
static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS_SIZE = 10; // this allows around 100 TXs of max size (and many more of normal size)
/** 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 */

View File

@ -198,6 +198,48 @@ struct timeval MillisToTimeval(int64_t nTimeout)
return timeout;
}
/** SOCKS version */
enum SOCKSVersion: uint8_t {
SOCKS4 = 0x04,
SOCKS5 = 0x05
};
/** Values defined for METHOD in RFC1928 */
enum SOCKS5Method: uint8_t {
NOAUTH = 0x00, //! No authentication required
GSSAPI = 0x01, //! GSSAPI
USER_PASS = 0x02, //! Username/password
NO_ACCEPTABLE = 0xff, //! No acceptable methods
};
/** Values defined for CMD in RFC1928 */
enum SOCKS5Command: uint8_t {
CONNECT = 0x01,
BIND = 0x02,
UDP_ASSOCIATE = 0x03
};
/** Values defined for REP in RFC1928 */
enum SOCKS5Reply: uint8_t {
SUCCEEDED = 0x00, //! Succeeded
GENFAILURE = 0x01, //! General failure
NOTALLOWED = 0x02, //! Connection not allowed by ruleset
NETUNREACHABLE = 0x03, //! Network unreachable
HOSTUNREACHABLE = 0x04, //! Network unreachable
CONNREFUSED = 0x05, //! Connection refused
TTLEXPIRED = 0x06, //! TTL expired
CMDUNSUPPORTED = 0x07, //! Command not supported
ATYPEUNSUPPORTED = 0x08, //! Address type not supported
};
/** Values defined for ATYPE in RFC1928 */
enum SOCKS5Atyp: uint8_t {
IPV4 = 0x01,
DOMAINNAME = 0x03,
IPV6 = 0x04,
};
/** Status codes that can be returned by InterruptibleRecv */
enum class IntrRecvError {
OK,
Timeout,
@ -217,7 +259,7 @@ enum class IntrRecvError {
*
* @note This function requires that hSocket is in non-blocking mode.
*/
static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, SOCKET& hSocket)
static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, SOCKET& hSocket)
{
int64_t curTime = GetTimeMillis();
int64_t endTime = curTime + timeout;
@ -225,7 +267,7 @@ static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, SOCK
// to break off in case of an interruption.
const int64_t maxWait = 1000;
while (len > 0 && curTime < endTime) {
ssize_t ret = recv(hSocket, data, len, 0); // Optimistically try the recv first
ssize_t ret = recv(hSocket, (char*)data, len, 0); // Optimistically try the recv first
if (ret > 0) {
len -= ret;
data += ret;
@ -256,24 +298,35 @@ static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, SOCK
return len == 0 ? IntrRecvError::OK : IntrRecvError::Timeout;
}
/** Credentials for proxy authentication */
struct ProxyCredentials
{
std::string username;
std::string password;
};
std::string Socks5ErrorString(int err)
/** Convert SOCKS5 reply to a an error message */
std::string Socks5ErrorString(uint8_t err)
{
switch(err) {
case 0x01: return "general failure";
case 0x02: return "connection not allowed";
case 0x03: return "network unreachable";
case 0x04: return "host unreachable";
case 0x05: return "connection refused";
case 0x06: return "TTL expired";
case 0x07: return "protocol error";
case 0x08: return "address type not supported";
default: return "unknown";
case SOCKS5Reply::GENFAILURE:
return "general failure";
case SOCKS5Reply::NOTALLOWED:
return "connection not allowed";
case SOCKS5Reply::NETUNREACHABLE:
return "network unreachable";
case SOCKS5Reply::HOSTUNREACHABLE:
return "host unreachable";
case SOCKS5Reply::CONNREFUSED:
return "connection refused";
case SOCKS5Reply::TTLEXPIRED:
return "TTL expired";
case SOCKS5Reply::CMDUNSUPPORTED:
return "protocol error";
case SOCKS5Reply::ATYPEUNSUPPORTED:
return "address type not supported";
default:
return "unknown";
}
}
@ -288,34 +341,34 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
}
// Accepted authentication methods
std::vector<uint8_t> vSocks5Init;
vSocks5Init.push_back(0x05);
vSocks5Init.push_back(SOCKSVersion::SOCKS5);
if (auth) {
vSocks5Init.push_back(0x02); // # METHODS
vSocks5Init.push_back(0x00); // X'00' NO AUTHENTICATION REQUIRED
vSocks5Init.push_back(0x02); // X'02' USERNAME/PASSWORD (RFC1929)
vSocks5Init.push_back(0x02); // Number of methods
vSocks5Init.push_back(SOCKS5Method::NOAUTH);
vSocks5Init.push_back(SOCKS5Method::USER_PASS);
} else {
vSocks5Init.push_back(0x01); // # METHODS
vSocks5Init.push_back(0x00); // X'00' NO AUTHENTICATION REQUIRED
vSocks5Init.push_back(0x01); // Number of methods
vSocks5Init.push_back(SOCKS5Method::NOAUTH);
}
ssize_t ret = send(hSocket, (const char*)vSocks5Init.data(), vSocks5Init.size(), MSG_NOSIGNAL);
if (ret != (ssize_t)vSocks5Init.size()) {
CloseSocket(hSocket);
return error("Error sending to proxy");
}
char pchRet1[2];
uint8_t pchRet1[2];
if ((recvr = InterruptibleRecv(pchRet1, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
CloseSocket(hSocket);
LogPrintf("Socks5() connect to %s:%d failed: InterruptibleRecv() timeout or other failure\n", strDest, port);
return false;
}
if (pchRet1[0] != 0x05) {
if (pchRet1[0] != SOCKSVersion::SOCKS5) {
CloseSocket(hSocket);
return error("Proxy failed to initialize");
}
if (pchRet1[1] == 0x02 && auth) {
if (pchRet1[1] == SOCKS5Method::USER_PASS && auth) {
// Perform username/password authentication (as described in RFC1929)
std::vector<uint8_t> vAuth;
vAuth.push_back(0x01);
vAuth.push_back(0x01); // Current (and only) version of user/pass subnegotiation
if (auth->username.size() > 255 || auth->password.size() > 255)
return error("Proxy username or password too long");
vAuth.push_back(auth->username.size());
@ -328,7 +381,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
return error("Error sending authentication to proxy");
}
LogPrint("proxy", "SOCKS5 sending proxy authentication %s:%s\n", auth->username, auth->password);
char pchRetA[2];
uint8_t pchRetA[2];
if ((recvr = InterruptibleRecv(pchRetA, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
CloseSocket(hSocket);
return error("Error reading proxy authentication response");
@ -337,17 +390,17 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
CloseSocket(hSocket);
return error("Proxy authentication unsuccessful");
}
} else if (pchRet1[1] == 0x00) {
} else if (pchRet1[1] == SOCKS5Method::NOAUTH) {
// Perform no authentication
} else {
CloseSocket(hSocket);
return error("Proxy requested wrong authentication method %02x", pchRet1[1]);
}
std::vector<uint8_t> vSocks5;
vSocks5.push_back(0x05); // VER protocol version
vSocks5.push_back(0x01); // CMD CONNECT
vSocks5.push_back(0x00); // RSV Reserved
vSocks5.push_back(0x03); // ATYP DOMAINNAME
vSocks5.push_back(SOCKSVersion::SOCKS5); // VER protocol version
vSocks5.push_back(SOCKS5Command::CONNECT); // CMD CONNECT
vSocks5.push_back(0x00); // RSV Reserved must be 0
vSocks5.push_back(SOCKS5Atyp::DOMAINNAME); // ATYP DOMAINNAME
vSocks5.push_back(strDest.size()); // Length<=255 is checked at beginning of function
vSocks5.insert(vSocks5.end(), strDest.begin(), strDest.end());
vSocks5.push_back((port >> 8) & 0xFF);
@ -357,7 +410,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
CloseSocket(hSocket);
return error("Error sending to proxy");
}
char pchRet2[4];
uint8_t pchRet2[4];
if ((recvr = InterruptibleRecv(pchRet2, 4, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
CloseSocket(hSocket);
if (recvr == IntrRecvError::Timeout) {
@ -369,26 +422,26 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
return error("Error while reading proxy response");
}
}
if (pchRet2[0] != 0x05) {
if (pchRet2[0] != SOCKSVersion::SOCKS5) {
CloseSocket(hSocket);
return error("Proxy failed to accept request");
}
if (pchRet2[1] != 0x00) {
if (pchRet2[1] != SOCKS5Reply::SUCCEEDED) {
// Failures to connect to a peer that are not proxy errors
CloseSocket(hSocket);
LogPrintf("Socks5() connect to %s:%d failed: %s\n", strDest, port, Socks5ErrorString(pchRet2[1]));
return false;
}
if (pchRet2[2] != 0x00) {
if (pchRet2[2] != 0x00) { // Reserved field must be 0
CloseSocket(hSocket);
return error("Error: malformed proxy response");
}
char pchRet3[256];
uint8_t pchRet3[256];
switch (pchRet2[3])
{
case 0x01: recvr = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, hSocket); break;
case 0x04: recvr = InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, hSocket); break;
case 0x03:
case SOCKS5Atyp::IPV4: recvr = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, hSocket); break;
case SOCKS5Atyp::IPV6: recvr = InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, hSocket); break;
case SOCKS5Atyp::DOMAINNAME:
{
recvr = InterruptibleRecv(pchRet3, 1, SOCKS5_RECV_TIMEOUT, hSocket);
if (recvr != IntrRecvError::OK) {

View File

@ -55,12 +55,17 @@ void CPrivateSendClientManager::ProcessMessage(CNode* pfrom, const std::string&
// LogPrint("privatesend", "DSQUEUE -- %s seen\n", dsq.ToString());
return;
}
if (q.fReady == dsq.fReady && q.masternodeOutpoint == dsq.masternodeOutpoint) {
// no way the same mn can send another dsq with the same readiness this soon
LogPrint("privatesend", "DSQUEUE -- Peer %d is sending WAY too many dsq messages for a masternode with collateral %s\n", pfrom->id, dsq.masternodeOutpoint.ToStringShort());
return;
}
}
} // cs_vecqueue
LogPrint("privatesend", "DSQUEUE -- %s new\n", dsq.ToString());
if (dsq.IsExpired()) return;
if (dsq.IsTimeOutOfBounds()) return;
auto mnList = deterministicMNManager->GetListAtChainTip();
auto dmn = mnList.GetValidMNByCollateral(dsq.masternodeOutpoint);
@ -84,18 +89,6 @@ void CPrivateSendClientManager::ProcessMessage(CNode* pfrom, const std::string&
}
}
} else {
LOCK(cs_deqsessions); // have to lock this first to avoid deadlocks with cs_vecqueue
TRY_LOCK(cs_vecqueue, lockRecv);
if (!lockRecv) return;
for (const auto& q : vecPrivateSendQueue) {
if (q.masternodeOutpoint == dsq.masternodeOutpoint) {
// no way same mn can send another "not yet ready" dsq this soon
LogPrint("privatesend", "DSQUEUE -- Masternode %s is sending WAY too many dsq messages\n", dmn->pdmnState->ToString());
return;
}
}
int64_t nLastDsq = mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastDsq();
int nThreshold = nLastDsq + mnList.GetValidMNsCount() / 5;
LogPrint("privatesend", "DSQUEUE -- nLastDsq: %d threshold: %d nDsqCount: %d\n", nLastDsq, nThreshold, mmetaman.GetDsqCount());
@ -108,12 +101,17 @@ void CPrivateSendClientManager::ProcessMessage(CNode* pfrom, const std::string&
mmetaman.AllowMixing(dmn->proTxHash);
LogPrint("privatesend", "DSQUEUE -- new PrivateSend queue (%s) from masternode %s\n", dsq.ToString(), dmn->pdmnState->addr.ToString());
LOCK(cs_deqsessions);
for (auto& session : deqSessions) {
CDeterministicMNCPtr mnMixing;
if (session.GetMixingMasternodeInfo(mnMixing) && mnMixing->collateralOutpoint == dsq.masternodeOutpoint) {
dsq.fTried = true;
}
}
TRY_LOCK(cs_vecqueue, lockRecv);
if (!lockRecv) return;
vecPrivateSendQueue.push_back(dsq);
dsq.Relay(connman);
}
@ -1425,8 +1423,12 @@ bool CPrivateSendClientSession::MakeCollateralAmounts(CConnman& connman)
{
if (!pwalletMain) return false;
// NOTE: We do not allow txes larger than 100kB, so we have to limit number of inputs here.
// We still want to consume a lot of inputs to avoid creating only smaller denoms though.
// Knowing that each CTxIn is at least 148b big, 400 inputs should take 400 x ~148b = ~60kB.
// This still leaves more than enough room for another data of typical MakeCollateralAmounts tx.
std::vector<CompactTallyItem> vecTally;
if (!pwalletMain->SelectCoinsGroupedByAddresses(vecTally, false, false)) {
if (!pwalletMain->SelectCoinsGroupedByAddresses(vecTally, false, false, true, 400)) {
LogPrint("privatesend", "CPrivateSendClientSession::MakeCollateralAmounts -- SelectCoinsGroupedByAddresses can't find any inputs!\n");
return false;
}

View File

@ -93,9 +93,6 @@ void CPrivateSendServer::ProcessMessage(CNode* pfrom, const std::string& strComm
}
} else if (strCommand == NetMsgType::DSQUEUE) {
TRY_LOCK(cs_vecqueue, lockRecv);
if (!lockRecv) return;
if (pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) {
LogPrint("privatesend", "DSQUEUE -- peer=%d using obsolete version %i\n", pfrom->id, pfrom->nVersion);
connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, strprintf("Version must be %d or greater", MIN_PRIVATESEND_PEER_PROTO_VERSION)));
@ -105,17 +102,27 @@ void CPrivateSendServer::ProcessMessage(CNode* pfrom, const std::string& strComm
CPrivateSendQueue dsq;
vRecv >> dsq;
// process every dsq only once
for (const auto& q : vecPrivateSendQueue) {
if (q == dsq) {
// LogPrint("privatesend", "DSQUEUE -- %s seen\n", dsq.ToString());
return;
{
TRY_LOCK(cs_vecqueue, lockRecv);
if (!lockRecv) return;
// process every dsq only once
for (const auto& q : vecPrivateSendQueue) {
if (q == dsq) {
// LogPrint("privatesend", "DSQUEUE -- %s seen\n", dsq.ToString());
return;
}
if (q.fReady == dsq.fReady && q.masternodeOutpoint == dsq.masternodeOutpoint) {
// no way the same mn can send another dsq with the same readiness this soon
LogPrint("privatesend", "DSQUEUE -- Peer %d is sending WAY too many dsq messages for a masternode with collateral %s\n", pfrom->id, dsq.masternodeOutpoint.ToStringShort());
return;
}
}
}
} // cs_vecqueue
LogPrint("privatesend", "DSQUEUE -- %s new\n", dsq.ToString());
if (dsq.IsExpired()) return;
if (dsq.IsTimeOutOfBounds()) return;
auto mnList = deterministicMNManager->GetListAtChainTip();
auto dmn = mnList.GetValidMNByCollateral(dsq.masternodeOutpoint);
@ -128,14 +135,6 @@ void CPrivateSendServer::ProcessMessage(CNode* pfrom, const std::string& strComm
}
if (!dsq.fReady) {
for (const auto& q : vecPrivateSendQueue) {
if (q.masternodeOutpoint == dsq.masternodeOutpoint) {
// no way same mn can send another "not yet ready" dsq this soon
LogPrint("privatesend", "DSQUEUE -- Masternode %s is sending WAY too many dsq messages\n", dmn->pdmnState->addr.ToString());
return;
}
}
int64_t nLastDsq = mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastDsq();
int nThreshold = nLastDsq + mnList.GetValidMNsCount() / 5;
LogPrint("privatesend", "DSQUEUE -- nLastDsq: %d threshold: %d nDsqCount: %d\n", nLastDsq, nThreshold, mmetaman.GetDsqCount());
@ -147,6 +146,9 @@ void CPrivateSendServer::ProcessMessage(CNode* pfrom, const std::string& strComm
mmetaman.AllowMixing(dmn->proTxHash);
LogPrint("privatesend", "DSQUEUE -- new PrivateSend queue (%s) from masternode %s\n", dsq.ToString(), dmn->pdmnState->addr.ToString());
TRY_LOCK(cs_vecqueue, lockRecv);
if (!lockRecv) return;
vecPrivateSendQueue.push_back(dsq);
dsq.Relay(connman);
}
@ -507,10 +509,11 @@ void CPrivateSendServer::ChargeRandomFees(CConnman& connman)
void CPrivateSendServer::CheckTimeout(CConnman& connman)
{
if (!fMasternodeMode) return;
if (nState == POOL_STATE_IDLE) return;
CheckQueue();
if (nState == POOL_STATE_IDLE) return;
int nTimeout = (nState == POOL_STATE_SIGNING) ? PRIVATESEND_SIGNING_TIMEOUT : PRIVATESEND_QUEUE_TIMEOUT;
bool fTimeout = GetTime() - nTimeLastSuccessfulStep >= nTimeout;

View File

@ -80,6 +80,11 @@ bool CPrivateSendQueue::Relay(CConnman& connman)
return true;
}
bool CPrivateSendQueue::IsTimeOutOfBounds() const
{
return GetAdjustedTime() - nTime > PRIVATESEND_QUEUE_TIMEOUT || nTime - GetAdjustedTime() > PRIVATESEND_QUEUE_TIMEOUT;
}
uint256 CPrivateSendBroadcastTx::GetSignatureHash() const
{
return SerializeHash(*this);
@ -149,8 +154,8 @@ void CPrivateSendBaseManager::CheckQueue()
// check mixing queue objects for timeouts
std::vector<CPrivateSendQueue>::iterator it = vecPrivateSendQueue.begin();
while (it != vecPrivateSendQueue.end()) {
if ((*it).IsExpired()) {
LogPrint("privatesend", "CPrivateSendBaseManager::%s -- Removing expired queue (%s)\n", __func__, (*it).ToString());
if ((*it).IsTimeOutOfBounds()) {
LogPrint("privatesend", "CPrivateSendBaseManager::%s -- Removing a queue (%s)\n", __func__, (*it).ToString());
it = vecPrivateSendQueue.erase(it);
} else
++it;
@ -164,7 +169,7 @@ bool CPrivateSendBaseManager::GetQueueItemAndTry(CPrivateSendQueue& dsqRet)
for (auto& dsq : vecPrivateSendQueue) {
// only try each queue once
if (dsq.fTried || dsq.IsExpired()) continue;
if (dsq.fTried || dsq.IsTimeOutOfBounds()) continue;
dsq.fTried = true;
dsqRet = dsq;
return true;

View File

@ -229,8 +229,8 @@ public:
bool Relay(CConnman& connman);
/// Is this queue expired?
bool IsExpired() { return GetAdjustedTime() - nTime > PRIVATESEND_QUEUE_TIMEOUT; }
/// Check if a queue is too old or too far into the future
bool IsTimeOutOfBounds() const;
std::string ToString() const
{

View File

@ -136,7 +136,6 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "prioritisetransaction", 1, "fee_delta" },
{ "setban", 2, "bantime" },
{ "setban", 3, "absolute" },
{ "setbip69enabled", 0, "enabled" },
{ "setnetworkactive", 0, "state" },
{ "setprivatesendrounds", 0, "rounds" },
{ "setprivatesendamount", 0, "amount" },

View File

@ -354,7 +354,7 @@ UniValue masternode_status(const JSONRPCRequest& request)
mnObj.push_back(Pair("outpoint", activeMasternodeInfo.outpoint.ToStringShort()));
mnObj.push_back(Pair("service", activeMasternodeInfo.service.ToString()));
auto dmn = activeMasternodeManager->GetDMN();
auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(activeMasternodeInfo.proTxHash);
if (dmn) {
mnObj.push_back(Pair("proTxHash", dmn->proTxHash.ToString()));
mnObj.push_back(Pair("collateralHash", dmn->collateralOutpoint.hash.ToString()));

View File

@ -193,6 +193,10 @@ bool CBlockTreeDB::WriteBatchSync(const std::vector<std::pair<int, const CBlockF
return WriteBatch(batch, true);
}
bool CBlockTreeDB::HasTxIndex(const uint256& txid) {
return Exists(std::make_pair(DB_TXINDEX, txid));
}
bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) {
return Read(std::make_pair(DB_TXINDEX, txid), pos);
}

View File

@ -122,6 +122,7 @@ public:
bool ReadLastBlockFile(int &nFile);
bool WriteReindexing(bool fReindex);
bool ReadReindexing(bool &fReindex);
bool HasTxIndex(const uint256 &txid);
bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos);
bool WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> > &list);
bool ReadSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value);

View File

@ -1755,6 +1755,13 @@ static DisconnectResult DisconnectBlock(const CBlock& block, CValidationState& s
// move best block pointer to prevout block
view.SetBestBlock(pindex->pprev->GetBlockHash());
if (fSpentIndex) {
if (!pblocktree->UpdateSpentIndex(spentIndex)) {
AbortNode("Failed to delete spent index");
return DISCONNECT_FAILED;
}
}
if (fAddressIndex) {
if (!pblocktree->EraseAddressIndex(addressIndex)) {
AbortNode(state, "Failed to delete address index");

View File

@ -2977,27 +2977,6 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
return result;
}
UniValue setbip69enabled(const JSONRPCRequest& request)
{
CWallet* const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
return NullUniValue;
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
"setbip69enabled enable\n"
"\nEnable/Disable BIP69 input/output sorting (-regtest only)\n"
"\nArguments:\n"
"1. enable (bool, required) true or false"
);
if (Params().NetworkIDString() != CBaseChainParams::REGTEST)
throw std::runtime_error("setbip69enabled for regression testing (-regtest mode) only");
bBIP69Enabled = request.params[0].get_bool();
return NullUniValue;
}
extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
extern UniValue importprivkey(const JSONRPCRequest& request);
extern UniValue importaddress(const JSONRPCRequest& request);
@ -3068,8 +3047,6 @@ static const CRPCCommand commands[] =
{ "wallet", "instantsendtoaddress", &instantsendtoaddress, false, {"address","amount","comment","comment_to","subtractfeefromamount"} },
{ "wallet", "dumphdinfo", &dumphdinfo, true, {} },
{ "wallet", "importelectrumwallet", &importelectrumwallet, true, {"filename", "index"} },
{ "hidden", "setbip69enabled", &setbip69enabled, true, {} },
};
void RegisterWalletRPCCommands(CRPCTable &t)

View File

@ -50,7 +50,6 @@ CWallet* pwalletMain = NULL;
CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE);
unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET;
bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE;
bool bBIP69Enabled = true;
const char * DEFAULT_WALLET_DAT = "wallet.dat";
@ -1314,6 +1313,12 @@ void CWallet::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex,
{
LOCK2(cs_main, cs_wallet);
// v0.14.0.x: Simulates the behavior found in the develop branch when ::BlockConnected/BlockDisconnected are called
if (pindex != nullptr && (posInBlock == 0 || posInBlock == CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK)) {
fAnonymizableTallyCached = false;
fAnonymizableTallyCachedNonDenom = false;
}
if (!AddToWalletIfInvolvingMe(tx, pindex, posInBlock, true))
return; // Not one of ours
@ -2565,6 +2570,7 @@ static void ApproximateBestSubset(std::vector<std::pair<CAmount, std::pair<const
vfBest.assign(vValue.size(), true);
nBest = nTotalLower;
int nBestInputCount = 0;
if (!llmq::IsOldInstantSendEnabled()) {
// The new system does not require special handling for InstantSend as this is all done in CInstantSendManager.
@ -2578,6 +2584,7 @@ static void ApproximateBestSubset(std::vector<std::pair<CAmount, std::pair<const
{
vfIncluded.assign(vValue.size(), false);
CAmount nTotal = 0;
int nTotalInputCount = 0;
bool fReachedTarget = false;
for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++)
{
@ -2595,16 +2602,19 @@ static void ApproximateBestSubset(std::vector<std::pair<CAmount, std::pair<const
if (nPass == 0 ? insecure_rand.rand32()&1 : !vfIncluded[i])
{
nTotal += vValue[i].first;
++nTotalInputCount;
vfIncluded[i] = true;
if (nTotal >= nTargetValue)
{
fReachedTarget = true;
if (nTotal < nBest)
if (nTotal < nBest || (nTotal == nBest && nTotalInputCount < nBestInputCount))
{
nBest = nTotal;
nBestInputCount = nTotalInputCount;
vfBest = vfIncluded;
}
nTotal -= vValue[i].first;
--nTotalInputCount;
vfIncluded[i] = false;
}
}
@ -2690,8 +2700,10 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin
const CWalletTx *pcoin = output.tx;
bool fLockedByIS = pcoin->IsLockedByInstantSend();
// if (fDebug) LogPrint("selectcoins", "value %s confirms %d\n", FormatMoney(pcoin->vout[output.i].nValue), output.nDepth);
if (output.nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? nConfMine : nConfTheirs))
if (output.nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? nConfMine : nConfTheirs) && !fLockedByIS)
continue;
if (!mempool.TransactionWithinChainLimit(pcoin->GetHash(), nMaxAncestors))
@ -2767,7 +2779,7 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin
CAmount nBest;
ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest, fUseInstantSend);
if (nBest != nTargetValue && nTotalLower >= nTargetValue + nMinChange)
if (nBest != nTargetValue && nMinChange != 0 && nTotalLower >= nTargetValue + nMinChange)
ApproximateBestSubset(vValue, nTotalLower, nTargetValue + nMinChange, vfBest, nBest, fUseInstantSend);
// If we have a bigger coin and (either the stochastic approximation didn't find a good solution,
@ -3001,8 +3013,9 @@ bool CWallet::SelectCoinsGroupedByAddresses(std::vector<CompactTallyItem>& vecTa
isminefilter filter = ISMINE_SPENDABLE;
// try to use cache for already confirmed anonymizable inputs, no cache should be used when the limit is specified
if(nMaxOupointsPerAddress != -1 && fAnonymizable && fSkipUnconfirmed) {
// Try using the cache for already confirmed anonymizable inputs.
// This should only be used if nMaxOupointsPerAddress was NOT specified.
if(nMaxOupointsPerAddress == -1 && fAnonymizable && fSkipUnconfirmed) {
if(fSkipDenominated && fAnonymizableTallyCachedNonDenom) {
vecTallyRet = vecAnonymizableTallyCachedNonDenom;
LogPrint("selectcoins", "SelectCoinsGroupedByAddresses - using cache for non-denom inputs %d\n", vecTallyRet.size());
@ -3074,8 +3087,9 @@ bool CWallet::SelectCoinsGroupedByAddresses(std::vector<CompactTallyItem>& vecTa
vecTallyRet.push_back(item.second);
}
// cache already confirmed anonymizable entries for later use, no cache should be saved when the limit is specified
if(nMaxOupointsPerAddress != -1 && fAnonymizable && fSkipUnconfirmed) {
// Cache already confirmed anonymizable entries for later use.
// This should only be used if nMaxOupointsPerAddress was NOT specified.
if(nMaxOupointsPerAddress == -1 && fAnonymizable && fSkipUnconfirmed) {
if(fSkipDenominated) {
vecAnonymizableTallyCachedNonDenom = vecTallyRet;
fAnonymizableTallyCachedNonDenom = true;
@ -3578,7 +3592,8 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
txNew.vin.push_back(txin);
}
if (bBIP69Enabled) {
// If no specific change position was requested, apply BIP69
if (nChangePosRequest == -1) {
std::sort(txNew.vin.begin(), txNew.vin.end(), CompareInputBIP69());
std::sort(vecTxDSInTmp.begin(), vecTxDSInTmp.end(), CompareInputBIP69());
std::sort(txNew.vout.begin(), txNew.vout.end(), CompareOutputBIP69());

View File

@ -43,7 +43,6 @@ extern CWallet* pwalletMain;
extern CFeeRate payTxFee;
extern unsigned int nTxConfirmTarget;
extern bool bSpendZeroConfChange;
extern bool bBIP69Enabled;
static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000;
//! -paytxfee default