mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 03:52:49 +01:00
Merge pull request #3282 from UdjinM6/bprc2
Backport "candidates" from develop to v0.15.x
This commit is contained in:
commit
9d48227b04
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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 \
|
||||
|
@ -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;
|
||||
|
86
src/init.cpp
86
src/init.cpp
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -107,6 +107,7 @@ private:
|
||||
CDKGSessionManager& dkgManager;
|
||||
|
||||
QuorumPhase phase{QuorumPhase_Idle};
|
||||
int currentHeight{-1};
|
||||
int quorumHeight{-1};
|
||||
uint256 quorumHash;
|
||||
std::shared_ptr<CDKGSession> curSession;
|
||||
|
@ -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()) {
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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.");
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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
165
src/rpc/privatesend.cpp
Normal 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]);
|
||||
}
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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"])
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user