Merge pull request #3282 from UdjinM6/bprc2

Backport "candidates" from develop to v0.15.x
This commit is contained in:
UdjinM6 2020-01-11 04:04:10 +03:00 committed by GitHub
commit 9d48227b04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 601 additions and 387 deletions

View File

@ -38,7 +38,7 @@ stages:
if wget --quiet -O cache-artifact.zip https://gitlab.com/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME/-/jobs/artifacts/develop/download?job=$CI_JOB_NAME; then
unzip -q cache-artifact.zip
rm cache-artifact.zip
mv cache-artifact/* $CACHE_DIR/
mv cache-artifact/* $CACHE_DIR/ || true
else
echo "Failed to download cache"
fi
@ -65,7 +65,22 @@ stages:
- pip3 install jinja2
# Setup some environment variables
- if [ "$CI_EXTERNAL_PULL_REQUEST_IID" != "" ]; then export PULL_REQUEST="true"; else export PULL_REQUEST="false"; fi
- |
if [ "$CI_EXTERNAL_PULL_REQUEST_IID" != "" ]; then
export PULL_REQUEST="true"
else
# CI_EXTERNAL_PULL_REQUEST_IID is false every time until https://gitlab.com/gitlab-org/gitlab/issues/5667 is done
# Until then, we're using https://github.com/brndnmtthws/labhub atm to mirror Github pull requests as branches into Gitlab,
# which allows us to use Gitlab CI for Github. The following check detects such mirrored branches.
if [[ $CI_COMMIT_REF_NAME =~ ^pr-[^/]*/[^/]*/[^/]*/[^/]*$ ]]; then
export PULL_REQUEST="true"
# CI_COMMIT_BEFORE_SHA is also invalid until #5667 is implemented, so we need to figure it out by ourself
git fetch origin develop
export CI_COMMIT_BEFORE_SHA="$(git merge-base origin/develop HEAD)"
else
export PULL_REQUEST="false"
fi
fi
- export COMMIT_RANGE="$CI_COMMIT_BEFORE_SHA..$CI_COMMIT_SHA"
- export JOB_NUMBER="$CI_JOB_ID"
- export HOST_SRC_DIR=$CI_PROJECT_DIR

View File

@ -36,7 +36,9 @@ if [ "$BUILD_TARGET" = "arm-linux" ]; then
export HOST=arm-linux-gnueabihf
export PACKAGES="g++-arm-linux-gnueabihf"
export CHECK_DOC=1
export BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports"
# -Wno-psabi is to disable ABI warnings: "note: parameter passing for argument of type ... changed in GCC 7.1"
# This could be removed once the ABI change warning does not show up by default
export BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports CXXFLAGS=-Wno-psabi"
elif [ "$BUILD_TARGET" = "win32" ]; then
export HOST=i686-w64-mingw32
export DPKG_ADD_ARCH="i386"

View File

@ -51,10 +51,8 @@ def build():
os.chdir('gitian-builder')
os.makedirs('inputs', exist_ok=True)
subprocess.check_call(['wget', '-N', '-P', 'inputs', 'https://downloads.sourceforge.net/project/osslsigncode/osslsigncode/osslsigncode-1.7.1.tar.gz'])
subprocess.check_call(['wget', '-N', '-P', 'inputs', 'https://bitcoincore.org/cfields/osslsigncode-Backports-to-1.7.1.patch'])
subprocess.check_output(["echo 'a8c4e9cafba922f89de0df1f2152e7be286aba73f78505169bc351a7938dd911 inputs/osslsigncode-Backports-to-1.7.1.patch' | sha256sum -c"], shell=True)
subprocess.check_output(["echo 'f9a8cdb38b9c309326764ebc937cba1523a3a751a7ab05df3ecc99d18ae466c9 inputs/osslsigncode-1.7.1.tar.gz' | sha256sum -c"], shell=True)
subprocess.check_call(['wget', '-O', 'inputs/osslsigncode-2.0.tar.gz', 'https://github.com/mtrojnar/osslsigncode/archive/2.0.tar.gz'])
subprocess.check_call(["echo '5a60e0a4b3e0b4d655317b2f12a810211c50242138322b16e7e01c6fbb89d92f inputs/osslsigncode-2.0.tar.gz' | sha256sum -c"], shell=True)
subprocess.check_call(['make', '-C', '../dash/depends', 'download', 'SOURCES_PATH=' + os.getcwd() + '/cache/common'])
if args.linux:

View File

@ -5,31 +5,30 @@ suites:
architectures:
- "amd64"
packages:
# Once osslsigncode supports openssl 1.1, we can change this back to libssl-dev
- "libssl1.0-dev"
- "libssl-dev"
- "autoconf"
- "libtool"
- "pkg-config"
remotes:
- "url": "https://github.com/dashpay/dash-detached-sigs.git"
"dir": "signature"
files:
- "osslsigncode-1.7.1.tar.gz"
- "osslsigncode-Backports-to-1.7.1.patch"
- "osslsigncode-2.0.tar.gz"
- "dashcore-win-unsigned.tar.gz"
script: |
BUILD_DIR=`pwd`
SIGDIR=${BUILD_DIR}/signature/win
UNSIGNED_DIR=${BUILD_DIR}/unsigned
echo "f9a8cdb38b9c309326764ebc937cba1523a3a751a7ab05df3ecc99d18ae466c9 osslsigncode-1.7.1.tar.gz" | sha256sum -c
echo "a8c4e9cafba922f89de0df1f2152e7be286aba73f78505169bc351a7938dd911 osslsigncode-Backports-to-1.7.1.patch" | sha256sum -c
echo "5a60e0a4b3e0b4d655317b2f12a810211c50242138322b16e7e01c6fbb89d92f osslsigncode-2.0.tar.gz" | sha256sum -c
mkdir -p ${UNSIGNED_DIR}
tar -C ${UNSIGNED_DIR} -xf dashcore-win-unsigned.tar.gz
tar xf osslsigncode-1.7.1.tar.gz
cd osslsigncode-1.7.1
patch -p1 < ${BUILD_DIR}/osslsigncode-Backports-to-1.7.1.patch
tar xf osslsigncode-2.0.tar.gz
cd osslsigncode-2.0
./autogen.sh
./configure --without-gsf --without-curl --disable-dependency-tracking
make
find ${UNSIGNED_DIR} -name "*-unsigned.exe" | while read i; do

View File

@ -80,8 +80,8 @@ Ensure gitian-builder is up-to-date:
pushd ./gitian-builder
mkdir -p inputs
wget -P inputs https://bitcoincore.org/cfields/osslsigncode-Backports-to-1.7.1.patch
wget -P inputs http://downloads.sourceforge.net/project/osslsigncode/osslsigncode/osslsigncode-1.7.1.tar.gz
wget -O inputs/osslsigncode-2.0.tar.gz https://github.com/mtrojnar/osslsigncode/archive/2.0.tar.gz
echo '5a60e0a4b3e0b4d655317b2f12a810211c50242138322b16e7e01c6fbb89d92f inputs/osslsigncode-2.0.tar.gz' | sha256sum -c
popd
Create the OS X SDK tarball, see the [OS X readme](README_osx.md) for details, and copy it into the inputs directory.

View File

@ -339,6 +339,7 @@ libdash_server_a_SOURCES = \
rpc/rpcevo.cpp \
rpc/rpcquorums.cpp \
rpc/server.cpp \
rpc/privatesend.cpp \
script/sigcache.cpp \
script/ismine.cpp \
spork.cpp \

View File

@ -101,12 +101,16 @@ void CMNAuth::ProcessMessage(CNode* pnode, const std::string& strCommand, CDataS
connman.ForEachNode([&](CNode* pnode2) {
if (pnode2->verifiedProRegTxHash == mnauth.proRegTxHash) {
LogPrint(BCLog::NET, "CMNAuth::ProcessMessage -- Masternode %s has already verified as peer %d, dropping old connection. peer=%d\n",
LogPrint(BCLog::NET, "CMNAuth::ProcessMessage -- Masternode %s has already verified as peer %d, dropping new connection. peer=%d\n",
mnauth.proRegTxHash.ToString(), pnode2->GetId(), pnode->GetId());
pnode2->fDisconnect = true;
pnode->fDisconnect = true;
}
});
if (pnode->fDisconnect) {
return;
}
{
LOCK(pnode->cs_mnauth);
pnode->verifiedProRegTxHash = mnauth.proRegTxHash;

View File

@ -465,6 +465,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-minimumchainwork=<hex>", strprintf("Minimum work assumed to exist on a valid chain in hex (default: %s, testnet: %s)", defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnetChainParams->GetConsensus().nMinimumChainWork.GetHex()));
}
strUsage += HelpMessageOpt("-persistmempool", strprintf(_("Whether to save the mempool on shutdown and load on restart (default: %u)"), DEFAULT_PERSIST_MEMPOOL));
strUsage += HelpMessageOpt("-syncmempool", strprintf(_("Sync mempool from other nodes on start (default: %u)"), DEFAULT_SYNC_MEMPOOL));
strUsage += HelpMessageOpt("-blockreconstructionextratxn=<n>", strprintf(_("Extra transactions to keep in memory for compact block reconstructions (default: %u)"), DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN));
strUsage += HelpMessageOpt("-par=<n>", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"),
-GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS));
@ -609,7 +610,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-privatesendmultisession", strprintf(_("Enable multiple PrivateSend mixing sessions per block, experimental (0-1, default: %u)"), DEFAULT_PRIVATESEND_MULTISESSION));
strUsage += HelpMessageOpt("-privatesendsessions=<n>", strprintf(_("Use N separate masternodes in parallel to mix funds (%u-%u, default: %u)"), MIN_PRIVATESEND_SESSIONS, MAX_PRIVATESEND_SESSIONS, DEFAULT_PRIVATESEND_SESSIONS));
strUsage += HelpMessageOpt("-privatesendrounds=<n>", strprintf(_("Use N separate masternodes for each denominated input to mix funds (%u-%u, default: %u)"), MIN_PRIVATESEND_ROUNDS, MAX_PRIVATESEND_ROUNDS, DEFAULT_PRIVATESEND_ROUNDS));
strUsage += HelpMessageOpt("-privatesendamount=<n>", strprintf(_("Keep N DASH mixed (%u-%u, default: %u)"), MIN_PRIVATESEND_AMOUNT, MAX_PRIVATESEND_AMOUNT, DEFAULT_PRIVATESEND_AMOUNT));
strUsage += HelpMessageOpt("-privatesendamount=<n>", strprintf(_("Target PrivateSend balance (%u-%u, default: %u)"), MIN_PRIVATESEND_AMOUNT, MAX_PRIVATESEND_AMOUNT, DEFAULT_PRIVATESEND_AMOUNT));
strUsage += HelpMessageOpt("-privatesenddenoms=<n>", strprintf(_("Create up to N inputs of each denominated amount (%u-%u, default: %u)"), MIN_PRIVATESEND_DENOMS, MAX_PRIVATESEND_DENOMS, DEFAULT_PRIVATESEND_DENOMS));
#endif // ENABLE_WALLET
@ -1973,6 +1974,14 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
LogPrintf("No wallet support compiled in!\n");
#endif
// As InitLoadWallet can take several minutes, it's possible the user
// requested to kill the GUI during the last operation. If so, exit.
if (fRequestShutdown)
{
LogPrintf("Shutdown requested. Exiting.\n");
return false;
}
// ********************************************************* Step 9: data directory maintenance
// if pruning, unset the service bit and perform the initial blockstore prune
@ -1986,6 +1995,14 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
}
}
// As PruneAndFlush can take several minutes, it's possible the user
// requested to kill the GUI during the last operation. If so, exit.
if (fRequestShutdown)
{
LogPrintf("Shutdown requested. Exiting.\n");
return false;
}
// ********************************************************* Step 10a: Prepare Masternode related stuff
fMasternodeMode = false;
std::string strMasterNodeBLSPrivKey = gArgs.GetArg("-masternodeblsprivkey", "");
@ -2057,32 +2074,58 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
// LOAD SERIALIZED DAT FILES INTO DATA CACHES FOR INTERNAL USE
bool fIgnoreCacheFiles = fLiteMode || fReindex || fReindexChainState;
if (!fIgnoreCacheFiles) {
fs::path pathDB = GetDataDir();
std::string strDBName;
bool fLoadCacheFiles = !(fLiteMode || fReindex || fReindexChainState);
{
LOCK(cs_main);
// was blocks/chainstate deleted?
if (chainActive.Tip() == nullptr) {
fLoadCacheFiles = false;
}
}
fs::path pathDB = GetDataDir();
std::string strDBName;
strDBName = "mncache.dat";
uiInterface.InitMessage(_("Loading masternode cache..."));
CFlatDB<CMasternodeMetaMan> flatdb1(strDBName, "magicMasternodeCache");
strDBName = "mncache.dat";
uiInterface.InitMessage(_("Loading masternode cache..."));
CFlatDB<CMasternodeMetaMan> flatdb1(strDBName, "magicMasternodeCache");
if (fLoadCacheFiles) {
if(!flatdb1.Load(mmetaman)) {
return InitError(_("Failed to load masternode cache from") + "\n" + (pathDB / strDBName).string());
}
} else {
CMasternodeMetaMan mmetamanTmp;
if(!flatdb1.Dump(mmetamanTmp)) {
return InitError(_("Failed to clear masternode cache at") + "\n" + (pathDB / strDBName).string());
}
}
strDBName = "governance.dat";
uiInterface.InitMessage(_("Loading governance cache..."));
CFlatDB<CGovernanceManager> flatdb3(strDBName, "magicGovernanceCache");
strDBName = "governance.dat";
uiInterface.InitMessage(_("Loading governance cache..."));
CFlatDB<CGovernanceManager> flatdb3(strDBName, "magicGovernanceCache");
if (fLoadCacheFiles) {
if(!flatdb3.Load(governance)) {
return InitError(_("Failed to load governance cache from") + "\n" + (pathDB / strDBName).string());
}
governance.InitOnLoad();
} else {
CGovernanceManager governanceTmp;
if(!flatdb3.Dump(governanceTmp)) {
return InitError(_("Failed to clear governance cache at") + "\n" + (pathDB / strDBName).string());
}
}
strDBName = "netfulfilled.dat";
uiInterface.InitMessage(_("Loading fulfilled requests cache..."));
CFlatDB<CNetFulfilledRequestManager> flatdb4(strDBName, "magicFulfilledCache");
strDBName = "netfulfilled.dat";
uiInterface.InitMessage(_("Loading fulfilled requests cache..."));
CFlatDB<CNetFulfilledRequestManager> flatdb4(strDBName, "magicFulfilledCache");
if (fLoadCacheFiles) {
if(!flatdb4.Load(netfulfilledman)) {
return InitError(_("Failed to load fulfilled requests cache from") + "\n" + (pathDB / strDBName).string());
}
} else {
CNetFulfilledRequestManager netfulfilledmanTmp;
if(!flatdb4.Dump(netfulfilledmanTmp)) {
return InitError(_("Failed to clear fulfilled requests cache at") + "\n" + (pathDB / strDBName).string());
}
}
// ********************************************************* Step 10c: schedule Dash-specific tasks
@ -2138,6 +2181,14 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
uiInterface.NotifyBlockTip.disconnect(BlockNotifyGenesisWait);
}
// As importing blocks can take several minutes, it's possible the user
// requested to kill the GUI during one of the last operations. If so, exit.
if (fRequestShutdown)
{
LogPrintf("Shutdown requested. Exiting.\n");
return false;
}
// ********************************************************* Step 12: start node
//// debug print
@ -2211,5 +2262,12 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
}
#endif
// Final check if the user requested to kill the GUI during one of the last operations. If so, exit.
if (fRequestShutdown)
{
LogPrintf("Shutdown requested. Exiting.\n");
return false;
}
return true;
}

View File

@ -45,7 +45,7 @@ void CDKGPendingMessages::PushPendingMessage(NodeId from, CDataStream& vRecv)
LOCK2(cs_main, cs);
if (!seenMessages.emplace(hash).second) {
LogPrint(BCLog::LLMQ_DKG, "CDKGPendingMessages::%s -- already seen %s, peer=%d", __func__, from);
LogPrint(BCLog::LLMQ_DKG, "CDKGPendingMessages::%s -- already seen %s, peer=%d\n", __func__, hash.ToString(), from);
return;
}
@ -115,6 +115,7 @@ void CDKGSessionHandler::UpdatedBlockTip(const CBlockIndex* pindexNew)
int quorumStageInt = pindexNew->nHeight % params.dkgInterval;
const CBlockIndex* pindexQuorum = pindexNew->GetAncestor(pindexNew->nHeight - quorumStageInt);
currentHeight = pindexNew->nHeight;
quorumHeight = pindexQuorum->nHeight;
quorumHash = pindexQuorum->GetBlockHash();
@ -236,22 +237,43 @@ void CDKGSessionHandler::SleepBeforePhase(QuorumPhase curPhase,
return;
}
// expected time for a full phase
double phaseTime = params.dkgPhaseBlocks * Params().GetConsensus().nPowTargetSpacing * 1000;
// expected time per member
phaseTime = phaseTime / params.size;
// Two blocks can come very close to each other, this happens pretty regularly. We don't want to be
// left behind and marked as a bad member. This means that we should not count the last block of the
// phase as a safe one to keep sleeping, that's why we calculate the phase sleep time as a time of
// the full phase minus one block here.
double phaseSleepTime = (params.dkgPhaseBlocks - 1) * Params().GetConsensus().nPowTargetSpacing * 1000;
// Expected phase sleep time per member
double phaseSleepTimePerMember = phaseSleepTime / params.size;
// Don't expect perfect block times and thus reduce the phase time to be on the secure side (caller chooses factor)
phaseTime *= randomSleepFactor;
double adjustedPhaseSleepTimePerMember = phaseSleepTimePerMember * randomSleepFactor;
int64_t sleepTime = (int64_t)(phaseTime * curSession->GetMyMemberIndex());
int64_t sleepTime = (int64_t)(adjustedPhaseSleepTimePerMember * curSession->GetMyMemberIndex());
int64_t endTime = GetTimeMillis() + sleepTime;
int heightTmp{-1};
int heightStart{-1};
{
LOCK(cs);
heightTmp = heightStart = currentHeight;
}
while (GetTimeMillis() < endTime) {
if (stopRequested || ShutdownRequested()) {
throw AbortPhaseException();
}
auto p = GetPhaseAndQuorumHash();
if (p.first != curPhase || p.second != expectedQuorumHash) {
throw AbortPhaseException();
{
LOCK(cs);
if (currentHeight > heightTmp) {
// New block(s) just came in
int64_t expectedBlockTime = (currentHeight - heightStart) * Params().GetConsensus().nPowTargetSpacing * 1000;
if (expectedBlockTime > sleepTime) {
// Blocks came faster than we expected, jump into the phase func asap
break;
}
heightTmp = currentHeight;
}
if (phase != curPhase || quorumHash != expectedQuorumHash) {
// Smth went wrong and/or we missed quite a few blocks and it's just too late now
throw AbortPhaseException();
}
}
if (!runWhileWaiting()) {
MilliSleep(100);

View File

@ -107,6 +107,7 @@ private:
CDKGSessionManager& dkgManager;
QuorumPhase phase{QuorumPhase_Idle};
int currentHeight{-1};
int quorumHeight{-1};
uint256 quorumHash;
std::shared_ptr<CDKGSession> curSession;

View File

@ -1438,6 +1438,17 @@ bool CInstantSendManager::GetInstantSendLockByHash(const uint256& hash, llmq::CI
return true;
}
bool CInstantSendManager::GetInstantSendLockHashByTxid(const uint256& txid, uint256& ret)
{
if (!IsInstantSendEnabled()) {
return false;
}
LOCK(cs);
ret = db.GetInstantSendLockHashByTxid(txid);
return !ret.IsNull();
}
bool CInstantSendManager::IsLocked(const uint256& txHash)
{
if (!IsInstantSendEnabled()) {

View File

@ -164,6 +164,7 @@ public:
bool AlreadyHave(const CInv& inv);
bool GetInstantSendLockByHash(const uint256& hash, CInstantSendLock& ret);
bool GetInstantSendLockHashByTxid(const uint256& txid, uint256& ret);
size_t GetInstantSendLockCount();

View File

@ -110,6 +110,9 @@ void CMasternodeSync::ProcessTick(CConnman& connman)
static int nTick = 0;
nTick++;
const static int64_t nSyncStart = GetTimeMillis();
const static std::string strAllow = strprintf("allow-sync-%lld", nSyncStart);
// reset the sync process if the last call to this function was more than 60 minutes ago (client was in sleep mode)
static int64_t nTimeLastProcess = GetTime();
if(GetTime() - nTimeLastProcess > 60*60) {
@ -178,6 +181,12 @@ void CMasternodeSync::ProcessTick(CConnman& connman)
// NORMAL NETWORK MODE - TESTNET/MAINNET
{
if ((pnode->fWhitelisted || pnode->m_manual_connection) && !netfulfilledman.HasFulfilledRequest(pnode->addr, strAllow)) {
netfulfilledman.RemoveAllFulfilledRequests(pnode->addr);
netfulfilledman.AddFulfilledRequest(pnode->addr, strAllow);
LogPrintf("CMasternodeSync::ProcessTick -- skipping mnsync restrictions for peer=%d\n", pnode->GetId());
}
if(netfulfilledman.HasFulfilledRequest(pnode->addr, "full-sync")) {
// We already fully synced from this node recently,
// disconnect to free this connection slot for another peer.
@ -199,6 +208,12 @@ void CMasternodeSync::ProcessTick(CConnman& connman)
// INITIAL TIMEOUT
if(nCurrentAsset == MASTERNODE_SYNC_WAITING) {
if(!pnode->fInbound && gArgs.GetBoolArg("-syncmempool", DEFAULT_SYNC_MEMPOOL) && !netfulfilledman.HasFulfilledRequest(pnode->addr, "mempool-sync")) {
netfulfilledman.AddFulfilledRequest(pnode->addr, "mempool-sync");
connman.PushMessage(pnode, msgMaker.Make(NetMsgType::MEMPOOL));
LogPrintf("CMasternodeSync::ProcessTick -- nTick %d nCurrentAsset %d -- syncing mempool from peer=%d\n", nTick, nCurrentAsset, pnode->GetId());
}
if(GetTime() - nTimeLastBumped > MASTERNODE_SYNC_TIMEOUT_SECONDS) {
// At this point we know that:
// a) there are peers (because we are looping on at least one of them);

View File

@ -27,6 +27,17 @@ void CMasternodeUtils::ProcessMasternodeConnections(CConnman& connman)
privateSendClient.GetMixingMasternodesInfo(vecDmns);
#endif // ENABLE_WALLET
// Don't disconnect masternode connections when we have less then the desired amount of outbound nodes
int nonMasternodeCount = 0;
connman.ForEachNode(CConnman::AllNodes, [&](CNode* pnode) {
if (!pnode->fInbound && !pnode->fFeeler && !pnode->m_manual_connection && !pnode->fMasternode) {
nonMasternodeCount++;
}
});
if (nonMasternodeCount < connman.GetMaxOutboundNodeCount()) {
return;
}
connman.ForEachNode(CConnman::AllNodes, [&](CNode* pnode) {
if (pnode->fMasternode && !connman.IsMasternodeQuorumNode(pnode)) {
#ifdef ENABLE_WALLET

View File

@ -2924,6 +2924,11 @@ size_t CConnman::GetNodeCount(NumConnections flags)
return nNum;
}
size_t CConnman::GetMaxOutboundNodeCount()
{
return nMaxOutbound;
}
void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats)
{
vstats.clear();

View File

@ -407,6 +407,7 @@ public:
bool IsMasternodeQuorumNode(const CNode* pnode);
size_t GetNodeCount(NumConnections num);
size_t GetMaxOutboundNodeCount();
void GetNodeStats(std::vector<CNodeStats>& vstats);
bool DisconnectNode(const std::string& node);
bool DisconnectNode(NodeId id);

View File

@ -3799,6 +3799,19 @@ bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptM
connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
vInv.clear();
}
uint256 islockHash;
if (!llmq::quorumInstantSendManager->GetInstantSendLockHashByTxid(hash, islockHash)) continue;
CInv islockInv(MSG_ISLOCK, islockHash);
pto->filterInventoryKnown.insert(islockHash);
LogPrint(BCLog::NET, "SendMessages -- queued inv: %s index=%d peer=%d\n", inv.ToString(), vInv.size(), pto->GetId());
vInv.push_back(inv);
if (vInv.size() == MAX_INV_SZ) {
LogPrint(BCLog::NET, "SendMessages -- pushing inv's: count=%d peer=%d\n", vInv.size(), pto->GetId());
connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
vInv.clear();
}
}
pto->timeLastMempoolReq = GetTime();
}

View File

@ -38,6 +38,17 @@ void CNetFulfilledRequestManager::RemoveFulfilledRequest(const CService& addr, c
}
}
void CNetFulfilledRequestManager::RemoveAllFulfilledRequests(const CService& addr)
{
LOCK(cs_mapFulfilledRequests);
CService addrSquashed = Params().AllowMultiplePorts() ? addr : CService(addr, 0);
fulfilledreqmap_t::iterator it = mapFulfilledRequests.find(addrSquashed);
if (it != mapFulfilledRequests.end()) {
mapFulfilledRequests.erase(it++);
}
}
void CNetFulfilledRequestManager::CheckAndRemove()
{
LOCK(cs_mapFulfilledRequests);

View File

@ -40,6 +40,8 @@ public:
void AddFulfilledRequest(const CService& addr, const std::string& strRequest);
bool HasFulfilledRequest(const CService& addr, const std::string& strRequest);
void RemoveAllFulfilledRequests(const CService& addr);
void CheckAndRemove();
void Clear();

View File

@ -541,85 +541,97 @@ bool CPrivateSendClientSession::SignFinalTransaction(const CTransaction& finalTr
if (!mixingMasternode) return false;
finalMutableTransaction = finalTransactionNew;
LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::SignFinalTransaction -- finalMutableTransaction=%s", finalMutableTransaction.ToString());
LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- finalMutableTransaction=%s", __func__, finalMutableTransaction.ToString());
// STEP 1: check final transaction general rules
// Make sure it's BIP69 compliant
sort(finalMutableTransaction.vin.begin(), finalMutableTransaction.vin.end(), CompareInputBIP69());
sort(finalMutableTransaction.vout.begin(), finalMutableTransaction.vout.end(), CompareOutputBIP69());
if (finalMutableTransaction.GetHash() != finalTransactionNew.GetHash()) {
LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::SignFinalTransaction -- WARNING! Masternode %s is not BIP69 compliant!\n", mixingMasternode->proTxHash.ToString());
LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- ERROR! Masternode %s is not BIP69 compliant!\n", __func__, mixingMasternode->proTxHash.ToString());
UnlockCoins();
keyHolderStorage.ReturnAll();
SetNull();
return false;
}
// Make sure all inputs/outputs are valid
PoolMessage nMessageID{MSG_NOERR};
if (!IsValidInOuts(finalMutableTransaction.vin, finalMutableTransaction.vout, nMessageID, nullptr)) {
LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- ERROR! IsValidInOuts() failed: %s\n", __func__, CPrivateSend::GetMessageByID(nMessageID));
UnlockCoins();
keyHolderStorage.ReturnAll();
SetNull();
return false;
}
// STEP 2: make sure our own inputs/outputs are present, otherwise refuse to sign
std::vector<CTxIn> sigs;
//make sure my inputs/outputs are present, otherwise refuse to sign
for (const auto& entry : vecEntries) {
// Check that the final transaction has all our outputs
for (const auto& txout : entry.vecTxOut) {
bool fFound = false;
for (const auto& txoutFinal : finalMutableTransaction.vout) {
if (txoutFinal == txout) {
fFound = true;
break;
}
}
if (!fFound) {
// Something went wrong and we'll refuse to sign. It's possible we'll be charged collateral. But that's
// better than signing if the transaction doesn't look like what we wanted.
LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- an output is missing, refusing to sign! txout=%s\n", txout.ToString());
UnlockCoins();
keyHolderStorage.ReturnAll();
SetNull();
return false;
}
}
for (const auto& txdsin : entry.vecTxDSIn) {
/* Sign my transaction and all outputs */
int nMyInputIndex = -1;
CScript prevPubKey = CScript();
CTxIn txin = CTxIn();
for (unsigned int i = 0; i < finalMutableTransaction.vin.size(); i++) {
if (finalMutableTransaction.vin[i] == txdsin) {
nMyInputIndex = i;
prevPubKey = txdsin.prevPubKey;
txin = txdsin;
break;
}
}
if (nMyInputIndex >= 0) { //might have to do this one input at a time?
int nFoundOutputsCount = 0;
CAmount nValue1 = 0;
CAmount nValue2 = 0;
for (const auto& txoutFinal : finalMutableTransaction.vout) {
for (const auto& txout : entry.vecTxOut) {
if (txoutFinal == txout) {
nFoundOutputsCount++;
nValue1 += txoutFinal.nValue;
}
}
}
for (const auto& txout : entry.vecTxOut) {
nValue2 += txout.nValue;
}
int nTargetOuputsCount = entry.vecTxOut.size();
if (nFoundOutputsCount < nTargetOuputsCount || nValue1 != nValue2) {
// in this case, something went wrong and we'll refuse to sign. It's possible we'll be charged collateral. But that's
// better then signing if the transaction doesn't look like what we wanted.
LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::SignFinalTransaction -- My entries are not correct! Refusing to sign: nFoundOutputsCount: %d, nTargetOuputsCount: %d\n", nFoundOutputsCount, nTargetOuputsCount);
UnlockCoins();
keyHolderStorage.ReturnAll();
SetNull();
return false;
}
const CKeyStore& keystore = *vpwallets[0];
LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::SignFinalTransaction -- Signing my input %i\n", nMyInputIndex);
// TODO we're using amount=0 here but we should use the correct amount. This works because Dash ignores the amount while signing/verifying (only used in Bitcoin/Segwit)
if (!SignSignature(keystore, prevPubKey, finalMutableTransaction, nMyInputIndex, 0, int(SIGHASH_ALL | SIGHASH_ANYONECANPAY))) { // changes scriptSig
LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::SignFinalTransaction -- Unable to sign my own transaction!\n");
// not sure what to do here, it will timeout...?
}
sigs.push_back(finalMutableTransaction.vin[nMyInputIndex]);
LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::SignFinalTransaction -- nMyInputIndex: %d, sigs.size(): %d, scriptSig=%s\n", nMyInputIndex, (int)sigs.size(), ScriptToAsmStr(finalMutableTransaction.vin[nMyInputIndex].scriptSig));
if (nMyInputIndex == -1) {
// Can't find one of my own inputs, refuse to sign. It's possible we'll be charged collateral. But that's
// better than signing if the transaction doesn't look like what we wanted.
LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- missing input! txdsin=%s\n", __func__, txdsin.ToString());
UnlockCoins();
keyHolderStorage.ReturnAll();
SetNull();
return false;
}
const CKeyStore& keystore = *vpwallets[0];
LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- Signing my input %i\n", __func__, nMyInputIndex);
// TODO we're using amount=0 here but we should use the correct amount. This works because Dash ignores the amount while signing/verifying (only used in Bitcoin/Segwit)
if (!SignSignature(keystore, prevPubKey, finalMutableTransaction, nMyInputIndex, 0, int(SIGHASH_ALL | SIGHASH_ANYONECANPAY))) { // changes scriptSig
LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- Unable to sign my own transaction!\n", __func__);
// not sure what to do here, it will timeout...?
}
sigs.push_back(finalMutableTransaction.vin[nMyInputIndex]);
LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- nMyInputIndex: %d, sigs.size(): %d, scriptSig=%s\n",
__func__, nMyInputIndex, (int)sigs.size(), ScriptToAsmStr(finalMutableTransaction.vin[nMyInputIndex].scriptSig));
}
}
if (sigs.empty()) {
LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::SignFinalTransaction -- can't sign anything!\n");
LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- can't sign anything!\n", __func__);
UnlockCoins();
keyHolderStorage.ReturnAll();
SetNull();
@ -628,7 +640,7 @@ bool CPrivateSendClientSession::SignFinalTransaction(const CTransaction& finalTr
}
// push all of our signatures to the Masternode
LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::SignFinalTransaction -- pushing sigs to the masternode, finalMutableTransaction=%s", finalMutableTransaction.ToString());
LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- pushing sigs to the masternode, finalMutableTransaction=%s", __func__, finalMutableTransaction.ToString());
CNetMsgMaker msgMaker(pnode->GetSendVersion());
connman.PushMessage(pnode, msgMaker.Make(NetMsgType::DSSIGNFINALTX, sigs));
SetState(POOL_STATE_SIGNING);

View File

@ -178,100 +178,10 @@ void CPrivateSendServer::ProcessMessage(CNode* pfrom, const std::string& strComm
LogPrint(BCLog::PRIVATESEND, "DSVIN -- txCollateral %s", entry.txCollateral->ToString());
if (entry.vecTxDSIn.size() != entry.vecTxOut.size()) {
LogPrint(BCLog::PRIVATESEND, "DSVIN -- ERROR: inputs vs outputs size mismatch! %d vs %d\n", entry.vecTxDSIn.size(), entry.vecTxOut.size());
PushStatus(pfrom, STATUS_REJECTED, ERR_SIZE_MISMATCH, connman);
ConsumeCollateral(connman, entry.txCollateral);
return;
}
if (entry.vecTxDSIn.size() > PRIVATESEND_ENTRY_MAX_SIZE) {
LogPrint(BCLog::PRIVATESEND, "DSVIN -- ERROR: too many inputs! %d/%d\n", entry.vecTxDSIn.size(), PRIVATESEND_ENTRY_MAX_SIZE);
PushStatus(pfrom, STATUS_REJECTED, ERR_MAXIMUM, connman);
ConsumeCollateral(connman, entry.txCollateral);
return;
}
// do we have the same denominations as the current session?
if (!IsOutputsCompatibleWithSessionDenom(entry.vecTxOut)) {
LogPrint(BCLog::PRIVATESEND, "DSVIN -- not compatible with existing transactions!\n");
PushStatus(pfrom, STATUS_REJECTED, ERR_EXISTING_TX, connman);
return;
}
// check it like a transaction
{
CAmount nValueIn = 0;
CAmount nValueOut = 0;
CMutableTransaction tx;
for (const auto& txout : entry.vecTxOut) {
nValueOut += txout.nValue;
tx.vout.push_back(txout);
if (txout.scriptPubKey.size() != 25) {
LogPrint(BCLog::PRIVATESEND, "DSVIN -- non-standard pubkey detected! scriptPubKey=%s\n", ScriptToAsmStr(txout.scriptPubKey));
PushStatus(pfrom, STATUS_REJECTED, ERR_NON_STANDARD_PUBKEY, connman);
ConsumeCollateral(connman, entry.txCollateral);
return;
}
if (!txout.scriptPubKey.IsPayToPublicKeyHash()) {
LogPrint(BCLog::PRIVATESEND, "DSVIN -- invalid script! scriptPubKey=%s\n", ScriptToAsmStr(txout.scriptPubKey));
PushStatus(pfrom, STATUS_REJECTED, ERR_INVALID_SCRIPT, connman);
ConsumeCollateral(connman, entry.txCollateral);
return;
}
}
for (const auto& txin : entry.vecTxDSIn) {
tx.vin.push_back(txin);
LogPrint(BCLog::PRIVATESEND, "DSVIN -- txin=%s\n", txin.ToString());
Coin coin;
auto mempoolTx = mempool.get(txin.prevout.hash);
if (mempoolTx != nullptr) {
if (mempool.isSpent(txin.prevout) || !llmq::quorumInstantSendManager->IsLocked(txin.prevout.hash)) {
LogPrint(BCLog::PRIVATESEND, "DSVIN -- spent or non-locked mempool input! txin=%s\n", txin.ToString());
PushStatus(pfrom, STATUS_REJECTED, ERR_MISSING_TX, connman);
return;
}
if (!CPrivateSend::IsDenominatedAmount(mempoolTx->vout[txin.prevout.n].nValue)) {
LogPrintf("DSVIN -- non-denominated mempool input! txin=%s\n", txin.ToString());
PushStatus(pfrom, STATUS_REJECTED, ERR_DENOM, connman);
ConsumeCollateral(connman, entry.txCollateral);
return;
}
nValueIn += mempoolTx->vout[txin.prevout.n].nValue;
} else if (GetUTXOCoin(txin.prevout, coin)) {
if (!CPrivateSend::IsDenominatedAmount(coin.out.nValue)) {
LogPrintf("DSVIN -- non-denominated input! txin=%s\n", txin.ToString());
PushStatus(pfrom, STATUS_REJECTED, ERR_DENOM, connman);
ConsumeCollateral(connman, entry.txCollateral);
return;
}
nValueIn += coin.out.nValue;
} else {
LogPrint(BCLog::PRIVATESEND, "DSVIN -- missing input! txin=%s\n", txin.ToString());
PushStatus(pfrom, STATUS_REJECTED, ERR_MISSING_TX, connman);
return;
}
}
// There should be no fee in mixing tx
CAmount nFee = nValueIn - nValueOut;
if (nFee != 0) {
LogPrint(BCLog::PRIVATESEND, "DSVIN -- there should be no fee in mixing tx! fees: %lld, tx=%s", nFee, tx.ToString());
PushStatus(pfrom, STATUS_REJECTED, ERR_FEES, connman);
return;
}
}
PoolMessage nMessageID = MSG_NOERR;
entry.addr = pfrom->addr;
if (AddEntry(entry, nMessageID)) {
if (AddEntry(connman, entry, nMessageID)) {
PushStatus(pfrom, STATUS_ACCEPTED, nMessageID, connman);
CheckPool(connman);
RelayStatus(STATUS_ACCEPTED, connman);
@ -628,48 +538,62 @@ bool CPrivateSendServer::IsInputScriptSigValid(const CTxIn& txin)
}
//
// Add a clients transaction to the pool
// Add a client's transaction inputs/outputs to the pool
//
bool CPrivateSendServer::AddEntry(const CPrivateSendEntry& entryNew, PoolMessage& nMessageIDRet)
bool CPrivateSendServer::AddEntry(CConnman& connman, const CPrivateSendEntry& entry, PoolMessage& nMessageIDRet)
{
if (!fMasternodeMode) return false;
for (const auto& txin : entryNew.vecTxDSIn) {
if (txin.prevout.IsNull()) {
LogPrint(BCLog::PRIVATESEND, "CPrivateSendServer::AddEntry -- input not valid!\n");
nMessageIDRet = ERR_INVALID_INPUT;
return false;
}
}
if (!CPrivateSend::IsCollateralValid(*entryNew.txCollateral)) {
LogPrint(BCLog::PRIVATESEND, "CPrivateSendServer::AddEntry -- collateral not valid!\n");
nMessageIDRet = ERR_INVALID_COLLATERAL;
return false;
}
if (GetEntriesCount() >= nSessionMaxParticipants) {
LogPrint(BCLog::PRIVATESEND, "CPrivateSendServer::AddEntry -- entries is full!\n");
LogPrint(BCLog::PRIVATESEND, "CPrivateSendServer::%s -- ERROR: entries is full!\n", __func__);
nMessageIDRet = ERR_ENTRIES_FULL;
return false;
}
for (const auto& txin : entryNew.vecTxDSIn) {
LogPrint(BCLog::PRIVATESEND, "looking for txin -- %s\n", txin.ToString());
if (!CPrivateSend::IsCollateralValid(*entry.txCollateral)) {
LogPrint(BCLog::PRIVATESEND, "CPrivateSendServer::%s -- ERROR: collateral not valid!\n", __func__);
nMessageIDRet = ERR_INVALID_COLLATERAL;
return false;
}
if (entry.vecTxDSIn.size() > PRIVATESEND_ENTRY_MAX_SIZE) {
LogPrint(BCLog::PRIVATESEND, "CPrivateSendServer::%s -- ERROR: too many inputs! %d/%d\n", __func__, entry.vecTxDSIn.size(), PRIVATESEND_ENTRY_MAX_SIZE);
nMessageIDRet = ERR_MAXIMUM;
ConsumeCollateral(connman, entry.txCollateral);
return false;
}
std::vector<CTxIn> vin;
for (const auto& txin : entry.vecTxDSIn) {
LogPrint(BCLog::PRIVATESEND, "CPrivateSendServer::%s -- txin=%s\n", __func__, txin.ToString());
for (const auto& entry : vecEntries) {
for (const auto& txdsin : entry.vecTxDSIn) {
if (txdsin.prevout == txin.prevout) {
LogPrint(BCLog::PRIVATESEND, "CPrivateSendServer::AddEntry -- found in txin\n");
LogPrint(BCLog::PRIVATESEND, "CPrivateSendServer::%s -- ERROR: already have this txin in entries\n", __func__);
nMessageIDRet = ERR_ALREADY_HAVE;
// Two peers sent the same input? Can't really say who is the malicious one here,
// could be that someone is picking someone else's inputs randomly trying to force
// collateral consumption. Do not punish.
return false;
}
}
}
vin.emplace_back(txin);
}
vecEntries.push_back(entryNew);
bool fConsumeCollateral{false};
if (!IsValidInOuts(vin, entry.vecTxOut, nMessageIDRet, &fConsumeCollateral)) {
LogPrint(BCLog::PRIVATESEND, "CPrivateSendServer::%s -- ERROR! IsValidInOuts() failed: %s\n", __func__, CPrivateSend::GetMessageByID(nMessageIDRet));
if (fConsumeCollateral) {
ConsumeCollateral(connman, entry.txCollateral);
}
return false;
}
LogPrint(BCLog::PRIVATESEND, "CPrivateSendServer::AddEntry -- adding entry %d of %d required\n", GetEntriesCount(), nSessionMaxParticipants);
vecEntries.push_back(entry);
LogPrint(BCLog::PRIVATESEND, "CPrivateSendServer::%s -- adding entry %d of %d required\n", __func__, GetEntriesCount(), nSessionMaxParticipants);
nMessageIDRet = MSG_ENTRIES_ADDED;
return true;
@ -724,19 +648,6 @@ bool CPrivateSendServer::IsSignaturesComplete()
return true;
}
bool CPrivateSendServer::IsOutputsCompatibleWithSessionDenom(const std::vector<CTxOut>& vecTxOut)
{
if (CPrivateSend::GetDenominations(vecTxOut) == 0) return false;
for (const auto& entry : vecEntries) {
LogPrint(BCLog::PRIVATESEND, "CPrivateSendServer::IsOutputsCompatibleWithSessionDenom -- vecTxOut denom %d, entry.vecTxOut denom %d\n",
CPrivateSend::GetDenominations(vecTxOut), CPrivateSend::GetDenominations(entry.vecTxOut));
if (CPrivateSend::GetDenominations(vecTxOut) != CPrivateSend::GetDenominations(entry.vecTxOut)) return false;
}
return true;
}
bool CPrivateSendServer::IsAcceptableDSA(const CPrivateSendAccept& dsa, PoolMessage& nMessageIDRet)
{
if (!fMasternodeMode) return false;

View File

@ -29,7 +29,7 @@ private:
bool fUnitTest;
/// Add a clients entry to the pool
bool AddEntry(const CPrivateSendEntry& entryNew, PoolMessage& nMessageIDRet);
bool AddEntry(CConnman& connman, const CPrivateSendEntry& entry, PoolMessage& nMessageIDRet);
/// Add signature to a txin
bool AddScriptSig(const CTxIn& txin);
@ -57,8 +57,6 @@ private:
bool IsSignaturesComplete();
/// Check to make sure a given input matches an input in the pool and its scriptSig is valid
bool IsInputScriptSigValid(const CTxIn& txin);
/// Are these outputs compatible with other client in the pool?
bool IsOutputsCompatibleWithSessionDenom(const std::vector<CTxOut>& vecTxOut);
// Set the 'state' value, with some logging and capturing when the state changed
void SetState(PoolState nStateNew);

View File

@ -224,6 +224,93 @@ std::string CPrivateSendBaseSession::GetStateString() const
}
}
bool CPrivateSendBaseSession::IsValidInOuts(const std::vector<CTxIn>& vin, const std::vector<CTxOut>& vout, PoolMessage& nMessageIDRet, bool* fConsumeCollateralRet) const
{
std::set<CScript> setScripPubKeys;
nMessageIDRet = MSG_NOERR;
if (fConsumeCollateralRet) *fConsumeCollateralRet = false;
if (vin.size() != vout.size()) {
LogPrint(BCLog::PRIVATESEND, "CPrivateSendBaseSession::%s -- ERROR: inputs vs outputs size mismatch! %d vs %d\n", __func__, vin.size(), vout.size());
nMessageIDRet = ERR_SIZE_MISMATCH;
if (fConsumeCollateralRet) *fConsumeCollateralRet = true;
return false;
}
auto checkTxOut = [&](const CTxOut& txout) {
std::vector<CTxOut> vecTxOut{txout};
int nDenom = CPrivateSend::GetDenominations(vecTxOut);
if (nDenom != nSessionDenom) {
LogPrint(BCLog::PRIVATESEND, "CPrivateSendBaseSession::IsValidInOuts -- ERROR: incompatible denom %d (%s) != nSessionDenom %d (%s)\n",
nDenom, CPrivateSend::GetDenominationsToString(nDenom), nSessionDenom, CPrivateSend::GetDenominationsToString(nSessionDenom));
nMessageIDRet = ERR_DENOM;
if (fConsumeCollateralRet) *fConsumeCollateralRet = true;
return false;
}
if (!txout.scriptPubKey.IsPayToPublicKeyHash()) {
LogPrint(BCLog::PRIVATESEND, "CPrivateSendBaseSession::IsValidInOuts -- ERROR: invalid script! scriptPubKey=%s\n", ScriptToAsmStr(txout.scriptPubKey));
nMessageIDRet = ERR_INVALID_SCRIPT;
if (fConsumeCollateralRet) *fConsumeCollateralRet = true;
return false;
}
if (!setScripPubKeys.insert(txout.scriptPubKey).second) {
LogPrint(BCLog::PRIVATESEND, "CPrivateSendBaseSession::IsValidInOuts -- ERROR: already have this script! scriptPubKey=%s\n", ScriptToAsmStr(txout.scriptPubKey));
nMessageIDRet = ERR_ALREADY_HAVE;
if (fConsumeCollateralRet) *fConsumeCollateralRet = true;
return false;
}
// IsPayToPublicKeyHash() above already checks for scriptPubKey size,
// no need to double check, hence no usage of ERR_NON_STANDARD_PUBKEY
return true;
};
CAmount nFees{0};
for (const auto& txout : vout) {
if (!checkTxOut(txout)) {
return false;
}
nFees -= txout.nValue;
}
CCoinsViewMemPool viewMemPool(pcoinsTip, mempool);
for (const auto& txin : vin) {
LogPrint(BCLog::PRIVATESEND, "CPrivateSendBaseSession::%s -- txin=%s\n", __func__, txin.ToString());
if (txin.prevout.IsNull()) {
LogPrint(BCLog::PRIVATESEND, "CPrivateSendBaseSession::%s -- ERROR: invalid input!\n", __func__);
nMessageIDRet = ERR_INVALID_INPUT;
if (fConsumeCollateralRet) *fConsumeCollateralRet = true;
return false;
}
Coin coin;
if (!viewMemPool.GetCoin(txin.prevout, coin) || coin.IsSpent() ||
(coin.nHeight == MEMPOOL_HEIGHT && !llmq::quorumInstantSendManager->IsLocked(txin.prevout.hash))) {
LogPrint(BCLog::PRIVATESEND, "CPrivateSendBaseSession::%s -- ERROR: missing, spent or non-locked mempool input! txin=%s\n", __func__, txin.ToString());
nMessageIDRet = ERR_MISSING_TX;
return false;
}
if (!checkTxOut(coin.out)) {
return false;
}
nFees += coin.out.nValue;
}
// The same size and denom for inputs and outputs ensures their total value is also the same,
// no need to double check. If not, we are doing smth wrong, bail out.
if (nFees != 0) {
LogPrint(BCLog::PRIVATESEND, "CPrivateSendBaseSession::%s -- ERROR: non-zero fees! fees: %lld\n", __func__, nFees);
nMessageIDRet = ERR_FEES;
return false;
}
return true;
}
// Definitions for static data members
std::vector<CAmount> CPrivateSend::vecStandardDenominations;
std::map<uint256, CPrivateSendBroadcastTx> CPrivateSend::mapDSTX;
@ -457,8 +544,6 @@ std::string CPrivateSend::GetMessageByID(PoolMessage nMessageID)
return _("Not in the Masternode list.");
case ERR_MODE:
return _("Incompatible mode.");
case ERR_NON_STANDARD_PUBKEY:
return _("Non-standard public key detected.");
case ERR_QUEUE_FULL:
return _("Masternode queue is full.");
case ERR_RECENT:
@ -477,6 +562,8 @@ std::string CPrivateSend::GetMessageByID(PoolMessage nMessageID)
return _("Your entries added successfully.");
case ERR_SIZE_MISMATCH:
return _("Inputs vs outputs size mismatch.");
case ERR_NON_STANDARD_PUBKEY:
case ERR_NOT_A_MN:
default:
return _("Unknown response.");
}

View File

@ -42,7 +42,7 @@ enum PoolMessage : int32_t {
ERR_MAXIMUM,
ERR_MN_LIST,
ERR_MODE,
ERR_NON_STANDARD_PUBKEY,
ERR_NON_STANDARD_PUBKEY, // not used
ERR_NOT_A_MN, // not used
ERR_QUEUE_FULL,
ERR_RECENT,
@ -372,6 +372,8 @@ protected:
void SetNull();
bool IsValidInOuts(const std::vector<CTxIn>& vin, const std::vector<CTxOut>& vout, PoolMessage& nMessageIDRet, bool* fConsumeCollateralRet) const;
public:
int nSessionDenom; // Users must submit a denom matching this

View File

@ -263,7 +263,7 @@
<string notr="true"/>
</property>
<property name="text">
<string>Amount of Dash to keep mixed</string>
<string>Target PrivateSend balance</string>
</property>
</widget>
</item>

View File

@ -428,8 +428,8 @@ void OverviewPage::updatePrivateSendProgress()
QString strToolPip = ("<b>" + tr("Overall progress") + ": %1%</b><br/>" +
tr("Denominated") + ": %2%<br/>" +
tr("Mixed") + ": %3%<br/>" +
tr("Anonymized") + ": %4%<br/>" +
tr("Partially mixed") + ": %3%<br/>" +
tr("Mixed") + ": %4%<br/>" +
tr("Denominated inputs have %5 of %n rounds on average", "", privateSendClient.nPrivateSendRounds))
.arg(progress).arg(denomPart).arg(anonNormPart).arg(anonFullPart)
.arg(nAverageAnonymizedRounds);

View File

@ -378,9 +378,9 @@ void SendCoinsDialog::send(QList<SendCoinsRecipient> recipients)
if(ctrl.IsUsingPrivateSend()) {
questionString.append(tr("using") + " <b>" + tr("mixed funds") + "</b>");
questionString.append(tr("using") + " <b>" + tr("PrivateSend funds only") + "</b>");
} else {
questionString.append(tr("using") + " <b>" + tr("any available funds (not mixed)") + "</b>");
questionString.append(tr("using") + " <b>" + tr("any available funds") + "</b>");
}
if (displayedEntries < messageEntries) {

View File

@ -8,166 +8,30 @@
#include "init.h"
#include "netbase.h"
#include "validation.h"
#include "masternode/masternode-payments.h"
#include "masternode/masternode-sync.h"
#ifdef ENABLE_WALLET
#include "privatesend/privatesend-client.h"
#endif // ENABLE_WALLET
#include "privatesend/privatesend-server.h"
#include "rpc/server.h"
#include "util.h"
#include "utilmoneystr.h"
#include "txmempool.h"
#include "wallet/coincontrol.h"
#include "wallet/rpcwallet.h"
#include "evo/specialtx.h"
#include "evo/deterministicmns.h"
#include "masternode/masternode-payments.h"
#include "masternode/masternode-sync.h"
#include "rpc/server.h"
#include "wallet/coincontrol.h"
#include "wallet/rpcwallet.h"
#ifdef ENABLE_WALLET
#include "wallet/wallet.h"
#endif // ENABLE_WALLET
#include <fstream>
#include <iomanip>
#include <univalue.h>
UniValue masternodelist(const JSONRPCRequest& request);
#ifdef ENABLE_WALLET
UniValue privatesend(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(
"privatesend \"command\"\n"
"\nArguments:\n"
"1. \"command\" (string or set of strings, required) The command to execute\n"
"\nAvailable commands:\n"
" start - Start mixing\n"
" stop - Stop mixing\n"
" reset - Reset mixing\n"
);
if (fMasternodeMode)
throw JSONRPCError(RPC_INTERNAL_ERROR, "Client-side mixing is not supported on masternodes");
if (!privateSendClient.fEnablePrivateSend) {
if (fLiteMode) {
// mixing is disabled by default in lite mode
throw JSONRPCError(RPC_INTERNAL_ERROR, "Mixing is disabled in lite mode, use -enableprivatesend command line option to enable mixing again");
} else if (!gArgs.GetBoolArg("-enableprivatesend", true)) {
// otherwise it's on by default, unless cmd line option says otherwise
throw JSONRPCError(RPC_INTERNAL_ERROR, "Mixing is disabled via -enableprivatesend=0 command line option, remove it to enable mixing again");
} else {
// neither litemode nor enableprivatesend=false casee,
// most likely smth bad happened and we disabled it while running the wallet
throw JSONRPCError(RPC_INTERNAL_ERROR, "Mixing is disabled due to some internal error");
}
}
if (request.params[0].get_str() == "start") {
{
LOCK(pwallet->cs_wallet);
if (pwallet->IsLocked(true))
throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please unlock wallet for mixing with walletpassphrase first.");
}
privateSendClient.fPrivateSendRunning = true;
bool result = privateSendClient.DoAutomaticDenominating(*g_connman);
return "Mixing " + (result ? "started successfully" : ("start failed: " + privateSendClient.GetStatuses() + ", will retry"));
}
if (request.params[0].get_str() == "stop") {
privateSendClient.fPrivateSendRunning = false;
return "Mixing was stopped";
}
if (request.params[0].get_str() == "reset") {
privateSendClient.ResetPool();
return "Mixing was reset";
}
return "Unknown command, please see \"help privatesend\"";
}
#endif // ENABLE_WALLET
UniValue getpoolinfo(const JSONRPCRequest& request)
{
throw std::runtime_error(
"getpoolinfo\n"
"DEPRECATED. Please use getprivatesendinfo instead.\n"
);
}
UniValue getprivatesendinfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"getprivatesendinfo\n"
"Returns an object containing an information about PrivateSend settings and state.\n"
"\nResult (for regular nodes):\n"
"{\n"
" \"enabled\": true|false, (bool) Whether mixing functionality is enabled\n"
" \"running\": true|false, (bool) Whether mixing is currently running\n"
" \"multisession\": true|false, (bool) Whether PrivateSend Multisession option is enabled\n"
" \"max_sessions\": xxx, (numeric) How many parallel mixing sessions can there be at once\n"
" \"max_rounds\": xxx, (numeric) How many rounds to mix\n"
" \"max_amount\": xxx, (numeric) How many " + CURRENCY_UNIT + " to keep mixed\n"
" \"max_denoms\": xxx, (numeric) How many inputs of each denominated amount to create\n"
" \"queue_size\": xxx, (numeric) How many queues there are currently on the network\n"
" \"sessions\": (array of json objects)\n"
" [\n"
" {\n"
" \"protxhash\": \"...\", (string) The ProTxHash of the masternode\n"
" \"outpoint\": \"txid-index\", (string) The outpoint of the masternode\n"
" \"service\": \"host:port\", (string) The IP address and port of the masternode\n"
" \"denomination\": xxx, (numeric) The denomination of the mixing session in " + CURRENCY_UNIT + "\n"
" \"state\": \"...\", (string) Current state of the mixing session\n"
" \"entries_count\": xxx, (numeric) The number of entries in the mixing session\n"
" }\n"
" ,...\n"
" ],\n"
" \"keys_left\": xxx, (numeric) How many new keys are left since last automatic backup\n"
" \"warnings\": \"...\" (string) Warnings if any\n"
"}\n"
"\nResult (for masternodes):\n"
"{\n"
" \"queue_size\": xxx, (numeric) How many queues there are currently on the network\n"
" \"denomination\": xxx, (numeric) The denomination of the mixing session in " + CURRENCY_UNIT + "\n"
" \"state\": \"...\", (string) Current state of the mixing session\n"
" \"entries_count\": xxx, (numeric) The number of entries in the mixing session\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getprivatesendinfo", "")
+ HelpExampleRpc("getprivatesendinfo", "")
);
}
UniValue obj(UniValue::VOBJ);
if (fMasternodeMode) {
privateSendServer.GetJsonInfo(obj);
return obj;
}
#ifdef ENABLE_WALLET
privateSendClient.GetJsonInfo(obj);
CWallet* const pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) {
return obj;
}
obj.push_back(Pair("keys_left", pwallet->nKeysLeftSinceAutoBackup));
obj.push_back(Pair("warnings", pwallet->nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_WARNING
? "WARNING: keypool is almost depleted!" : ""));
#endif // ENABLE_WALLET
return obj;
}
void masternode_list_help()
{
throw std::runtime_error(
@ -668,11 +532,6 @@ static const CRPCCommand commands[] =
// --------------------- ------------------------ ----------------------- ------ ----------
{ "dash", "masternode", &masternode, true, {} },
{ "dash", "masternodelist", &masternodelist, true, {} },
{ "dash", "getpoolinfo", &getpoolinfo, true, {} },
{ "dash", "getprivatesendinfo", &getprivatesendinfo, true, {} },
#ifdef ENABLE_WALLET
{ "dash", "privatesend", &privatesend, false, {} },
#endif // ENABLE_WALLET
};
void RegisterMasternodeRPCCommands(CRPCTable &t)

View File

@ -64,7 +64,7 @@ UniValue getinfo(const JSONRPCRequest& request)
" \"protocolversion\": xxxxx, (numeric) the protocol version\n"
" \"walletversion\": xxxxx, (numeric) the wallet version\n"
" \"balance\": xxxxxxx, (numeric) the total dash balance of the wallet\n"
" \"privatesend_balance\": xxxxxx, (numeric) the mixed dash balance of the wallet\n"
" \"privatesend_balance\": xxxxxx, (numeric) the PrivateSend balance in " + CURRENCY_UNIT + "\n"
" \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n"
" \"timeoffset\": xxxxx, (numeric) the time offset\n"
" \"connections\": xxxxx, (numeric) the number of connections\n"

165
src/rpc/privatesend.cpp Normal file
View File

@ -0,0 +1,165 @@
// Copyright (c) 2019 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "validation.h"
#ifdef ENABLE_WALLET
#include "privatesend/privatesend-client.h"
#endif // ENABLE_WALLET
#include "privatesend/privatesend-server.h"
#include "rpc/server.h"
#include <univalue.h>
#ifdef ENABLE_WALLET
UniValue privatesend(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(
"privatesend \"command\"\n"
"\nArguments:\n"
"1. \"command\" (string or set of strings, required) The command to execute\n"
"\nAvailable commands:\n"
" start - Start mixing\n"
" stop - Stop mixing\n"
" reset - Reset mixing\n"
);
if (fMasternodeMode)
throw JSONRPCError(RPC_INTERNAL_ERROR, "Client-side mixing is not supported on masternodes");
if (!privateSendClient.fEnablePrivateSend) {
if (fLiteMode) {
// mixing is disabled by default in lite mode
throw JSONRPCError(RPC_INTERNAL_ERROR, "Mixing is disabled in lite mode, use -enableprivatesend command line option to enable mixing again");
} else if (!gArgs.GetBoolArg("-enableprivatesend", true)) {
// otherwise it's on by default, unless cmd line option says otherwise
throw JSONRPCError(RPC_INTERNAL_ERROR, "Mixing is disabled via -enableprivatesend=0 command line option, remove it to enable mixing again");
} else {
// neither litemode nor enableprivatesend=false casee,
// most likely smth bad happened and we disabled it while running the wallet
throw JSONRPCError(RPC_INTERNAL_ERROR, "Mixing is disabled due to some internal error");
}
}
if (request.params[0].get_str() == "start") {
{
LOCK(pwallet->cs_wallet);
if (pwallet->IsLocked(true))
throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please unlock wallet for mixing with walletpassphrase first.");
}
privateSendClient.fPrivateSendRunning = true;
bool result = privateSendClient.DoAutomaticDenominating(*g_connman);
return "Mixing " + (result ? "started successfully" : ("start failed: " + privateSendClient.GetStatuses() + ", will retry"));
}
if (request.params[0].get_str() == "stop") {
privateSendClient.fPrivateSendRunning = false;
return "Mixing was stopped";
}
if (request.params[0].get_str() == "reset") {
privateSendClient.ResetPool();
return "Mixing was reset";
}
return "Unknown command, please see \"help privatesend\"";
}
#endif // ENABLE_WALLET
UniValue getpoolinfo(const JSONRPCRequest& request)
{
throw std::runtime_error(
"getpoolinfo\n"
"DEPRECATED. Please use getprivatesendinfo instead.\n"
);
}
UniValue getprivatesendinfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"getprivatesendinfo\n"
"Returns an object containing an information about PrivateSend settings and state.\n"
"\nResult (for regular nodes):\n"
"{\n"
" \"enabled\": true|false, (bool) Whether mixing functionality is enabled\n"
" \"running\": true|false, (bool) Whether mixing is currently running\n"
" \"multisession\": true|false, (bool) Whether PrivateSend Multisession option is enabled\n"
" \"max_sessions\": xxx, (numeric) How many parallel mixing sessions can there be at once\n"
" \"max_rounds\": xxx, (numeric) How many rounds to mix\n"
" \"max_amount\": xxx, (numeric) Target PrivateSend balance in " + CURRENCY_UNIT + "\n"
" \"max_denoms\": xxx, (numeric) How many inputs of each denominated amount to create\n"
" \"queue_size\": xxx, (numeric) How many queues there are currently on the network\n"
" \"sessions\": (array of json objects)\n"
" [\n"
" {\n"
" \"protxhash\": \"...\", (string) The ProTxHash of the masternode\n"
" \"outpoint\": \"txid-index\", (string) The outpoint of the masternode\n"
" \"service\": \"host:port\", (string) The IP address and port of the masternode\n"
" \"denomination\": xxx, (numeric) The denomination of the mixing session in " + CURRENCY_UNIT + "\n"
" \"state\": \"...\", (string) Current state of the mixing session\n"
" \"entries_count\": xxx, (numeric) The number of entries in the mixing session\n"
" }\n"
" ,...\n"
" ],\n"
" \"keys_left\": xxx, (numeric) How many new keys are left since last automatic backup\n"
" \"warnings\": \"...\" (string) Warnings if any\n"
"}\n"
"\nResult (for masternodes):\n"
"{\n"
" \"queue_size\": xxx, (numeric) How many queues there are currently on the network\n"
" \"denomination\": xxx, (numeric) The denomination of the mixing session in " + CURRENCY_UNIT + "\n"
" \"state\": \"...\", (string) Current state of the mixing session\n"
" \"entries_count\": xxx, (numeric) The number of entries in the mixing session\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getprivatesendinfo", "")
+ HelpExampleRpc("getprivatesendinfo", "")
);
}
UniValue obj(UniValue::VOBJ);
if (fMasternodeMode) {
privateSendServer.GetJsonInfo(obj);
return obj;
}
#ifdef ENABLE_WALLET
privateSendClient.GetJsonInfo(obj);
CWallet* const pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) {
return obj;
}
obj.push_back(Pair("keys_left", pwallet->nKeysLeftSinceAutoBackup));
obj.push_back(Pair("warnings", pwallet->nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_WARNING
? "WARNING: keypool is almost depleted!" : ""));
#endif // ENABLE_WALLET
return obj;
}
static const CRPCCommand commands[] =
{ // category name actor (function) okSafe argNames
// --------------------- ------------------------ ----------------------- ------ ----------
{ "dash", "getpoolinfo", &getpoolinfo, true, {} },
{ "dash", "getprivatesendinfo", &getprivatesendinfo, true, {} },
#ifdef ENABLE_WALLET
{ "dash", "privatesend", &privatesend, false, {} },
#endif // ENABLE_WALLET
};
void RegisterPrivateSendRPCCommands(CRPCTable &t)
{
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
t.appendCommand(commands[vcidx].name, &commands[vcidx]);
}

View File

@ -21,6 +21,8 @@ void RegisterMiningRPCCommands(CRPCTable &tableRPC);
void RegisterRawTransactionRPCCommands(CRPCTable &tableRPC);
/** Register masternode RPC commands */
void RegisterMasternodeRPCCommands(CRPCTable &tableRPC);
/** Register PrivateSend RPC commands */
void RegisterPrivateSendRPCCommands(CRPCTable &tableRPC);
/** Register governance RPC commands */
void RegisterGovernanceRPCCommands(CRPCTable &tableRPC);
/** Register Evo RPC commands */
@ -36,6 +38,7 @@ static inline void RegisterAllCoreRPCCommands(CRPCTable &t)
RegisterMiningRPCCommands(t);
RegisterRawTransactionRPCCommands(t);
RegisterMasternodeRPCCommands(t);
RegisterPrivateSendRPCCommands(t);
RegisterGovernanceRPCCommands(t);
RegisterEvoRPCCommands(t);
RegisterQuorumsRPCCommands(t);

View File

@ -974,7 +974,7 @@ UniValue protx_list(const JSONRPCRequest& request)
#ifdef ENABLE_WALLET
LOCK2(cs_main, pwallet->cs_wallet);
if (request.params.size() > 3) {
if (request.params.size() > 4) {
protx_list_help();
}

View File

@ -137,6 +137,8 @@ static const bool DEFAULT_SPENTINDEX = false;
static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100;
/** Default for -persistmempool */
static const bool DEFAULT_PERSIST_MEMPOOL = true;
/** Default for -syncmempool */
static const bool DEFAULT_SYNC_MEMPOOL = true;
/** Maximum number of headers to announce when relaying blocks with headers message.*/
static const unsigned int MAX_BLOCKS_TO_ANNOUNCE = 8;

View File

@ -435,7 +435,7 @@ UniValue sendtoaddress(const JSONRPCRequest& request)
"5. subtractfeefromamount (boolean, optional, default=false) The fee will be deducted from the amount being sent.\n"
" The recipient will receive less amount of Dash than you enter in the amount field.\n"
"6. \"use_is\" (bool, optional, default=false) Deprecated and ignored\n"
"7. \"use_ps\" (bool, optional, default=false) Use mixed funds only\n"
"7. \"use_ps\" (bool, optional, default=false) Use PrivateSend funds only\n"
"8. conf_target (numeric, optional) Confirmation target (in blocks)\n"
"9. \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n"
" \"UNSET\"\n"
@ -1004,7 +1004,7 @@ UniValue sendmany(const JSONRPCRequest& request)
" ,...\n"
" ]\n"
"7. \"use_is\" (bool, optional, default=false) Deprecated and ignored\n"
"8. \"use_ps\" (bool, optional, default=false) Use mixed funds only\n"
"8. \"use_ps\" (bool, optional, default=false) Use PrivateSend funds only\n"
"9. conf_target (numeric, optional) Confirmation target (in blocks)\n"
"10. \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n"
" \"UNSET\"\n"
@ -2534,7 +2534,7 @@ UniValue getwalletinfo(const JSONRPCRequest& request)
" \"walletname\": xxxxx, (string) the wallet name\n"
" \"walletversion\": xxxxx, (numeric) the wallet version\n"
" \"balance\": xxxxxxx, (numeric) the total confirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
" \"privatesend_balance\": xxxxxx, (numeric) the mixed dash balance of the wallet in " + CURRENCY_UNIT + "\n"
" \"privatesend_balance\": xxxxxx, (numeric) the PrivateSend balance in " + CURRENCY_UNIT + "\n"
" \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
" \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet in " + CURRENCY_UNIT + "\n"
" \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n"

View File

@ -109,6 +109,11 @@ class VersionBitsWarningTest(BitcoinTestFramework):
pass
self.start_nodes()
# TODO this is a workaround. We have to wait for IBD to finish before we generate a block, as otherwise there
# won't be any warning generated. This workaround must be removed when we backport https://github.com/bitcoin/bitcoin/pull/12264
self.nodes[0].generate(1)
time.sleep(5)
# Connecting one block should be enough to generate an error.
self.nodes[0].generate(1)
assert(WARN_UNKNOWN_RULES_ACTIVE in self.nodes[0].getinfo()["errors"])

View File

@ -39,7 +39,7 @@ class WalletEncryptionTest(BitcoinTestFramework):
assert_equal(privkey, self.nodes[0].dumpprivkey(address))
# Check that the timeout is right
time.sleep(2)
time.sleep(4) # Wait a little bit longer to make sure wallet gets locked
assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address)
# Test wrong passphrase