dash/src/coinjoin/coinjoin.cpp

512 lines
16 KiB
C++
Raw Normal View History

// Copyright (c) 2014-2021 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.
2018-11-05 10:29:33 +01:00
#include <coinjoin/coinjoin.h>
#include <bls/bls.h>
#include <chain.h>
#include <chainparams.h>
#include <consensus/validation.h>
#include <llmq/chainlocks.h>
#include <llmq/instantsend.h>
#include <masternode/node.h>
#include <masternode/sync.h>
Backport 11651 (#3358) * scripted-diff: Replace #include "" with #include <> (ryanofsky) -BEGIN VERIFY SCRIPT- for f in \ src/*.cpp \ src/*.h \ src/bench/*.cpp \ src/bench/*.h \ src/compat/*.cpp \ src/compat/*.h \ src/consensus/*.cpp \ src/consensus/*.h \ src/crypto/*.cpp \ src/crypto/*.h \ src/crypto/ctaes/*.h \ src/policy/*.cpp \ src/policy/*.h \ src/primitives/*.cpp \ src/primitives/*.h \ src/qt/*.cpp \ src/qt/*.h \ src/qt/test/*.cpp \ src/qt/test/*.h \ src/rpc/*.cpp \ src/rpc/*.h \ src/script/*.cpp \ src/script/*.h \ src/support/*.cpp \ src/support/*.h \ src/support/allocators/*.h \ src/test/*.cpp \ src/test/*.h \ src/wallet/*.cpp \ src/wallet/*.h \ src/wallet/test/*.cpp \ src/wallet/test/*.h \ src/zmq/*.cpp \ src/zmq/*.h do base=${f%/*}/ relbase=${base#src/} sed -i "s:#include \"\(.*\)\"\(.*\):if test -e \$base'\\1'; then echo \"#include <\"\$relbase\"\\1>\\2\"; else echo \"#include <\\1>\\2\"; fi:e" $f done -END VERIFY SCRIPT- Signed-off-by: Pasta <pasta@dashboost.org> * scripted-diff: Replace #include "" with #include <> (Dash Specific) -BEGIN VERIFY SCRIPT- for f in \ src/bls/*.cpp \ src/bls/*.h \ src/evo/*.cpp \ src/evo/*.h \ src/governance/*.cpp \ src/governance/*.h \ src/llmq/*.cpp \ src/llmq/*.h \ src/masternode/*.cpp \ src/masternode/*.h \ src/privatesend/*.cpp \ src/privatesend/*.h do base=${f%/*}/ relbase=${base#src/} sed -i "s:#include \"\(.*\)\"\(.*\):if test -e \$base'\\1'; then echo \"#include <\"\$relbase\"\\1>\\2\"; else echo \"#include <\\1>\\2\"; fi:e" $f done -END VERIFY SCRIPT- Signed-off-by: Pasta <pasta@dashboost.org> * build: Remove -I for everything but project root Remove -I from build system for everything but the project root, and built-in dependencies. Signed-off-by: Pasta <pasta@dashboost.org> # Conflicts: # src/Makefile.test.include * qt: refactor: Use absolute include paths in .ui files * qt: refactor: Changes to make include paths absolute This makes all include paths in the GUI absolute. Many changes are involved as every single source file in src/qt/ assumes to be able to use relative includes. Signed-off-by: Pasta <pasta@dashboost.org> # Conflicts: # src/qt/dash.cpp # src/qt/optionsmodel.cpp # src/qt/test/rpcnestedtests.cpp * test: refactor: Use absolute include paths for test data files * Recommend #include<> syntax in developer notes * refactor: Include obj/build.h instead of build.h * END BACKPORT #11651 Remove trailing whitespace causing travis failure * fix backport 11651 Signed-off-by: Pasta <pasta@dashboost.org> * More of 11651 * fix blockchain.cpp Signed-off-by: pasta <pasta@dashboost.org> * Add missing "qt/" in includes * Add missing "test/" in includes * Fix trailing whitespaces Co-authored-by: Wladimir J. van der Laan <laanwj@gmail.com> Co-authored-by: Russell Yanofsky <russ@yanofsky.org> Co-authored-by: MeshCollider <dobsonsa68@gmail.com> Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
2020-03-19 23:46:56 +01:00
#include <messagesigner.h>
#include <netmessagemaker.h>
#include <txmempool.h>
merge bitcoin#14555: Move util files to directory (script modified to account for Dash backports, doesn't account for rebasing) ------------- BEGIN SCRIPT --------------- mkdir -p src/util git mv src/util.h src/util/system.h git mv src/util.cpp src/util/system.cpp git mv src/utilmemory.h src/util/memory.h git mv src/utilmoneystr.h src/util/moneystr.h git mv src/utilmoneystr.cpp src/util/moneystr.cpp git mv src/utilstrencodings.h src/util/strencodings.h git mv src/utilstrencodings.cpp src/util/strencodings.cpp git mv src/utiltime.h src/util/time.h git mv src/utiltime.cpp src/util/time.cpp git mv src/utilasmap.h src/util/asmap.h git mv src/utilasmap.cpp src/util/asmap.cpp git mv src/utilstring.h src/util/string.h git mv src/utilstring.cpp src/util/string.cpp gsed -i 's/<util\.h>/<util\/system\.h>/g' $(git ls-files 'src/*.h' 'src/*.cpp') gsed -i 's/<utilmemory\.h>/<util\/memory\.h>/g' $(git ls-files 'src/*.h' 'src/*.cpp') gsed -i 's/<utilmoneystr\.h>/<util\/moneystr\.h>/g' $(git ls-files 'src/*.h' 'src/*.cpp') gsed -i 's/<utilstrencodings\.h>/<util\/strencodings\.h>/g' $(git ls-files 'src/*.h' 'src/*.cpp') gsed -i 's/<utiltime\.h>/<util\/time\.h>/g' $(git ls-files 'src/*.h' 'src/*.cpp') gsed -i 's/<utilasmap\.h>/<util\/asmap\.h>/g' $(git ls-files 'src/*.h' 'src/*.cpp') gsed -i 's/<utilstring\.h>/<util\/string\.h>/g' $(git ls-files 'src/*.h' 'src/*.cpp') gsed -i 's/BITCOIN_UTIL_H/BITCOIN_UTIL_SYSTEM_H/g' src/util/system.h gsed -i 's/BITCOIN_UTILMEMORY_H/BITCOIN_UTIL_MEMORY_H/g' src/util/memory.h gsed -i 's/BITCOIN_UTILMONEYSTR_H/BITCOIN_UTIL_MONEYSTR_H/g' src/util/moneystr.h gsed -i 's/BITCOIN_UTILSTRENCODINGS_H/BITCOIN_UTIL_STRENCODINGS_H/g' src/util/strencodings.h gsed -i 's/BITCOIN_UTILTIME_H/BITCOIN_UTIL_TIME_H/g' src/util/time.h gsed -i 's/BITCOIN_UTILASMAP_H/BITCOIN_UTIL_ASMAP_H/g' src/util/asmap.h gsed -i 's/BITCOIN_UTILSTRING_H/BITCOIN_UTIL_STRING_H/g' src/util/string.h gsed -i 's/ util\.\(h\|cpp\)/ util\/system\.\1/g' src/Makefile.am gsed -i 's/utilmemory\.\(h\|cpp\)/util\/memory\.\1/g' src/Makefile.am gsed -i 's/utilmoneystr\.\(h\|cpp\)/util\/moneystr\.\1/g' src/Makefile.am gsed -i 's/utilstrencodings\.\(h\|cpp\)/util\/strencodings\.\1/g' src/Makefile.am gsed -i 's/utiltime\.\(h\|cpp\)/util\/time\.\1/g' src/Makefile.am gsed -i 's/utilasmap\.\(h\|cpp\)/util\/asmap\.\1/g' src/Makefile.am gsed -i 's/utilstring\.\(h\|cpp\)/util\/string\.\1/g' src/Makefile.am gsed -i 's/-> util ->/-> util\/system ->/' test/lint/lint-circular-dependencies.sh gsed -i 's/src\/util\.cpp/src\/util\/system\.cpp/g' test/lint/lint-format-strings.py test/lint/lint-locale-dependence.sh gsed -i 's/src\/utilmoneystr\.cpp/src\/util\/moneystr\.cpp/g' test/lint/lint-locale-dependence.sh gsed -i 's/src\/utilstrencodings\.\(h\|cpp\)/src\/util\/strencodings\.\1/g' test/lint/lint-locale-dependence.sh ------------- END SCRIPT ---------------
2021-06-27 08:33:13 +02:00
#include <util/moneystr.h>
#include <util/system.h>
#include <util/translation.h>
Backport 11651 (#3358) * scripted-diff: Replace #include "" with #include <> (ryanofsky) -BEGIN VERIFY SCRIPT- for f in \ src/*.cpp \ src/*.h \ src/bench/*.cpp \ src/bench/*.h \ src/compat/*.cpp \ src/compat/*.h \ src/consensus/*.cpp \ src/consensus/*.h \ src/crypto/*.cpp \ src/crypto/*.h \ src/crypto/ctaes/*.h \ src/policy/*.cpp \ src/policy/*.h \ src/primitives/*.cpp \ src/primitives/*.h \ src/qt/*.cpp \ src/qt/*.h \ src/qt/test/*.cpp \ src/qt/test/*.h \ src/rpc/*.cpp \ src/rpc/*.h \ src/script/*.cpp \ src/script/*.h \ src/support/*.cpp \ src/support/*.h \ src/support/allocators/*.h \ src/test/*.cpp \ src/test/*.h \ src/wallet/*.cpp \ src/wallet/*.h \ src/wallet/test/*.cpp \ src/wallet/test/*.h \ src/zmq/*.cpp \ src/zmq/*.h do base=${f%/*}/ relbase=${base#src/} sed -i "s:#include \"\(.*\)\"\(.*\):if test -e \$base'\\1'; then echo \"#include <\"\$relbase\"\\1>\\2\"; else echo \"#include <\\1>\\2\"; fi:e" $f done -END VERIFY SCRIPT- Signed-off-by: Pasta <pasta@dashboost.org> * scripted-diff: Replace #include "" with #include <> (Dash Specific) -BEGIN VERIFY SCRIPT- for f in \ src/bls/*.cpp \ src/bls/*.h \ src/evo/*.cpp \ src/evo/*.h \ src/governance/*.cpp \ src/governance/*.h \ src/llmq/*.cpp \ src/llmq/*.h \ src/masternode/*.cpp \ src/masternode/*.h \ src/privatesend/*.cpp \ src/privatesend/*.h do base=${f%/*}/ relbase=${base#src/} sed -i "s:#include \"\(.*\)\"\(.*\):if test -e \$base'\\1'; then echo \"#include <\"\$relbase\"\\1>\\2\"; else echo \"#include <\\1>\\2\"; fi:e" $f done -END VERIFY SCRIPT- Signed-off-by: Pasta <pasta@dashboost.org> * build: Remove -I for everything but project root Remove -I from build system for everything but the project root, and built-in dependencies. Signed-off-by: Pasta <pasta@dashboost.org> # Conflicts: # src/Makefile.test.include * qt: refactor: Use absolute include paths in .ui files * qt: refactor: Changes to make include paths absolute This makes all include paths in the GUI absolute. Many changes are involved as every single source file in src/qt/ assumes to be able to use relative includes. Signed-off-by: Pasta <pasta@dashboost.org> # Conflicts: # src/qt/dash.cpp # src/qt/optionsmodel.cpp # src/qt/test/rpcnestedtests.cpp * test: refactor: Use absolute include paths for test data files * Recommend #include<> syntax in developer notes * refactor: Include obj/build.h instead of build.h * END BACKPORT #11651 Remove trailing whitespace causing travis failure * fix backport 11651 Signed-off-by: Pasta <pasta@dashboost.org> * More of 11651 * fix blockchain.cpp Signed-off-by: pasta <pasta@dashboost.org> * Add missing "qt/" in includes * Add missing "test/" in includes * Fix trailing whitespaces Co-authored-by: Wladimir J. van der Laan <laanwj@gmail.com> Co-authored-by: Russell Yanofsky <russ@yanofsky.org> Co-authored-by: MeshCollider <dobsonsa68@gmail.com> Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
2020-03-19 23:46:56 +01:00
#include <validation.h>
#include <string>
constexpr static CAmount DEFAULT_MAX_RAW_TX_FEE{COIN / 10};
bool CCoinJoinEntry::AddScriptSig(const CTxIn& txin)
{
for (auto& txdsin : vecTxDSIn) {
2018-11-05 10:29:07 +01:00
if (txdsin.prevout == txin.prevout && txdsin.nSequence == txin.nSequence) {
if (txdsin.fHasSig) return false;
txdsin.scriptSig = txin.scriptSig;
txdsin.fHasSig = true;
return true;
}
}
return false;
}
uint256 CCoinJoinQueue::GetSignatureHash() const
{
Remove all legacy/compatibility MN code (#2600) * Remove CActiveLegacyMasternodeManager * Remove sentinelping RPC * Remove unused P2P messages and inv types There are still places where these are used in the code. The next commits will clean these up. * Remove MNB/MNP/MNVERIFY related code from masternode(man).h/cpp * Remove all legacy code regarding block MN payee voting * Remove MASTERNODE_SYNC_LIST and MASTERNODE_SYNC_MNW states Also replace all uses of IsMasternodeListSynced and IsWinnersListSynced with IsBlockchainSynced. * Remove unsupported masternode RPCs * Remove UpdateLastPaid methods * Remove duplicate deterministicmns.h include * Remove masternode.conf support * Remove legacy MN lists support from masternode list GUI * Remove unnecessary AskForMN call * Remove compatibility code in CPrivateSendQueue::GetSignatureHash * Don't add locally calculated MN payee in case GetBlockTxOuts failed This is not valid in DIP3 mode * Remove check for IsDeterministicMNsSporkActive in "masternode status" * Move CMasternode::IsValidNetAddr to CActiveDeterministicMasternodeManager * Remove use of CMasternode::CheckCollateral in governance code * Remove uses of MASTERNODE_SENTINEL_PING_MAX_SECONDS/MASTERNODE_SENTINEL_PING_MAX_SECONDS * Remove support for "-masternodeprivkey" * Remove pre-DIP3 vote cleanup * Remove compatibility code for quorumModifierHash/masternodeProTxHash * Remove check for invalid nBlockHeight in CMasternodePayments::GetBlockTxOuts ...and let it crash instead. We expect this method to be called with the correct height now (after DIP3 was fully deployed). * Remove ECDSA based Sign/CheckSignature from CGovernanceObject Only masternodes sign governance objects, so there is no need for ECDSA support here anymore. * Always add superblock and MN reward payments into new block * Always check block payees (except if fLiteMode==true) * Always allow superblock and MN payees in same block * Remove/Fix a few references to masternode.conf and related stuff Also delete guide-startmany.md and masternode_conf.md * Implement NotifyMasternodeListChanged signal and call governance maintenance * Remove non-DIP3 code path from CMasternodeMan::Find * Remove remaining unused code from CMasternode/CMasternodeMan * Always load governance.dat on startup * Mine an empty block instead of incrementing nHeight from chain tip in miner tests This test is crashing otherwise in GetBlockTxOuts as it tries to access a previous block that is not existing. * Skip MN payments verification on historical blocks (pre-DIP3 blocks) Even though DIP3 was active on BIP9 level, the spork was not active yet at that point meaning that payments were not enforced at that time. * Remove unused state and CollateralStatus enums * Unconditionally return false from IsBlockPayeeValid when IsTransactionValid returns false IsTransactionValid already handles the case where IsDIP3Active() returns false, making it return true. * Add override keyword to CDSNotificationInterface::NotifyMasternodeListChanged * Fix help for masternodelist status (POSE_BANNED and no OUTPOINT_SPENT)
2019-01-03 10:17:43 +01:00
return SerializeHash(*this);
}
bool CCoinJoinQueue::Sign()
{
2018-11-05 10:29:07 +01:00
if (!fMasternodeMode) return false;
uint256 hash = GetSignatureHash();
CBLSSignature sig = WITH_LOCK(activeMasternodeInfoCs, return activeMasternodeInfo.blsKeyOperator->Sign(hash));
if (!sig.IsValid()) {
return false;
}
vchSig = sig.ToByteVector();
return true;
}
bool CCoinJoinQueue::CheckSignature(const CBLSPublicKey& blsPubKey) const
{
if (!CBLSSignature(vchSig).VerifyInsecure(blsPubKey, GetSignatureHash())) {
LogPrint(BCLog::COINJOIN, "CCoinJoinQueue::CheckSignature -- VerifyInsecure() failed\n");
return false;
}
return true;
}
bool CCoinJoinQueue::Relay(CConnman& connman)
{
connman.ForEachNode([&connman, this](CNode* pnode) {
CNetMsgMaker msgMaker(pnode->GetSendVersion());
if (pnode->fSendDSQueue) {
connman.PushMessage(pnode, msgMaker.Make(NetMsgType::DSQUEUE, (*this)));
}
});
return true;
}
bool CCoinJoinQueue::IsTimeOutOfBounds(int64_t current_time) const
{
return current_time - nTime > COINJOIN_QUEUE_TIMEOUT ||
nTime - current_time > COINJOIN_QUEUE_TIMEOUT;
}
uint256 CCoinJoinBroadcastTx::GetSignatureHash() const
{
return SerializeHash(*this);
}
bool CCoinJoinBroadcastTx::Sign()
{
2018-11-05 10:29:07 +01:00
if (!fMasternodeMode) return false;
uint256 hash = GetSignatureHash();
CBLSSignature sig = WITH_LOCK(activeMasternodeInfoCs, return activeMasternodeInfo.blsKeyOperator->Sign(hash));
if (!sig.IsValid()) {
return false;
}
vchSig = sig.ToByteVector();
return true;
}
bool CCoinJoinBroadcastTx::CheckSignature(const CBLSPublicKey& blsPubKey) const
{
if (!CBLSSignature(vchSig).VerifyInsecure(blsPubKey, GetSignatureHash())) {
LogPrint(BCLog::COINJOIN, "CCoinJoinBroadcastTx::CheckSignature -- VerifyInsecure() failed\n");
return false;
}
return true;
}
bool CCoinJoinBroadcastTx::IsExpired(const CBlockIndex* pindex) const
{
// expire confirmed DSTXes after ~1h since confirmation or chainlocked confirmation
if (nConfirmedHeight == -1 || pindex->nHeight < nConfirmedHeight) return false; // not mined yet
if (pindex->nHeight - nConfirmedHeight > 24) return true; // mined more than an hour ago
return llmq::chainLocksHandler->HasChainLock(pindex->nHeight, *pindex->phashBlock);
}
refactor: misc refactoring in instantsend, llmq, coinjoin (#4212) * instantsend: make stuff const where possible Signed-off-by: pasta <pasta@dashboost.org> * instantsend: remove unused `params` Signed-off-by: pasta <pasta@dashboost.org> * instantsend: combine two nested if's into one Signed-off-by: pasta <pasta@dashboost.org> * instantsend: use auto in spots where possible and clear Signed-off-by: pasta <pasta@dashboost.org> * coinjoin: make IsValidStructure const Signed-off-by: pasta <pasta@dashboost.org> * coinjoin: divide by an integer to avoid double implicit conversions Signed-off-by: pasta <pasta@dashboost.org> * coinjoin: make unused parameter unnamed Signed-off-by: pasta <pasta@dashboost.org> * quorums.* use const and use references where possible Signed-off-by: pasta <pasta@dashboost.org> * quorums.h don't return const when returning by value Signed-off-by: pasta <pasta@dashboost.org> * quorums_blockprocessor.cpp remove redundant casts and combine two if statements Signed-off-by: pasta <pasta@dashboost.org> * quorums_blockprocessor.cpp make values const Signed-off-by: pasta <pasta@dashboost.org> * quorums_chainlocks.cpp access static function statically Signed-off-by: pasta <pasta@dashboost.org> * quorums_chainlocks.h remove commented out include Signed-off-by: pasta <pasta@dashboost.org> * quorums_commitment.cpp remove redundant casts Signed-off-by: pasta <pasta@dashboost.org> * quorums_debug.cpp remove redundant casts and add const Signed-off-by: pasta <pasta@dashboost.org> * quorums_dkgsession.cpp use const where possible Signed-off-by: pasta <pasta@dashboost.org> * quorums_dkgsessionhandler.cpp use const where possible Signed-off-by: pasta <pasta@dashboost.org> * quorums_dkgsessionhandler.cpp don't use std::move which apparently prevents "copy elision" in this instance Signed-off-by: pasta <pasta@dashboost.org> * quorums_dkgsessionhandler.cpp use const Signed-off-by: pasta <pasta@dashboost.org> * quorums_dkgsessionmgr.cpp misc refactoring Signed-off-by: pasta <pasta@dashboost.org> * quorums_signing.* misc refactoring Signed-off-by: pasta <pasta@dashboost.org> * quorums_signing_shares.* misc refactoring Signed-off-by: pasta <pasta@dashboost.org> * quorums_utils.cpp misc refactoring Signed-off-by: pasta <pasta@dashboost.org>
2021-06-26 15:10:53 +02:00
bool CCoinJoinBroadcastTx::IsValidStructure() const
{
// some trivial checks only
if (tx->vin.size() != tx->vout.size()) {
return false;
}
if (tx->vin.size() < size_t(CCoinJoin::GetMinPoolParticipants())) {
return false;
}
if (tx->vin.size() > CCoinJoin::GetMaxPoolParticipants() * COINJOIN_ENTRY_MAX_SIZE) {
return false;
}
return ranges::all_of(tx->vout, [] (const auto& txOut){
return CCoinJoin::IsDenominatedAmount(txOut.nValue) && txOut.scriptPubKey.IsPayToPublicKeyHash();
});
}
void CCoinJoinBaseSession::SetNull()
{
// Both sides
LOCK(cs_coinjoin);
nState = POOL_STATE_IDLE;
nSessionID = 0;
nSessionDenom = 0;
vecEntries.clear();
finalMutableTransaction.vin.clear();
finalMutableTransaction.vout.clear();
nTimeLastSuccessfulStep = GetTime();
}
void CCoinJoinBaseManager::SetNull()
{
LOCK(cs_vecqueue);
vecCoinJoinQueue.clear();
}
void CCoinJoinBaseManager::CheckQueue()
{
TRY_LOCK(cs_vecqueue, lockDS);
2018-11-05 10:29:07 +01:00
if (!lockDS) return; // it's ok to fail here, we run this quite frequently
// check mixing queue objects for timeouts
auto it = vecCoinJoinQueue.begin();
while (it != vecCoinJoinQueue.end()) {
if (it->IsTimeOutOfBounds()) {
LogPrint(BCLog::COINJOIN, "CCoinJoinBaseManager::%s -- Removing a queue (%s)\n", __func__, it->ToString());
it = vecCoinJoinQueue.erase(it);
} else {
2018-11-05 10:29:07 +01:00
++it;
}
}
}
bool CCoinJoinBaseManager::GetQueueItemAndTry(CCoinJoinQueue& dsqRet)
{
TRY_LOCK(cs_vecqueue, lockDS);
2018-11-05 10:29:07 +01:00
if (!lockDS) return false; // it's ok to fail here, we run this quite frequently
for (auto& dsq : vecCoinJoinQueue) {
// only try each queue once
if (dsq.fTried || dsq.IsTimeOutOfBounds()) continue;
dsq.fTried = true;
dsqRet = dsq;
return true;
}
return false;
}
std::string CCoinJoinBaseSession::GetStateString() const
{
2018-11-05 10:29:07 +01:00
switch (nState) {
case POOL_STATE_IDLE:
return "IDLE";
case POOL_STATE_QUEUE:
return "QUEUE";
case POOL_STATE_ACCEPTING_ENTRIES:
return "ACCEPTING_ENTRIES";
case POOL_STATE_SIGNING:
return "SIGNING";
case POOL_STATE_ERROR:
return "ERROR";
default:
return "UNKNOWN";
}
}
bool CCoinJoinBaseSession::IsValidInOuts(const std::vector<CTxIn>& vin, const std::vector<CTxOut>& vout, PoolMessage& nMessageIDRet, bool* fConsumeCollateralRet) const
{
AssertLockHeld(cs_main);
std::set<CScript> setScripPubKeys;
nMessageIDRet = MSG_NOERR;
if (fConsumeCollateralRet) *fConsumeCollateralRet = false;
if (vin.size() != vout.size()) {
LogPrint(BCLog::COINJOIN, "CCoinJoinBaseSession::%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) {
int nDenom = CCoinJoin::AmountToDenomination(txout.nValue);
if (nDenom != nSessionDenom) {
LogPrint(BCLog::COINJOIN, "CCoinJoinBaseSession::IsValidInOuts -- ERROR: incompatible denom %d (%s) != nSessionDenom %d (%s)\n",
nDenom, CCoinJoin::DenominationToString(nDenom), nSessionDenom, CCoinJoin::DenominationToString(nSessionDenom));
nMessageIDRet = ERR_DENOM;
if (fConsumeCollateralRet) *fConsumeCollateralRet = true;
return false;
}
if (!txout.scriptPubKey.IsPayToPublicKeyHash()) {
LogPrint(BCLog::COINJOIN, "CCoinJoinBaseSession::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::COINJOIN, "CCoinJoinBaseSession::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(&::ChainstateActive().CoinsTip(), mempool);
for (const auto& txin : vin) {
LogPrint(BCLog::COINJOIN, "CCoinJoinBaseSession::%s -- txin=%s\n", __func__, txin.ToString());
if (txin.prevout.IsNull()) {
LogPrint(BCLog::COINJOIN, "CCoinJoinBaseSession::%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::COINJOIN, "CCoinJoinBaseSession::%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 something wrong, bail out.
if (nFees != 0) {
LogPrint(BCLog::COINJOIN, "CCoinJoinBaseSession::%s -- ERROR: non-zero fees! fees: %lld\n", __func__, nFees);
nMessageIDRet = ERR_FEES;
return false;
}
return true;
}
// Definitions for static data members
CCriticalSection CCoinJoin::cs_mapdstx;
std::map<uint256, CCoinJoinBroadcastTx> CCoinJoin::mapDSTX GUARDED_BY(CCoinJoin::cs_mapdstx);
// check to make sure the collateral provided by the client is valid
bool CCoinJoin::IsCollateralValid(const CTransaction& txCollateral)
{
2018-11-05 10:29:07 +01:00
if (txCollateral.vout.empty()) return false;
if (txCollateral.nLockTime != 0) return false;
CAmount nValueIn = 0;
CAmount nValueOut = 0;
for (const auto& txout : txCollateral.vout) {
nValueOut += txout.nValue;
if (!txout.scriptPubKey.IsPayToPublicKeyHash() && !txout.scriptPubKey.IsUnspendable()) {
LogPrint(BCLog::COINJOIN, "CCoinJoin::IsCollateralValid -- Invalid Script, txCollateral=%s", txCollateral.ToString()); /* Continued */
return false;
}
}
for (const auto& txin : txCollateral.vin) {
2017-09-26 16:33:46 +02:00
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::COINJOIN, "CCoinJoin::IsCollateralValid -- spent or non-locked mempool input! txin=%s\n", txin.ToString());
return false;
}
nValueIn += mempoolTx->vout[txin.prevout.n].nValue;
} else if (GetUTXOCoin(txin.prevout, coin)) {
nValueIn += coin.out.nValue;
} else {
LogPrint(BCLog::COINJOIN, "CCoinJoin::IsCollateralValid -- Unknown inputs in collateral transaction, txCollateral=%s", txCollateral.ToString()); /* Continued */
return false;
}
}
//collateral transactions are required to pay out a small fee to the miners
2018-11-05 10:29:07 +01:00
if (nValueIn - nValueOut < GetCollateralAmount()) {
LogPrint(BCLog::COINJOIN, "CCoinJoin::IsCollateralValid -- did not include enough fees in transaction: fees: %d, txCollateral=%s", nValueOut - nValueIn, txCollateral.ToString()); /* Continued */
return false;
}
LogPrint(BCLog::COINJOIN, "CCoinJoin::IsCollateralValid -- %s", txCollateral.ToString()); /* Continued */
{
LOCK(cs_main);
CValidationState validationState;
if (!AcceptToMemoryPool(mempool, validationState, MakeTransactionRef(txCollateral), /*pfMissingInputs=*/nullptr, /*bypass_limits=*/false, /*nAbsurdFee=*/DEFAULT_MAX_RAW_TX_FEE, /*test_accept=*/true)) {
LogPrint(BCLog::COINJOIN, "CCoinJoin::IsCollateralValid -- didn't pass AcceptToMemoryPool()\n");
return false;
}
}
return true;
}
std::string CCoinJoin::DenominationToString(int nDenom)
{
CAmount nDenomAmount = DenominationToAmount(nDenom);
switch (nDenomAmount) {
case 0: return "N/A";
case -1: return "out-of-bounds";
case -2: return "non-denom";
case -3: return "to-amount-error";
default: return ValueFromAmount(nDenomAmount).getValStr();
}
// shouldn't happen
return "to-string-error";
}
2022-04-06 22:37:20 +02:00
bilingual_str CCoinJoin::GetMessageByID(PoolMessage nMessageID)
{
switch (nMessageID) {
2018-11-05 10:29:07 +01:00
case ERR_ALREADY_HAVE:
2022-04-06 22:37:20 +02:00
return _("Already have that input.");
2018-11-05 10:29:07 +01:00
case ERR_DENOM:
2022-04-06 22:37:20 +02:00
return _("No matching denominations found for mixing.");
2018-11-05 10:29:07 +01:00
case ERR_ENTRIES_FULL:
2022-04-06 22:37:20 +02:00
return _("Entries are full.");
2018-11-05 10:29:07 +01:00
case ERR_EXISTING_TX:
2022-04-06 22:37:20 +02:00
return _("Not compatible with existing transactions.");
2018-11-05 10:29:07 +01:00
case ERR_FEES:
2022-04-06 22:37:20 +02:00
return _("Transaction fees are too high.");
2018-11-05 10:29:07 +01:00
case ERR_INVALID_COLLATERAL:
2022-04-06 22:37:20 +02:00
return _("Collateral not valid.");
2018-11-05 10:29:07 +01:00
case ERR_INVALID_INPUT:
2022-04-06 22:37:20 +02:00
return _("Input is not valid.");
2018-11-05 10:29:07 +01:00
case ERR_INVALID_SCRIPT:
2022-04-06 22:37:20 +02:00
return _("Invalid script detected.");
2018-11-05 10:29:07 +01:00
case ERR_INVALID_TX:
2022-04-06 22:37:20 +02:00
return _("Transaction not valid.");
2018-11-05 10:29:07 +01:00
case ERR_MAXIMUM:
2022-04-06 22:37:20 +02:00
return _("Entry exceeds maximum size.");
2018-11-05 10:29:07 +01:00
case ERR_MN_LIST:
2022-04-06 22:37:20 +02:00
return _("Not in the Masternode list.");
2018-11-05 10:29:07 +01:00
case ERR_MODE:
2022-04-06 22:37:20 +02:00
return _("Incompatible mode.");
2018-11-05 10:29:07 +01:00
case ERR_QUEUE_FULL:
2022-04-06 22:37:20 +02:00
return _("Masternode queue is full.");
2018-11-05 10:29:07 +01:00
case ERR_RECENT:
2022-04-06 22:37:20 +02:00
return _("Last queue was created too recently.");
2018-11-05 10:29:07 +01:00
case ERR_SESSION:
2022-04-06 22:37:20 +02:00
return _("Session not complete!");
2018-11-05 10:29:07 +01:00
case ERR_MISSING_TX:
2022-04-06 22:37:20 +02:00
return _("Missing input transaction information.");
2018-11-05 10:29:07 +01:00
case ERR_VERSION:
2022-04-06 22:37:20 +02:00
return _("Incompatible version.");
2018-11-05 10:29:07 +01:00
case MSG_NOERR:
2022-04-06 22:37:20 +02:00
return _("No errors detected.");
2018-11-05 10:29:07 +01:00
case MSG_SUCCESS:
2022-04-06 22:37:20 +02:00
return _("Transaction created successfully.");
2018-11-05 10:29:07 +01:00
case MSG_ENTRIES_ADDED:
2022-04-06 22:37:20 +02:00
return _("Your entries added successfully.");
case ERR_SIZE_MISMATCH:
2022-04-06 22:37:20 +02:00
return _("Inputs vs outputs size mismatch.");
case ERR_NON_STANDARD_PUBKEY:
case ERR_NOT_A_MN:
2018-11-05 10:29:07 +01:00
default:
2022-04-06 22:37:20 +02:00
return _("Unknown response.");
}
}
void CCoinJoin::AddDSTX(const CCoinJoinBroadcastTx& dstx)
{
LOCK(cs_mapdstx);
mapDSTX.insert(std::make_pair(dstx.tx->GetHash(), dstx));
}
CCoinJoinBroadcastTx CCoinJoin::GetDSTX(const uint256& hash)
{
LOCK(cs_mapdstx);
auto it = mapDSTX.find(hash);
return (it == mapDSTX.end()) ? CCoinJoinBroadcastTx() : it->second;
}
void CCoinJoin::CheckDSTXes(const CBlockIndex* pindex)
{
LOCK(cs_mapdstx);
auto it = mapDSTX.begin();
2018-11-05 10:29:07 +01:00
while (it != mapDSTX.end()) {
if (it->second.IsExpired(pindex)) {
mapDSTX.erase(it++);
} else {
++it;
}
}
LogPrint(BCLog::COINJOIN, "CCoinJoin::CheckDSTXes -- mapDSTX.size()=%llu\n", mapDSTX.size());
}
void CCoinJoin::UpdatedBlockTip(const CBlockIndex* pindex)
{
if (pindex && masternodeSync.IsBlockchainSynced()) {
CheckDSTXes(pindex);
}
}
void CCoinJoin::NotifyChainLock(const CBlockIndex* pindex)
{
if (pindex && masternodeSync.IsBlockchainSynced()) {
CheckDSTXes(pindex);
}
}
void CCoinJoin::UpdateDSTXConfirmedHeight(const CTransactionRef& tx, int nHeight)
{
AssertLockHeld(cs_mapdstx);
auto it = mapDSTX.find(tx->GetHash());
if (it == mapDSTX.end()) {
return;
}
it->second.SetConfirmedHeight(nHeight);
LogPrint(BCLog::COINJOIN, "CCoinJoin::%s -- txid=%s, nHeight=%d\n", __func__, tx->GetHash().ToString(), nHeight);
}
void CCoinJoin::TransactionAddedToMempool(const CTransactionRef& tx)
{
LOCK(cs_mapdstx);
UpdateDSTXConfirmedHeight(tx, -1);
}
void CCoinJoin::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex, const std::vector<CTransactionRef>& vtxConflicted)
{
LOCK(cs_mapdstx);
for (const auto& tx : vtxConflicted) {
UpdateDSTXConfirmedHeight(tx, -1);
}
for (const auto& tx : pblock->vtx) {
UpdateDSTXConfirmedHeight(tx, pindex->nHeight);
}
}
refactor: misc refactoring in instantsend, llmq, coinjoin (#4212) * instantsend: make stuff const where possible Signed-off-by: pasta <pasta@dashboost.org> * instantsend: remove unused `params` Signed-off-by: pasta <pasta@dashboost.org> * instantsend: combine two nested if's into one Signed-off-by: pasta <pasta@dashboost.org> * instantsend: use auto in spots where possible and clear Signed-off-by: pasta <pasta@dashboost.org> * coinjoin: make IsValidStructure const Signed-off-by: pasta <pasta@dashboost.org> * coinjoin: divide by an integer to avoid double implicit conversions Signed-off-by: pasta <pasta@dashboost.org> * coinjoin: make unused parameter unnamed Signed-off-by: pasta <pasta@dashboost.org> * quorums.* use const and use references where possible Signed-off-by: pasta <pasta@dashboost.org> * quorums.h don't return const when returning by value Signed-off-by: pasta <pasta@dashboost.org> * quorums_blockprocessor.cpp remove redundant casts and combine two if statements Signed-off-by: pasta <pasta@dashboost.org> * quorums_blockprocessor.cpp make values const Signed-off-by: pasta <pasta@dashboost.org> * quorums_chainlocks.cpp access static function statically Signed-off-by: pasta <pasta@dashboost.org> * quorums_chainlocks.h remove commented out include Signed-off-by: pasta <pasta@dashboost.org> * quorums_commitment.cpp remove redundant casts Signed-off-by: pasta <pasta@dashboost.org> * quorums_debug.cpp remove redundant casts and add const Signed-off-by: pasta <pasta@dashboost.org> * quorums_dkgsession.cpp use const where possible Signed-off-by: pasta <pasta@dashboost.org> * quorums_dkgsessionhandler.cpp use const where possible Signed-off-by: pasta <pasta@dashboost.org> * quorums_dkgsessionhandler.cpp don't use std::move which apparently prevents "copy elision" in this instance Signed-off-by: pasta <pasta@dashboost.org> * quorums_dkgsessionhandler.cpp use const Signed-off-by: pasta <pasta@dashboost.org> * quorums_dkgsessionmgr.cpp misc refactoring Signed-off-by: pasta <pasta@dashboost.org> * quorums_signing.* misc refactoring Signed-off-by: pasta <pasta@dashboost.org> * quorums_signing_shares.* misc refactoring Signed-off-by: pasta <pasta@dashboost.org> * quorums_utils.cpp misc refactoring Signed-off-by: pasta <pasta@dashboost.org>
2021-06-26 15:10:53 +02:00
void CCoinJoin::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex*)
{
LOCK(cs_mapdstx);
for (const auto& tx : pblock->vtx) {
UpdateDSTXConfirmedHeight(tx, -1);
}
}
int CCoinJoin::GetMinPoolParticipants() { return Params().PoolMinParticipants(); }
int CCoinJoin::GetMaxPoolParticipants() { return Params().PoolMaxParticipants(); }