Refactor PrivateSend (#1735)

* make infoMixingMasternode private

* move PS queue entries expiration checks (and cs_darksend) to CPrivateSendBase

* drop CTxDSOut

* move prevPubKey out of CTxIn into CTxDSIn and use CTxDSIn explicitly

* drop CPrivateSendClient::NewBlock

* move IsDenominatedAmount to CPrivateSend

* move IsCollateralAmount to CPrivateSend

* drop darksend-relay.cpp/h

* drop GetMasternodeByRank
This commit is contained in:
UdjinM6 2017-12-04 09:06:07 +03:00 committed by GitHub
parent c166ed39b0
commit 7e96af4e65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 169 additions and 412 deletions

View File

@ -172,7 +172,7 @@ When queue is ready user is expected to send his entry to start actual mixing
| ? | vecTxDSIn | CTxDSIn[] | vector of users inputs (CTxDSIn serialization is equal to [CTxIn](#ctxin) serialization)
| 8 | nAmount | int64_t | depreciated since 12.1, it's used for backwards compatibility only and can be removed with future protocol bump
| ? | txCollateral | [CTransaction](#ctransaction) | Collateral transaction which is used to prevent misbehavior and also to charge fees randomly
| ? | vecTxDSOut | CTxDSOut[] | vector of user outputs (CTxDSOut serialization is equal to [CTxOut](#ctxout) serialization)
| ? | vecTxOut | CTxOut[] | vector of user outputs
### DSSIGNFINALTX - "dss"

View File

@ -1,116 +0,0 @@
#include "darksend.h"
#include "darksend-relay.h"
#include "messagesigner.h"
CDarkSendRelay::CDarkSendRelay()
{
vinMasternode = CTxIn();
nBlockHeight = 0;
nRelayType = 0;
in = CTxIn();
out = CTxOut();
}
CDarkSendRelay::CDarkSendRelay(CTxIn& vinMasternodeIn, vector<unsigned char>& vchSigIn, int nBlockHeightIn, int nRelayTypeIn, CTxIn& in2, CTxOut& out2)
{
vinMasternode = vinMasternodeIn;
vchSig = vchSigIn;
nBlockHeight = nBlockHeightIn;
nRelayType = nRelayTypeIn;
in = in2;
out = out2;
}
std::string CDarkSendRelay::ToString()
{
std::ostringstream info;
info << "vin: " << vinMasternode.ToString() <<
" nBlockHeight: " << (int)nBlockHeight <<
" nRelayType: " << (int)nRelayType <<
" in " << in.ToString() <<
" out " << out.ToString();
return info.str();
}
bool CDarkSendRelay::Sign(std::string strSharedKey)
{
std::string strError = "";
std::string strMessage = in.ToString() + out.ToString();
CKey key2;
CPubKey pubkey2;
if(!CMessageSigner::GetKeysFromSecret(strSharedKey, key2, pubkey2)) {
LogPrintf("CDarkSendRelay::Sign -- GetKeysFromSecret() failed, invalid shared key %s\n", strSharedKey);
return false;
}
if(!CMessageSigner::SignMessage(strMessage, vchSig2, key2)) {
LogPrintf("CDarkSendRelay::Sign -- SignMessage() failed\n");
return false;
}
if(!CMessageSigner::VerifyMessage(pubkey2, vchSig2, strMessage, strError)) {
LogPrintf("CDarkSendRelay::Sign -- VerifyMessage() failed, error: %s\n", strError);
return false;
}
return true;
}
bool CDarkSendRelay::VerifyMessage(std::string strSharedKey)
{
std::string strError = "";
std::string strMessage = in.ToString() + out.ToString();
CKey key2;
CPubKey pubkey2;
if(!CMessageSigner::GetKeysFromSecret(strSharedKey, key2, pubkey2)) {
LogPrintf("CDarkSendRelay::VerifyMessage -- GetKeysFromSecret() failed, invalid shared key %s\n", strSharedKey);
return false;
}
if(!CMessageSigner::VerifyMessage(pubkey2, vchSig2, strMessage, strError)) {
LogPrintf("CDarkSendRelay::VerifyMessage -- VerifyMessage() failed, error: %s\n", strError);
return false;
}
return true;
}
void CDarkSendRelay::Relay()
{
int nCount = std::min(mnodeman.CountEnabled(MIN_PRIVATESEND_PEER_PROTO_VERSION), 20);
int nRank1 = (rand() % nCount)+1;
int nRank2 = (rand() % nCount)+1;
//keep picking another second number till we get one that doesn't match
while(nRank1 == nRank2) nRank2 = (rand() % nCount)+1;
//printf("rank 1 - rank2 %d %d \n", nRank1, nRank2);
//relay this message through 2 separate nodes for redundancy
RelayThroughNode(nRank1);
RelayThroughNode(nRank2);
}
void CDarkSendRelay::RelayThroughNode(int nRank)
{
masternode_info_t mnInfo;
if(mnodeman.GetMasternodeByRank(nRank, mnInfo, nBlockHeight, MIN_PRIVATESEND_PEER_PROTO_VERSION)) {
//printf("RelayThroughNode %s\n", mnInfo.addr.ToString().c_str());
// TODO: Pass CConnman instance somehow and don't use global variable.
CNode* pnode = g_connman->ConnectNode((CAddress)mnInfo.addr, NULL);
if(pnode) {
//printf("Connected\n");
pnode->PushMessage("dsr", (*this));
return;
}
} else {
//printf("RelayThroughNode NULL\n");
}
}

View File

@ -1,51 +0,0 @@
// Copyright (c) 2014-2017 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.
#ifndef DARKSEND_RELAY_H
#define DARKSEND_RELAY_H
#include "validation.h"
#include "activemasternode.h"
#include "masternodeman.h"
class CDarkSendRelay
{
public:
CTxIn vinMasternode;
vector<unsigned char> vchSig;
vector<unsigned char> vchSig2;
int nBlockHeight;
int nRelayType;
CTxIn in;
CTxOut out;
CDarkSendRelay();
CDarkSendRelay(CTxIn& vinMasternodeIn, vector<unsigned char>& vchSigIn, int nBlockHeightIn, int nRelayTypeIn, CTxIn& in2, CTxOut& out2);
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(vinMasternode);
READWRITE(vchSig);
READWRITE(vchSig2);
READWRITE(nBlockHeight);
READWRITE(nRelayType);
READWRITE(in);
READWRITE(out);
}
std::string ToString();
bool Sign(std::string strSharedKey);
bool VerifyMessage(std::string strSharedKey);
void Relay();
void RelayThroughNode(int nRank);
};
#endif

View File

@ -693,51 +693,16 @@ bool CMasternodeMan::GetMasternodeRanks(CMasternodeMan::rank_pair_vec_t& vecMast
return true;
}
bool CMasternodeMan::GetMasternodeByRank(int nRankIn, masternode_info_t& mnInfoRet, int nBlockHeight, int nMinProtocol)
{
mnInfoRet = masternode_info_t();
if (!masternodeSync.IsMasternodeListSynced())
return false;
// make sure we know about this block
uint256 nBlockHash = uint256();
if (!GetBlockHash(nBlockHash, nBlockHeight)) {
LogPrintf("CMasternodeMan::%s -- ERROR: GetBlockHash() failed at nBlockHeight %d\n", __func__, nBlockHeight);
return false;
}
LOCK(cs);
score_pair_vec_t vecMasternodeScores;
if (!GetMasternodeScores(nBlockHash, vecMasternodeScores, nMinProtocol))
return false;
if (vecMasternodeScores.size() < nRankIn)
return false;
int nRank = 0;
for (auto& scorePair : vecMasternodeScores) {
nRank++;
if(nRank == nRankIn) {
mnInfoRet = *scorePair.second;
return true;
}
}
return false;
}
void CMasternodeMan::ProcessMasternodeConnections(CConnman& connman)
{
//we don't care about this for regtest
if(Params().NetworkIDString() == CBaseChainParams::REGTEST) return;
connman.ForEachNode(CConnman::AllNodes, [](CNode* pnode) {
if(pnode->fMasternode) {
#ifdef ENABLE_WALLET
if(privateSendClient.infoMixingMasternode.fInfoValid && pnode->addr == privateSendClient.infoMixingMasternode.addr)
return;
if(pnode->fMasternode && !privateSendClient.IsMixingMasternode(pnode)) {
#else
if(pnode->fMasternode) {
#endif // ENABLE_WALLET
LogPrintf("Closing Masternode connection: peer=%d, addr=%s\n", pnode->id, pnode->addr.ToString());
pnode->fDisconnect = true;

View File

@ -177,7 +177,6 @@ public:
bool GetMasternodeRanks(rank_pair_vec_t& vecMasternodeRanksRet, int nBlockHeight = -1, int nMinProtocol = 0);
bool GetMasternodeRank(const COutPoint &outpoint, int& nRankRet, int nBlockHeight = -1, int nMinProtocol = 0);
bool GetMasternodeByRank(int nRank, masternode_info_t& mnInfoRet, int nBlockHeight = -1, int nMinProtocol = 0);
void ProcessMasternodeConnections(CConnman& connman);
std::pair<CService, std::set<uint256> > PopScheduledMnbRequestConnection();

View File

@ -61,7 +61,6 @@ public:
COutPoint prevout;
CScript scriptSig;
uint32_t nSequence;
CScript prevPubKey;
/* Setting nSequence to this value for every input in a transaction
* disables nLockTime. */

View File

@ -285,6 +285,17 @@ std::string CPrivateSendClient::GetStatus()
}
}
bool CPrivateSendClient::GetMixingMasternodeInfo(masternode_info_t& mnInfoRet)
{
mnInfoRet = infoMixingMasternode.fInfoValid ? infoMixingMasternode : masternode_info_t();
return infoMixingMasternode.fInfoValid;
}
bool CPrivateSendClient::IsMixingMasternode(const CNode* pnode)
{
return infoMixingMasternode.fInfoValid && pnode->addr == infoMixingMasternode.addr;
}
//
// Check the mixing progress and send client updates if a Masternode
//
@ -308,19 +319,7 @@ void CPrivateSendClient::CheckPool()
//
void CPrivateSendClient::CheckTimeout()
{
{
TRY_LOCK(cs_darksend, lockDS);
if(!lockDS) return; // it's ok to fail here, we run this quite frequently
// check mixing queue objects for timeouts
std::vector<CDarksendQueue>::iterator it = vecDarksendQueue.begin();
while(it != vecDarksendQueue.end()) {
if((*it).IsExpired()) {
LogPrint("privatesend", "CPrivateSendClient::CheckTimeout -- Removing expired queue (%s)\n", (*it).ToString());
it = vecDarksendQueue.erase(it);
} else ++it;
}
}
CheckQueue();
if(!fEnablePrivateSend && !fMasterNode) return;
@ -359,7 +358,7 @@ void CPrivateSendClient::CheckTimeout()
// Execute a mixing denomination via a Masternode.
// This is only ran from clients
//
bool CPrivateSendClient::SendDenominate(const std::vector<CTxIn>& vecTxIn, const std::vector<CTxOut>& vecTxOut, CConnman& connman)
bool CPrivateSendClient::SendDenominate(const std::vector<CTxDSIn>& vecTxDSIn, const std::vector<CTxOut>& vecTxOut, CConnman& connman)
{
if(fMasterNode) {
LogPrintf("CPrivateSendClient::SendDenominate -- PrivateSend from a Masternode is not supported currently.\n");
@ -375,8 +374,8 @@ bool CPrivateSendClient::SendDenominate(const std::vector<CTxIn>& vecTxIn, const
BOOST_FOREACH(CTxIn txin, txMyCollateral.vin)
vecOutPointLocked.push_back(txin.prevout);
BOOST_FOREACH(CTxIn txin, vecTxIn)
vecOutPointLocked.push_back(txin.prevout);
for (const auto& txdsin : vecTxDSIn)
vecOutPointLocked.push_back(txdsin.prevout);
// we should already be connected to a Masternode
if(!nSessionID) {
@ -406,9 +405,9 @@ bool CPrivateSendClient::SendDenominate(const std::vector<CTxIn>& vecTxIn, const
CValidationState validationState;
CMutableTransaction tx;
BOOST_FOREACH(const CTxIn& txin, vecTxIn) {
LogPrint("privatesend", "CPrivateSendClient::SendDenominate -- txin=%s\n", txin.ToString());
tx.vin.push_back(txin);
for (const auto& txdsin : vecTxDSIn) {
LogPrint("privatesend", "CPrivateSendClient::SendDenominate -- txdsin=%s\n", txdsin.ToString());
tx.vin.push_back(txdsin);
}
BOOST_FOREACH(const CTxOut& txout, vecTxOut) {
@ -430,7 +429,7 @@ bool CPrivateSendClient::SendDenominate(const std::vector<CTxIn>& vecTxIn, const
}
// store our entry for later use
CDarkSendEntry entry(vecTxIn, vecTxOut, txMyCollateral);
CDarkSendEntry entry(vecTxDSIn, vecTxOut, txMyCollateral);
vecEntries.push_back(entry);
RelayIn(entry, connman);
nTimeLastSuccessfulStep = GetTimeMillis();
@ -527,19 +526,19 @@ bool CPrivateSendClient::SignFinalTransaction(const CTransaction& finalTransacti
CAmount nValue1 = 0;
CAmount nValue2 = 0;
for(unsigned int i = 0; i < finalMutableTransaction.vout.size(); i++) {
BOOST_FOREACH(const CTxOut& txout, entry.vecTxDSOut) {
if(finalMutableTransaction.vout[i] == txout) {
for (const auto& txoutFinal : finalMutableTransaction.vout) {
for (const auto& txout: entry.vecTxOut) {
if(txoutFinal == txout) {
nFoundOutputsCount++;
nValue1 += finalMutableTransaction.vout[i].nValue;
nValue1 += txoutFinal.nValue;
}
}
}
BOOST_FOREACH(const CTxOut txout, entry.vecTxDSOut)
for (const auto& txout : entry.vecTxOut)
nValue2 += txout.nValue;
int nTargetOuputsCount = entry.vecTxDSOut.size();
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.
@ -583,18 +582,6 @@ bool CPrivateSendClient::SignFinalTransaction(const CTransaction& finalTransacti
return true;
}
void CPrivateSendClient::NewBlock()
{
static int64_t nTimeNewBlockReceived = 0;
//we we're processing lots of blocks, we'll just leave
if(GetTime() - nTimeNewBlockReceived < 10) return;
nTimeNewBlockReceived = GetTime();
LogPrint("privatesend", "CPrivateSendClient::NewBlock\n");
CheckTimeout();
}
// mixing transaction was completed (failed or successful)
void CPrivateSendClient::CompletedTransaction(PoolMessage nMessageID)
{
@ -875,11 +862,11 @@ bool CPrivateSendClient::JoinExistingQueue(CAmount nBalanceNeedsAnonymized, CCon
LogPrint("privatesend", "CPrivateSendClient::JoinExistingQueue -- found valid queue: %s\n", dsq.ToString());
CAmount nValueInTmp = 0;
std::vector<CTxIn> vecTxInTmp;
std::vector<CTxDSIn> vecTxDSInTmp;
std::vector<COutput> vCoinsTmp;
// Try to match their denominations if possible, select at least 1 denominations
if(!pwalletMain->SelectCoinsByDenominations(dsq.nDenom, vecStandardDenoms[vecBits.front()], nBalanceNeedsAnonymized, vecTxInTmp, vCoinsTmp, nValueInTmp, 0, nPrivateSendRounds)) {
if(!pwalletMain->SelectCoinsByDenominations(dsq.nDenom, vecStandardDenoms[vecBits.front()], nBalanceNeedsAnonymized, vecTxDSInTmp, vCoinsTmp, nValueInTmp, 0, nPrivateSendRounds)) {
LogPrintf("CPrivateSendClient::JoinExistingQueue -- Couldn't match denominations %d %d (%s)\n", vecBits.front(), dsq.nDenom, CPrivateSend::GetDenominationsToString(dsq.nDenom));
continue;
}
@ -1011,23 +998,23 @@ bool CPrivateSendClient::StartNewQueue(CAmount nValueMin, CAmount nBalanceNeedsA
bool CPrivateSendClient::SubmitDenominate(CConnman& connman)
{
std::string strError;
std::vector<CTxIn> vecTxInRet;
std::vector<CTxDSIn> vecTxDSInRet;
std::vector<CTxOut> vecTxOutRet;
// Submit transaction to the pool if we get here
// Try to use only inputs with the same number of rounds starting from the highest number of rounds possible
for(int i = nPrivateSendRounds; i > 0; i--) {
if(PrepareDenominate(i - 1, i, strError, vecTxInRet, vecTxOutRet)) {
if(PrepareDenominate(i - 1, i, strError, vecTxDSInRet, vecTxOutRet)) {
LogPrintf("CPrivateSendClient::SubmitDenominate -- Running PrivateSend denominate for %d rounds, success\n", i);
return SendDenominate(vecTxInRet, vecTxOutRet, connman);
return SendDenominate(vecTxDSInRet, vecTxOutRet, connman);
}
LogPrint("privatesend", "CPrivateSendClient::SubmitDenominate -- Running PrivateSend denominate for %d rounds, error: %s\n", i, strError);
}
// We failed? That's strange but let's just make final attempt and try to mix everything
if(PrepareDenominate(0, nPrivateSendRounds, strError, vecTxInRet, vecTxOutRet)) {
if(PrepareDenominate(0, nPrivateSendRounds, strError, vecTxDSInRet, vecTxOutRet)) {
LogPrintf("CPrivateSendClient::SubmitDenominate -- Running PrivateSend denominate for all rounds, success\n");
return SendDenominate(vecTxInRet, vecTxOutRet, connman);
return SendDenominate(vecTxDSInRet, vecTxOutRet, connman);
}
// Should never actually get here but just in case
@ -1036,7 +1023,7 @@ bool CPrivateSendClient::SubmitDenominate(CConnman& connman)
return false;
}
bool CPrivateSendClient::PrepareDenominate(int nMinRounds, int nMaxRounds, std::string& strErrorRet, std::vector<CTxIn>& vecTxInRet, std::vector<CTxOut>& vecTxOutRet)
bool CPrivateSendClient::PrepareDenominate(int nMinRounds, int nMaxRounds, std::string& strErrorRet, std::vector<CTxDSIn>& vecTxDSInRet, std::vector<CTxOut>& vecTxOutRet)
{
if(!pwalletMain) {
strErrorRet = "Wallet is not initialized";
@ -1054,11 +1041,11 @@ bool CPrivateSendClient::PrepareDenominate(int nMinRounds, int nMaxRounds, std::
}
// make sure returning vectors are empty before filling them up
vecTxInRet.clear();
vecTxDSInRet.clear();
vecTxOutRet.clear();
// ** find the coins we'll use
std::vector<CTxIn> vecTxIn;
std::vector<CTxDSIn> vecTxDSIn;
std::vector<COutput> vCoins;
CAmount nValueIn = 0;
@ -1073,7 +1060,7 @@ bool CPrivateSendClient::PrepareDenominate(int nMinRounds, int nMaxRounds, std::
return false;
}
std::vector<CAmount> vecStandardDenoms = CPrivateSend::GetStandardDenominations();
bool fSelected = pwalletMain->SelectCoinsByDenominations(nSessionDenom, vecStandardDenoms[vecBits.front()], CPrivateSend::GetMaxPoolAmount(), vecTxIn, vCoins, nValueIn, nMinRounds, nMaxRounds);
bool fSelected = pwalletMain->SelectCoinsByDenominations(nSessionDenom, vecStandardDenoms[vecBits.front()], CPrivateSend::GetMaxPoolAmount(), vecTxDSIn, vCoins, nValueIn, nMinRounds, nMaxRounds);
if (nMinRounds >= 0 && !fSelected) {
strErrorRet = "Can't select current denominated inputs";
return false;
@ -1083,7 +1070,7 @@ bool CPrivateSendClient::PrepareDenominate(int nMinRounds, int nMaxRounds, std::
{
LOCK(pwalletMain->cs_wallet);
BOOST_FOREACH(CTxIn txin, vecTxIn) {
for (auto& txin : vecTxDSIn) {
pwalletMain->LockCoin(txin.prevout);
}
}
@ -1102,15 +1089,15 @@ bool CPrivateSendClient::PrepareDenominate(int nMinRounds, int nMaxRounds, std::
if (nValueLeft - nValueDenom < 0) continue;
// Note: this relies on a fact that both vectors MUST have same size
std::vector<CTxIn>::iterator it = vecTxIn.begin();
std::vector<CTxDSIn>::iterator it = vecTxDSIn.begin();
std::vector<COutput>::iterator it2 = vCoins.begin();
while (it2 != vCoins.end()) {
// we have matching inputs
if ((*it2).tx->vout[(*it2).i].nValue == nValueDenom) {
// add new input in resulting vector
vecTxInRet.push_back(*it);
vecTxDSInRet.push_back(*it);
// remove corresponting items from initial vectors
vecTxIn.erase(it);
vecTxDSIn.erase(it);
vCoins.erase(it2);
CScript scriptDenom = keyHolderStorage.AddKey(pwalletMain).GetScriptForDestination();
@ -1136,7 +1123,7 @@ bool CPrivateSendClient::PrepareDenominate(int nMinRounds, int nMaxRounds, std::
{
// unlock unused coins
LOCK(pwalletMain->cs_wallet);
BOOST_FOREACH(CTxIn txin, vecTxIn) {
for (auto& txin : vecTxDSIn) {
pwalletMain->UnlockCoin(txin.prevout);
}
}
@ -1144,7 +1131,7 @@ bool CPrivateSendClient::PrepareDenominate(int nMinRounds, int nMaxRounds, std::
if (CPrivateSend::GetDenominations(vecTxOutRet) != nSessionDenom) {
// unlock used coins on failure
LOCK(pwalletMain->cs_wallet);
BOOST_FOREACH(CTxIn txin, vecTxInRet) {
for (auto& txin : vecTxDSInRet) {
pwalletMain->UnlockCoin(txin.prevout);
}
keyHolderStorage.ReturnAll();
@ -1188,7 +1175,7 @@ bool CPrivateSendClient::MakeCollateralAmounts(const CompactTallyItem& tallyItem
LOCK2(cs_main, pwalletMain->cs_wallet);
// denominated input is always a single one, so we can check its amount directly and return early
if(!fTryDenominated && tallyItem.vecTxIn.size() == 1 && pwalletMain->IsDenominatedAmount(tallyItem.nAmount))
if(!fTryDenominated && tallyItem.vecTxIn.size() == 1 && CPrivateSend::IsDenominatedAmount(tallyItem.nAmount))
return false;
CWalletTx wtx;
@ -1404,10 +1391,6 @@ void CPrivateSendClient::UpdatedBlockTip(const CBlockIndex *pindex)
nCachedBlockHeight = pindex->nHeight;
LogPrint("privatesend", "CPrivateSendClient::UpdatedBlockTip -- nCachedBlockHeight: %d\n", nCachedBlockHeight);
if(!fLiteMode && masternodeSync.IsMasternodeListSynced()) {
NewBlock();
}
CPrivateSend::CheckDSTXes(pindex->nHeight);
}

View File

@ -34,8 +34,6 @@ extern CPrivateSendClient privateSendClient;
class CPrivateSendClient : public CPrivateSendBase
{
private:
mutable CCriticalSection cs_darksend;
// Keep track of the used Masternodes
std::vector<COutPoint> vecMasternodesUsed;
@ -54,6 +52,7 @@ private:
std::string strLastMessage;
std::string strAutoDenomResult;
masternode_info_t infoMixingMasternode;
CMutableTransaction txMyCollateral; // client side collateral
CKeyHolderStorage keyHolderStorage; // storage for keys used in PrepareDenominate
@ -84,9 +83,9 @@ private:
/// As a client, submit part of a future mixing transaction to a Masternode to start the process
bool SubmitDenominate(CConnman& connman);
/// step 1: prepare denominated inputs and outputs
bool PrepareDenominate(int nMinRounds, int nMaxRounds, std::string& strErrorRet, std::vector<CTxIn>& vecTxInRet, std::vector<CTxOut>& vecTxOutRet);
bool PrepareDenominate(int nMinRounds, int nMaxRounds, std::string& strErrorRet, std::vector<CTxDSIn>& vecTxDSInRet, std::vector<CTxOut>& vecTxOutRet);
/// step 2: send denominated inputs and outputs prepared in step 1
bool SendDenominate(const std::vector<CTxIn>& vecTxIn, const std::vector<CTxOut>& vecTxOut, CConnman& connman);
bool SendDenominate(const std::vector<CTxDSIn>& vecTxDSIn, const std::vector<CTxOut>& vecTxOut, CConnman& connman);
/// Get Masternode updates about the progress of mixing
bool CheckPoolStateUpdate(PoolState nStateNew, int nEntriesCountNew, PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID, int nSessionIDNew=0);
@ -107,7 +106,6 @@ public:
bool fEnablePrivateSend;
bool fPrivateSendMultiSession;
masternode_info_t infoMixingMasternode;
int nCachedNumBlocks; //used for the overview screen
bool fCreateAutoBackups; //builtin support for automatic backups
@ -129,20 +127,21 @@ public:
void SetMinBlocksToWait(int nMinBlocksToWaitIn) { nMinBlocksToWait = nMinBlocksToWaitIn; }
void ResetPool();
void UnlockCoins();
std::string GetStatus();
bool GetMixingMasternodeInfo(masternode_info_t& mnInfoRet);
bool IsMixingMasternode(const CNode* pnode);
/// Passively run mixing in the background according to the configuration in settings
bool DoAutomaticDenominating(CConnman& connman, bool fDryRun=false);
void CheckTimeout();
/// Process a new block
void NewBlock();
void UpdatedBlockTip(const CBlockIndex *pindex);
};

View File

@ -153,14 +153,14 @@ void CPrivateSendServer::ProcessMessage(CNode* pfrom, std::string& strCommand, C
return;
}
if(entry.vecTxDSOut.size() > PRIVATESEND_ENTRY_MAX_SIZE) {
LogPrintf("DSVIN -- ERROR: too many outputs! %d/%d\n", entry.vecTxDSOut.size(), PRIVATESEND_ENTRY_MAX_SIZE);
if(entry.vecTxOut.size() > PRIVATESEND_ENTRY_MAX_SIZE) {
LogPrintf("DSVIN -- ERROR: too many outputs! %d/%d\n", entry.vecTxOut.size(), PRIVATESEND_ENTRY_MAX_SIZE);
PushStatus(pfrom, STATUS_REJECTED, ERR_MAXIMUM, connman);
return;
}
//do we have the same denominations as the current session?
if(!IsOutputsCompatibleWithSessionDenom(entry.vecTxDSOut)) {
if(!IsOutputsCompatibleWithSessionDenom(entry.vecTxOut)) {
LogPrintf("DSVIN -- not compatible with existing transactions!\n");
PushStatus(pfrom, STATUS_REJECTED, ERR_EXISTING_TX, connman);
return;
@ -173,7 +173,7 @@ void CPrivateSendServer::ProcessMessage(CNode* pfrom, std::string& strCommand, C
CMutableTransaction tx;
BOOST_FOREACH(const CTxOut txout, entry.vecTxDSOut) {
for (const auto& txout : entry.vecTxOut) {
nValueOut += txout.nValue;
tx.vout.push_back(txout);
@ -311,8 +311,8 @@ void CPrivateSendServer::CreateFinalTransaction(CConnman& connman)
// make our new transaction
for(int i = 0; i < GetEntriesCount(); i++) {
BOOST_FOREACH(const CTxDSOut& txdsout, vecEntries[i].vecTxDSOut)
txNew.vout.push_back(txdsout);
for (const auto& txout : vecEntries[i].vecTxOut)
txNew.vout.push_back(txout);
BOOST_FOREACH(const CTxDSIn& txdsin, vecEntries[i].vecTxDSIn)
txNew.vin.push_back(txdsin);
@ -495,19 +495,7 @@ void CPrivateSendServer::ChargeRandomFees(CConnman& connman)
//
void CPrivateSendServer::CheckTimeout(CConnman& connman)
{
{
TRY_LOCK(cs_darksend, lockDS);
if(!lockDS) return; // it's ok to fail here, we run this quite frequently
// check mixing queue objects for timeouts
std::vector<CDarksendQueue>::iterator it = vecDarksendQueue.begin();
while(it != vecDarksendQueue.end()) {
if((*it).IsExpired()) {
LogPrint("privatesend", "CPrivateSendServer::CheckTimeout -- Removing expired queue (%s)\n", (*it).ToString());
it = vecDarksendQueue.erase(it);
} else ++it;
}
}
CheckQueue();
if(!fMasterNode) return;
@ -556,8 +544,8 @@ bool CPrivateSendServer::IsInputScriptSigValid(const CTxIn& txin)
BOOST_FOREACH(CDarkSendEntry& entry, vecEntries) {
BOOST_FOREACH(const CTxDSOut& txdsout, entry.vecTxDSOut)
txNew.vout.push_back(txdsout);
for (const auto& txout : entry.vecTxOut)
txNew.vout.push_back(txout);
BOOST_FOREACH(const CTxDSIn& txdsin, entry.vecTxDSIn) {
txNew.vin.push_back(txdsin);
@ -658,7 +646,6 @@ bool CPrivateSendServer::AddScriptSig(const CTxIn& txinNew)
BOOST_FOREACH(CTxIn& txin, finalMutableTransaction.vin) {
if(txinNew.prevout == txin.prevout && txin.nSequence == txinNew.nSequence) {
txin.scriptSig = txinNew.scriptSig;
txin.prevPubKey = txinNew.prevPubKey;
LogPrint("privatesend", "CPrivateSendServer::AddScriptSig -- adding to finalMutableTransaction, scriptSig=%s\n", ScriptToAsmStr(txinNew.scriptSig).substr(0,24));
}
}
@ -683,14 +670,14 @@ bool CPrivateSendServer::IsSignaturesComplete()
return true;
}
bool CPrivateSendServer::IsOutputsCompatibleWithSessionDenom(const std::vector<CTxDSOut>& vecTxDSOut)
bool CPrivateSendServer::IsOutputsCompatibleWithSessionDenom(const std::vector<CTxOut>& vecTxOut)
{
if(CPrivateSend::GetDenominations(vecTxDSOut) == 0) return false;
if(CPrivateSend::GetDenominations(vecTxOut) == 0) return false;
BOOST_FOREACH(const CDarkSendEntry entry, vecEntries) {
LogPrintf("CPrivateSendServer::IsOutputsCompatibleWithSessionDenom -- vecTxDSOut denom %d, entry.vecTxDSOut denom %d\n",
CPrivateSend::GetDenominations(vecTxDSOut), CPrivateSend::GetDenominations(entry.vecTxDSOut));
if(CPrivateSend::GetDenominations(vecTxDSOut) != CPrivateSend::GetDenominations(entry.vecTxDSOut)) return false;
LogPrintf("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;

View File

@ -18,8 +18,6 @@ extern CPrivateSendServer privateSendServer;
class CPrivateSendServer : public CPrivateSendBase
{
private:
mutable CCriticalSection cs_darksend;
// Mixing uses collateral transactions to trust parties entering the pool
// to behave honestly. If they don't it takes their money.
std::vector<CTransaction> vecSessionCollaterals;
@ -54,7 +52,7 @@ private:
/// 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<CTxDSOut>& vecTxDSOut);
bool IsOutputsCompatibleWithSessionDenom(const std::vector<CTxOut>& vecTxOut);
// Set the 'state' value, with some logging and capturing when the state changed
void SetState(PoolState nStateNew);

View File

@ -19,15 +19,6 @@
#include <boost/lexical_cast.hpp>
CDarkSendEntry::CDarkSendEntry(const std::vector<CTxIn>& vecTxIn, const std::vector<CTxOut>& vecTxOut, const CTransaction& txCollateral) :
txCollateral(txCollateral), addr(CService())
{
BOOST_FOREACH(CTxIn txin, vecTxIn)
vecTxDSIn.push_back(txin);
BOOST_FOREACH(CTxOut txout, vecTxOut)
vecTxDSOut.push_back(txout);
}
bool CDarkSendEntry::AddScriptSig(const CTxIn& txin)
{
BOOST_FOREACH(CTxDSIn& txdsin, vecTxDSIn) {
@ -35,7 +26,6 @@ bool CDarkSendEntry::AddScriptSig(const CTxIn& txin)
if(txdsin.fHasSig) return false;
txdsin.scriptSig = txin.scriptSig;
txdsin.prevPubKey = txin.prevPubKey;
txdsin.fHasSig = true;
return true;
@ -128,6 +118,21 @@ void CPrivateSendBase::SetNull()
nTimeLastSuccessfulStep = GetTimeMillis();
}
void CPrivateSendBase::CheckQueue()
{
TRY_LOCK(cs_darksend, lockDS);
if(!lockDS) return; // it's ok to fail here, we run this quite frequently
// check mixing queue objects for timeouts
std::vector<CDarksendQueue>::iterator it = vecDarksendQueue.begin();
while(it != vecDarksendQueue.end()) {
if((*it).IsExpired()) {
LogPrint("privatesend", "CPrivateSendBase::%s -- Removing expired queue (%s)\n", __func__, (*it).ToString());
it = vecDarksendQueue.erase(it);
} else ++it;
}
}
std::string CPrivateSendBase::GetStateString() const
{
switch(nState) {
@ -217,6 +222,14 @@ bool CPrivateSend::IsCollateralValid(const CTransaction& txCollateral)
return true;
}
bool CPrivateSend::IsCollateralAmount(CAmount nInputAmount)
{
// collateral inputs should always be a 2x..4x of mixing collateral
return nInputAmount > GetCollateralAmount() &&
nInputAmount <= GetMaxCollateralAmount() &&
nInputAmount % GetCollateralAmount() == 0;
}
/* Create a nice string to show the denominations
Function returns as follows (for 4 denominations):
( bit on if present )
@ -249,16 +262,6 @@ std::string CPrivateSend::GetDenominationsToString(int nDenom)
return strDenom;
}
int CPrivateSend::GetDenominations(const std::vector<CTxDSOut>& vecTxDSOut)
{
std::vector<CTxOut> vecTxOut;
BOOST_FOREACH(CTxDSOut out, vecTxDSOut)
vecTxOut.push_back(out);
return GetDenominations(vecTxOut);
}
/* Return a bitshifted integer representing the denominations in this list
Function returns as follows (for 4 denominations):
( bit on if present )
@ -336,6 +339,14 @@ int CPrivateSend::GetDenominationsByAmounts(const std::vector<CAmount>& vecAmoun
return GetDenominations(vecTxOut, true);
}
bool CPrivateSend::IsDenominatedAmount(CAmount nInputAmount)
{
for (const auto& nDenomValue : vecStandardDenominations)
if(nInputAmount == nDenomValue)
return true;
return false;
}
std::string CPrivateSend::GetMessageByID(PoolMessage nMessageID)
{
switch (nMessageID) {

View File

@ -77,58 +77,49 @@ enum PoolStatusUpdate {
class CTxDSIn : public CTxIn
{
public:
// memory only
CScript prevPubKey;
bool fHasSig; // flag to indicate if signed
int nSentTimes; //times we've sent this anonymously
CTxDSIn(const CTxIn& txin) :
CTxDSIn(const CTxIn& txin, const CScript& script) :
CTxIn(txin),
prevPubKey(script),
fHasSig(false),
nSentTimes(0)
{}
CTxDSIn() :
CTxIn(),
prevPubKey(),
fHasSig(false),
nSentTimes(0)
{}
};
/** Holds an mixing output
*/
class CTxDSOut : public CTxOut
{
public:
int nSentTimes; //times we've sent this anonymously
CTxDSOut(const CTxOut& out) :
CTxOut(out),
nSentTimes(0)
{}
CTxDSOut() :
CTxOut(),
nSentTimes(0)
{}
};
// A clients transaction in the mixing pool
class CDarkSendEntry
{
public:
std::vector<CTxDSIn> vecTxDSIn;
std::vector<CTxDSOut> vecTxDSOut;
std::vector<CTxOut> vecTxOut;
CTransaction txCollateral;
// memory only
CService addr;
CDarkSendEntry() :
vecTxDSIn(std::vector<CTxDSIn>()),
vecTxDSOut(std::vector<CTxDSOut>()),
vecTxOut(std::vector<CTxOut>()),
txCollateral(CTransaction()),
addr(CService())
{}
CDarkSendEntry(const std::vector<CTxIn>& vecTxIn, const std::vector<CTxOut>& vecTxOut, const CTransaction& txCollateral);
CDarkSendEntry(const std::vector<CTxDSIn>& vecTxDSIn, const std::vector<CTxOut>& vecTxOut, const CTransaction& txCollateral) :
vecTxDSIn(vecTxDSIn),
vecTxOut(vecTxOut),
txCollateral(txCollateral),
addr(CService())
{}
ADD_SERIALIZE_METHODS;
@ -136,7 +127,7 @@ public:
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(vecTxDSIn);
READWRITE(txCollateral);
READWRITE(vecTxDSOut);
READWRITE(vecTxOut);
}
bool AddScriptSig(const CTxIn& txin);
@ -279,6 +270,8 @@ public:
class CPrivateSendBase
{
protected:
mutable CCriticalSection cs_darksend;
// The current mixing sessions in progress on the network
std::vector<CDarksendQueue> vecDarksendQueue;
@ -292,6 +285,7 @@ protected:
CMutableTransaction finalMutableTransaction; // the finalized transaction ready for signing
void SetNull();
void CheckQueue();
public:
int nSessionDenom; //Users must submit an denom matching this
@ -331,9 +325,10 @@ public:
/// Get the denominations for a specific amount of dash.
static int GetDenominationsByAmounts(const std::vector<CAmount>& vecAmount);
static bool IsDenominatedAmount(CAmount nInputAmount);
/// Get the denominations for a list of outputs (returns a bitshifted integer)
static int GetDenominations(const std::vector<CTxOut>& vecTxOut, bool fSingleRandomDenom = false);
static int GetDenominations(const std::vector<CTxDSOut>& vecTxDSOut);
static std::string GetDenominationsToString(int nDenom);
static bool GetDenominationsBits(int nDenom, std::vector<int> &vecBitsRet);
@ -349,6 +344,8 @@ public:
static CAmount GetCollateralAmount() { return COLLATERAL; }
static CAmount GetMaxCollateralAmount() { return COLLATERAL*4; }
static bool IsCollateralAmount(CAmount nInputAmount);
static void AddDSTX(const CDarksendBroadcastTx& dstx);
static CDarksendBroadcastTx GetDSTX(const uint256& hash);
static void CheckDSTXes(int nHeight);

View File

@ -105,7 +105,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
int nToMe = 0;
BOOST_FOREACH(const CTxOut& txout, wtx.vout) {
if(wallet->IsMine(txout)) {
fAllToMeDenom = fAllToMeDenom && wallet->IsDenominatedAmount(txout.nValue);
fAllToMeDenom = fAllToMeDenom && CPrivateSend::IsDenominatedAmount(txout.nValue);
nToMe++;
}
isminetype mine = wallet->IsMine(txout);
@ -150,8 +150,8 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
const CTxOut& txout = wtx.vout[nOut];
sub.idx = parts.size();
if(wallet->IsCollateralAmount(txout.nValue)) sub.type = TransactionRecord::PrivateSendMakeCollaterals;
if(wallet->IsDenominatedAmount(txout.nValue)) sub.type = TransactionRecord::PrivateSendCreateDenominations;
if(CPrivateSend::IsCollateralAmount(txout.nValue)) sub.type = TransactionRecord::PrivateSendMakeCollaterals;
if(CPrivateSend::IsDenominatedAmount(txout.nValue)) sub.type = TransactionRecord::PrivateSendCreateDenominations;
if(nDebit - wtx.GetValueOut() == CPrivateSend::GetCollateralAmount()) sub.type = TransactionRecord::PrivateSendCollateralPayment;
}
}

View File

@ -75,18 +75,19 @@ UniValue getpoolinfo(const UniValue& params, bool fHelp)
"Returns an object containing mixing pool related information.\n");
#ifdef ENABLE_WALLET
CPrivateSendBase privateSend = fMasterNode ? (CPrivateSendBase)privateSendServer : (CPrivateSendBase)privateSendClient;
CPrivateSendBase* pprivateSendBase = fMasterNode ? (CPrivateSendBase*)&privateSendServer : (CPrivateSendBase*)&privateSendClient;
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("state", privateSend.GetStateString()));
obj.push_back(Pair("state", pprivateSendBase->GetStateString()));
obj.push_back(Pair("mixing_mode", (!fMasterNode && privateSendClient.fPrivateSendMultiSession) ? "multi-session" : "normal"));
obj.push_back(Pair("queue", privateSend.GetQueueSize()));
obj.push_back(Pair("entries", privateSend.GetEntriesCount()));
obj.push_back(Pair("queue", pprivateSendBase->GetQueueSize()));
obj.push_back(Pair("entries", pprivateSendBase->GetEntriesCount()));
obj.push_back(Pair("status", privateSendClient.GetStatus()));
if (privateSendClient.infoMixingMasternode.fInfoValid) {
obj.push_back(Pair("outpoint", privateSendClient.infoMixingMasternode.vin.prevout.ToStringShort()));
obj.push_back(Pair("addr", privateSendClient.infoMixingMasternode.addr.ToString()));
masternode_info_t mnInfo;
if (privateSendClient.GetMixingMasternodeInfo(mnInfo)) {
obj.push_back(Pair("outpoint", mnInfo.vin.prevout.ToStringShort()));
obj.push_back(Pair("addr", mnInfo.addr.ToString()));
}
if (pwalletMain) {

View File

@ -1269,14 +1269,14 @@ int CWallet::GetRealOutpointPrivateSendRounds(const COutPoint& outpoint, int nRo
return -4;
}
if (IsCollateralAmount(wtx->vout[nout].nValue)) {
if (CPrivateSend::IsCollateralAmount(wtx->vout[nout].nValue)) {
mDenomWtxes[hash].vout[nout].nRounds = -3;
LogPrint("privatesend", "GetRealOutpointPrivateSendRounds UPDATED %s %3d %3d\n", hash.ToString(), nout, mDenomWtxes[hash].vout[nout].nRounds);
return mDenomWtxes[hash].vout[nout].nRounds;
}
//make sure the final output is non-denominate
if (!IsDenominatedAmount(wtx->vout[nout].nValue)) { //NOT DENOM
if (!CPrivateSend::IsDenominatedAmount(wtx->vout[nout].nValue)) { //NOT DENOM
mDenomWtxes[hash].vout[nout].nRounds = -2;
LogPrint("privatesend", "GetRealOutpointPrivateSendRounds UPDATED %s %3d %3d\n", hash.ToString(), nout, mDenomWtxes[hash].vout[nout].nRounds);
return mDenomWtxes[hash].vout[nout].nRounds;
@ -1284,7 +1284,7 @@ int CWallet::GetRealOutpointPrivateSendRounds(const COutPoint& outpoint, int nRo
bool fAllDenoms = true;
BOOST_FOREACH(CTxOut out, wtx->vout) {
fAllDenoms = fAllDenoms && IsDenominatedAmount(out.nValue);
fAllDenoms = fAllDenoms && CPrivateSend::IsDenominatedAmount(out.nValue);
}
// this one is denominated but there is another non-denominated output found in the same tx
@ -1333,21 +1333,13 @@ bool CWallet::IsDenominated(const COutPoint& outpoint) const
if (mi != mapWallet.end()) {
const CWalletTx& prev = (*mi).second;
if (outpoint.n < prev.vout.size()) {
return IsDenominatedAmount(prev.vout[outpoint.n].nValue);
return CPrivateSend::IsDenominatedAmount(prev.vout[outpoint.n].nValue);
}
}
return false;
}
bool CWallet::IsDenominatedAmount(CAmount nInputAmount) const
{
BOOST_FOREACH(CAmount d, CPrivateSend::GetStandardDenominations())
if(nInputAmount == d)
return true;
return false;
}
isminetype CWallet::IsMine(const CTxOut& txout) const
{
return ::IsMine(*this, txout.scriptPubKey);
@ -2003,7 +1995,7 @@ CAmount CWalletTx::GetDenominatedCredit(bool unconfirmed, bool fUseCache) const
{
const CTxOut &txout = vout[i];
if(pwallet->IsSpent(hashTx, i) || !pwallet->IsDenominatedAmount(vout[i].nValue)) continue;
if(pwallet->IsSpent(hashTx, i) || !CPrivateSend::IsDenominatedAmount(vout[i].nValue)) continue;
nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE);
if (!MoneyRange(nCredit))
@ -2164,7 +2156,7 @@ CAmount CWallet::GetAnonymizableBalance(bool fSkipDenominated, bool fSkipUnconfi
const CAmount nSmallestDenom = CPrivateSend::GetSmallestDenomination();
const CAmount nMixingCollateral = CPrivateSend::GetCollateralAmount();
BOOST_FOREACH(CompactTallyItem& item, vecTally) {
bool fIsDenominated = IsDenominatedAmount(item.nAmount);
bool fIsDenominated = CPrivateSend::IsDenominatedAmount(item.nAmount);
if(fSkipDenominated && fIsDenominated) continue;
// assume that the fee to create denoms should be mixing collateral at max
if(item.nAmount >= nSmallestDenom + (fIsDenominated ? 0 : nMixingCollateral))
@ -2390,14 +2382,14 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
for (unsigned int i = 0; i < pcoin->vout.size(); i++) {
bool found = false;
if(nCoinType == ONLY_DENOMINATED) {
found = IsDenominatedAmount(pcoin->vout[i].nValue);
found = CPrivateSend::IsDenominatedAmount(pcoin->vout[i].nValue);
} else if(nCoinType == ONLY_NONDENOMINATED) {
if (IsCollateralAmount(pcoin->vout[i].nValue)) continue; // do not use collateral amounts
found = !IsDenominatedAmount(pcoin->vout[i].nValue);
if (CPrivateSend::IsCollateralAmount(pcoin->vout[i].nValue)) continue; // do not use collateral amounts
found = !CPrivateSend::IsDenominatedAmount(pcoin->vout[i].nValue);
} else if(nCoinType == ONLY_1000) {
found = pcoin->vout[i].nValue == 1000*COIN;
} else if(nCoinType == ONLY_PRIVATESEND_COLLATERAL) {
found = IsCollateralAmount(pcoin->vout[i].nValue);
found = CPrivateSend::IsCollateralAmount(pcoin->vout[i].nValue);
} else {
found = true;
}
@ -2531,7 +2523,7 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int
int i = output.i;
CAmount n = pcoin->vout[i].nValue;
if (tryDenom == 0 && IsDenominatedAmount(n)) continue; // we don't want denom values on first run
if (tryDenom == 0 && CPrivateSend::IsDenominatedAmount(n)) continue; // we don't want denom values on first run
pair<CAmount,pair<const CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin, i));
@ -2756,9 +2748,9 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount &nFeeRet, int& nC
return true;
}
bool CWallet::SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount nValueMax, std::vector<CTxIn>& vecTxInRet, std::vector<COutput>& vCoinsRet, CAmount& nValueRet, int nPrivateSendRoundsMin, int nPrivateSendRoundsMax)
bool CWallet::SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount nValueMax, std::vector<CTxDSIn>& vecTxDSInRet, std::vector<COutput>& vCoinsRet, CAmount& nValueRet, int nPrivateSendRoundsMin, int nPrivateSendRoundsMax)
{
vecTxInRet.clear();
vecTxDSInRet.clear();
vCoinsRet.clear();
nValueRet = 0;
@ -2801,11 +2793,10 @@ bool CWallet::SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount
nValueMax -= insecureRand(nValueMax/5);
//on average use 50% of the inputs or less
int r = insecureRand(vCoins.size());
if((int)vecTxInRet.size() > r) return true;
if((int)vecTxDSInRet.size() > r) return true;
}
txin.prevPubKey = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey
nValueRet += out.tx->vout[out.i].nValue;
vecTxInRet.push_back(txin);
vecTxDSInRet.push_back(CTxDSIn(txin, out.tx->vout[out.i].scriptPubKey));
vCoinsRet.push_back(out);
nDenomResult |= 1 << nBit;
}
@ -2872,11 +2863,11 @@ bool CWallet::SelectCoinsGrouppedByAddresses(std::vector<CompactTallyItem>& vecT
if(IsSpent(outpoint.hash, i) || IsLockedCoin(outpoint.hash, i)) continue;
if(fSkipDenominated && IsDenominatedAmount(wtx.vout[i].nValue)) continue;
if(fSkipDenominated && CPrivateSend::IsDenominatedAmount(wtx.vout[i].nValue)) continue;
if(fAnonymizable) {
// ignore collaterals
if(IsCollateralAmount(wtx.vout[i].nValue)) continue;
if(CPrivateSend::IsCollateralAmount(wtx.vout[i].nValue)) continue;
if(fMasterNode && wtx.vout[i].nValue == 1000*COIN) continue;
// ignore outputs that are 10 times smaller then the smallest denomination
// otherwise they will just lead to higher fee / lower priority
@ -2942,7 +2933,7 @@ bool CWallet::SelectCoinsDark(CAmount nValueMin, CAmount nValueMax, std::vector<
//do not allow inputs less than 1/10th of minimum value
if(out.tx->vout[out.i].nValue < nValueMin/10) continue;
//do not allow collaterals to be selected
if(IsCollateralAmount(out.tx->vout[out.i].nValue)) continue;
if(CPrivateSend::IsCollateralAmount(out.tx->vout[out.i].nValue)) continue;
if(fMasterNode && out.tx->vout[out.i].nValue == 1000*COIN) continue; //masternode input
if(nValueRet + out.tx->vout[out.i].nValue <= nValueMax){
@ -2952,7 +2943,6 @@ bool CWallet::SelectCoinsDark(CAmount nValueMin, CAmount nValueMax, std::vector<
if(nRounds >= nPrivateSendRoundsMax) continue;
if(nRounds < nPrivateSendRoundsMin) continue;
txin.prevPubKey = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey
nValueRet += out.tx->vout[out.i].nValue;
vecTxInRet.push_back(txin);
}
@ -2961,7 +2951,7 @@ bool CWallet::SelectCoinsDark(CAmount nValueMin, CAmount nValueMax, std::vector<
return nValueRet >= nValueMin;
}
bool CWallet::GetCollateralTxIn(CTxIn& txinRet, CAmount& nValueRet) const
bool CWallet::GetCollateralTxDSIn(CTxDSIn& txdsinRet, CAmount& nValueRet) const
{
vector<COutput> vCoins;
@ -2969,10 +2959,9 @@ bool CWallet::GetCollateralTxIn(CTxIn& txinRet, CAmount& nValueRet) const
BOOST_FOREACH(const COutput& out, vCoins)
{
if(IsCollateralAmount(out.tx->vout[out.i].nValue))
if(CPrivateSend::IsCollateralAmount(out.tx->vout[out.i].nValue))
{
txinRet = CTxIn(out.tx->GetHash(), out.i);
txinRet.prevPubKey = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey
txdsinRet = CTxDSIn(CTxIn(out.tx->GetHash(), out.i), out.tx->vout[out.i].scriptPubKey);
nValueRet = out.tx->vout[out.i].nValue;
return true;
}
@ -3054,7 +3043,7 @@ int CWallet::CountInputsWithAmount(CAmount nInputAmount)
COutPoint outpoint = COutPoint(out.tx->GetHash(), out.i);
if(out.tx->vout[out.i].nValue != nInputAmount) continue;
if(!IsDenominatedAmount(pcoin->vout[i].nValue)) continue;
if(!CPrivateSend::IsDenominatedAmount(pcoin->vout[i].nValue)) continue;
if(IsSpent(out.tx->GetHash(), i) || IsMine(pcoin->vout[i]) != ISMINE_SPENDABLE || !IsDenominated(outpoint)) continue;
nTotal++;
@ -3074,14 +3063,6 @@ bool CWallet::HasCollateralInputs(bool fOnlyConfirmed) const
return !vCoins.empty();
}
bool CWallet::IsCollateralAmount(CAmount nInputAmount) const
{
// collateral inputs should always be a 2x..4x of mixing collateral
return nInputAmount > CPrivateSend::GetCollateralAmount() &&
nInputAmount <= CPrivateSend::GetMaxCollateralAmount() &&
nInputAmount % CPrivateSend::GetCollateralAmount() == 0;
}
bool CWallet::CreateCollateralTransaction(CMutableTransaction& txCollateral, std::string& strReason)
{
txCollateral.vin.clear();
@ -3089,9 +3070,9 @@ bool CWallet::CreateCollateralTransaction(CMutableTransaction& txCollateral, std
CReserveKey reservekey(this);
CAmount nValue = 0;
CTxIn txinCollateral;
CTxDSIn txdsinCollateral;
if (!GetCollateralTxIn(txinCollateral, nValue)) {
if (!GetCollateralTxDSIn(txdsinCollateral, nValue)) {
strReason = "PrivateSend requires a collateral transaction and could not locate an acceptable input!";
return false;
}
@ -3103,13 +3084,13 @@ bool CWallet::CreateCollateralTransaction(CMutableTransaction& txCollateral, std
scriptChange = GetScriptForDestination(vchPubKey.GetID());
reservekey.KeepKey();
txCollateral.vin.push_back(txinCollateral);
txCollateral.vin.push_back(txdsinCollateral);
//pay collateral charge in fees
CTxOut txout = CTxOut(nValue - CPrivateSend::GetCollateralAmount(), scriptChange);
txCollateral.vout.push_back(txout);
if(!SignSignature(*this, txinCollateral.prevPubKey, txCollateral, 0, int(SIGHASH_ALL|SIGHASH_ANYONECANPAY))) {
if(!SignSignature(*this, txdsinCollateral.prevPubKey, txCollateral, 0, int(SIGHASH_ALL|SIGHASH_ANYONECANPAY))) {
strReason = "Unable to sign collateral transaction!";
return false;
}
@ -3409,14 +3390,16 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
//
// Note how the sequence number is set to max()-1 so that the
// nLockTime set above actually works.
std::vector<CTxDSIn> vecTxDSInTmp;
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins){
CTxIn txin = CTxIn(coin.first->GetHash(),coin.second,CScript(),
std::numeric_limits<unsigned int>::max()-1);
txin.prevPubKey = coin.first->vout[coin.second].scriptPubKey;
vecTxDSInTmp.push_back(CTxDSIn(txin, coin.first->vout[coin.second].scriptPubKey));
txNew.vin.push_back(txin);
}
sort(txNew.vin.begin(), txNew.vin.end(), CompareInputBIP69());
sort(vecTxDSInTmp.begin(), vecTxDSInTmp.end(), CompareInputBIP69());
sort(txNew.vout.begin(), txNew.vout.end(), CompareOutputBIP69());
// If there was change output added before, we must update its position now
@ -3436,10 +3419,10 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
// Sign
int nIn = 0;
CTransaction txNewConst(txNew);
BOOST_FOREACH(const CTxIn& txin, txNew.vin)
for (const auto& txdsin : vecTxDSInTmp)
{
bool signSuccess;
const CScript& scriptPubKey = txin.prevPubKey;
const CScript& scriptPubKey = txdsin.prevPubKey;
CScript& scriptSigRes = txNew.vin[nIn].scriptSig;
if (sign)
signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, SIGHASH_ALL), scriptPubKey, scriptSigRes);

View File

@ -19,6 +19,8 @@
#include "wallet/wallet_ismine.h"
#include "wallet/walletdb.h"
#include "privatesend.h"
#include <algorithm>
#include <map>
#include <set>
@ -770,9 +772,11 @@ public:
*/
bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, bool fUseInstantSend = false) const;
bool SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount nValueMax, std::vector<CTxIn>& vecTxInRet, std::vector<COutput>& vCoinsRet, CAmount& nValueRet, int nPrivateSendRoundsMin, int nPrivateSendRoundsMax);
bool GetCollateralTxIn(CTxIn& txinRet, CAmount& nValueRet) const;
// Coin selection
bool SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount nValueMax, std::vector<CTxDSIn>& vecTxDSInRet, std::vector<COutput>& vCoinsRet, CAmount& nValueRet, int nPrivateSendRoundsMin, int nPrivateSendRoundsMax);
bool GetCollateralTxDSIn(CTxDSIn& txdsinRet, CAmount& nValueRet) const;
bool SelectCoinsDark(CAmount nValueMin, CAmount nValueMax, std::vector<CTxIn>& vecTxInRet, CAmount& nValueRet, int nPrivateSendRoundsMin, int nPrivateSendRoundsMax) const;
bool SelectCoinsGrouppedByAddresses(std::vector<CompactTallyItem>& vecTallyRet, bool fSkipDenominated = true, bool fAnonymizable = true, bool fSkipUnconfirmed = true) const;
/// Get 1000DASH output and keys which can be used for the Masternode
@ -781,7 +785,6 @@ public:
bool GetOutpointAndKeysFromOutput(const COutput& out, COutPoint& outpointRet, CPubKey& pubKeyRet, CKey& keyRet);
bool HasCollateralInputs(bool fOnlyConfirmed = true) const;
bool IsCollateralAmount(CAmount nInputAmount) const;
int CountInputsWithAmount(CAmount nInputAmount);
// get the PrivateSend chain depth for a given input
@ -790,7 +793,6 @@ public:
int GetOutpointPrivateSendRounds(const COutPoint& outpoint) const;
bool IsDenominated(const COutPoint& outpoint) const;
bool IsDenominatedAmount(CAmount nInputAmount) const;
bool IsSpent(const uint256& hash, unsigned int n) const;