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:
parent
c166ed39b0
commit
7e96af4e65
@ -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"
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
@ -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
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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. */
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user