f123248f1c
* update copyright * Update copyright in configure.ac and COPYING
427 lines
11 KiB
C++
427 lines
11 KiB
C++
// Copyright (c) 2014-2019 The Dash Core developers
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#ifndef PRIVATESEND_H
|
|
#define PRIVATESEND_H
|
|
|
|
#include "bls/bls.h"
|
|
#include "chain.h"
|
|
#include "chainparams.h"
|
|
#include "primitives/transaction.h"
|
|
#include "pubkey.h"
|
|
#include "sync.h"
|
|
#include "timedata.h"
|
|
#include "tinyformat.h"
|
|
|
|
class CPrivateSend;
|
|
class CConnman;
|
|
|
|
// timeouts
|
|
static const int PRIVATESEND_AUTO_TIMEOUT_MIN = 5;
|
|
static const int PRIVATESEND_AUTO_TIMEOUT_MAX = 15;
|
|
static const int PRIVATESEND_QUEUE_TIMEOUT = 30;
|
|
static const int PRIVATESEND_SIGNING_TIMEOUT = 15;
|
|
|
|
//! minimum peer version accepted by mixing pool
|
|
static const int MIN_PRIVATESEND_PEER_PROTO_VERSION = 70213;
|
|
|
|
static const size_t PRIVATESEND_ENTRY_MAX_SIZE = 9;
|
|
|
|
// pool responses
|
|
enum PoolMessage {
|
|
ERR_ALREADY_HAVE,
|
|
ERR_DENOM,
|
|
ERR_ENTRIES_FULL,
|
|
ERR_EXISTING_TX,
|
|
ERR_FEES,
|
|
ERR_INVALID_COLLATERAL,
|
|
ERR_INVALID_INPUT,
|
|
ERR_INVALID_SCRIPT,
|
|
ERR_INVALID_TX,
|
|
ERR_MAXIMUM,
|
|
ERR_MN_LIST,
|
|
ERR_MODE,
|
|
ERR_NON_STANDARD_PUBKEY,
|
|
ERR_NOT_A_MN, // not used
|
|
ERR_QUEUE_FULL,
|
|
ERR_RECENT,
|
|
ERR_SESSION,
|
|
ERR_MISSING_TX,
|
|
ERR_VERSION,
|
|
MSG_NOERR,
|
|
MSG_SUCCESS,
|
|
MSG_ENTRIES_ADDED,
|
|
MSG_POOL_MIN = ERR_ALREADY_HAVE,
|
|
MSG_POOL_MAX = MSG_ENTRIES_ADDED
|
|
};
|
|
|
|
// pool states
|
|
enum PoolState {
|
|
POOL_STATE_IDLE,
|
|
POOL_STATE_QUEUE,
|
|
POOL_STATE_ACCEPTING_ENTRIES,
|
|
POOL_STATE_SIGNING,
|
|
POOL_STATE_ERROR,
|
|
POOL_STATE_SUCCESS,
|
|
POOL_STATE_MIN = POOL_STATE_IDLE,
|
|
POOL_STATE_MAX = POOL_STATE_SUCCESS
|
|
};
|
|
|
|
// status update message constants
|
|
enum PoolStatusUpdate {
|
|
STATUS_REJECTED,
|
|
STATUS_ACCEPTED
|
|
};
|
|
|
|
/** Holds an mixing input
|
|
*/
|
|
class CTxDSIn : public CTxIn
|
|
{
|
|
public:
|
|
// memory only
|
|
CScript prevPubKey;
|
|
bool fHasSig; // flag to indicate if signed
|
|
|
|
CTxDSIn(const CTxIn& txin, const CScript& script) :
|
|
CTxIn(txin),
|
|
prevPubKey(script),
|
|
fHasSig(false)
|
|
{
|
|
}
|
|
|
|
CTxDSIn() :
|
|
CTxIn(),
|
|
prevPubKey(),
|
|
fHasSig(false)
|
|
{
|
|
}
|
|
};
|
|
|
|
class CPrivateSendAccept
|
|
{
|
|
public:
|
|
int nDenom;
|
|
CMutableTransaction txCollateral;
|
|
|
|
CPrivateSendAccept() :
|
|
nDenom(0),
|
|
txCollateral(CMutableTransaction()){};
|
|
|
|
CPrivateSendAccept(int nDenom, const CMutableTransaction& txCollateral) :
|
|
nDenom(nDenom),
|
|
txCollateral(txCollateral){};
|
|
|
|
ADD_SERIALIZE_METHODS;
|
|
|
|
template <typename Stream, typename Operation>
|
|
inline void SerializationOp(Stream& s, Operation ser_action)
|
|
{
|
|
READWRITE(nDenom);
|
|
READWRITE(txCollateral);
|
|
}
|
|
|
|
friend bool operator==(const CPrivateSendAccept& a, const CPrivateSendAccept& b)
|
|
{
|
|
return a.nDenom == b.nDenom && a.txCollateral == b.txCollateral;
|
|
}
|
|
};
|
|
|
|
// A clients transaction in the mixing pool
|
|
class CPrivateSendEntry
|
|
{
|
|
public:
|
|
std::vector<CTxDSIn> vecTxDSIn;
|
|
std::vector<CTxOut> vecTxOut;
|
|
CTransactionRef txCollateral;
|
|
// memory only
|
|
CService addr;
|
|
|
|
CPrivateSendEntry() :
|
|
vecTxDSIn(std::vector<CTxDSIn>()),
|
|
vecTxOut(std::vector<CTxOut>()),
|
|
txCollateral(MakeTransactionRef()),
|
|
addr(CService())
|
|
{
|
|
}
|
|
|
|
CPrivateSendEntry(const std::vector<CTxDSIn>& vecTxDSIn, const std::vector<CTxOut>& vecTxOut, const CTransaction& txCollateral) :
|
|
vecTxDSIn(vecTxDSIn),
|
|
vecTxOut(vecTxOut),
|
|
txCollateral(MakeTransactionRef(txCollateral)),
|
|
addr(CService())
|
|
{
|
|
}
|
|
|
|
ADD_SERIALIZE_METHODS;
|
|
|
|
template <typename Stream, typename Operation>
|
|
inline void SerializationOp(Stream& s, Operation ser_action)
|
|
{
|
|
READWRITE(vecTxDSIn);
|
|
READWRITE(txCollateral);
|
|
READWRITE(vecTxOut);
|
|
}
|
|
|
|
bool AddScriptSig(const CTxIn& txin);
|
|
};
|
|
|
|
|
|
/**
|
|
* A currently in progress mixing merge and denomination information
|
|
*/
|
|
class CPrivateSendQueue
|
|
{
|
|
public:
|
|
int nDenom;
|
|
COutPoint masternodeOutpoint;
|
|
int64_t nTime;
|
|
bool fReady; //ready for submit
|
|
std::vector<unsigned char> vchSig;
|
|
// memory only
|
|
bool fTried;
|
|
|
|
CPrivateSendQueue() :
|
|
nDenom(0),
|
|
masternodeOutpoint(COutPoint()),
|
|
nTime(0),
|
|
fReady(false),
|
|
vchSig(std::vector<unsigned char>()),
|
|
fTried(false)
|
|
{
|
|
}
|
|
|
|
CPrivateSendQueue(int nDenom, COutPoint outpoint, int64_t nTime, bool fReady) :
|
|
nDenom(nDenom),
|
|
masternodeOutpoint(outpoint),
|
|
nTime(nTime),
|
|
fReady(fReady),
|
|
vchSig(std::vector<unsigned char>()),
|
|
fTried(false)
|
|
{
|
|
}
|
|
|
|
ADD_SERIALIZE_METHODS;
|
|
|
|
template <typename Stream, typename Operation>
|
|
inline void SerializationOp(Stream& s, Operation ser_action)
|
|
{
|
|
READWRITE(nDenom);
|
|
READWRITE(masternodeOutpoint);
|
|
READWRITE(nTime);
|
|
READWRITE(fReady);
|
|
if (!(s.GetType() & SER_GETHASH)) {
|
|
READWRITE(vchSig);
|
|
}
|
|
}
|
|
|
|
uint256 GetSignatureHash() const;
|
|
/** Sign this mixing transaction
|
|
* \return true if all conditions are met:
|
|
* 1) we have an active Masternode,
|
|
* 2) we have a valid Masternode private key,
|
|
* 3) we signed the message successfully, and
|
|
* 4) we verified the message successfully
|
|
*/
|
|
bool Sign();
|
|
/// Check if we have a valid Masternode address
|
|
bool CheckSignature(const CBLSPublicKey& blsPubKey) const;
|
|
|
|
bool Relay(CConnman& connman);
|
|
|
|
/// Is this queue expired?
|
|
bool IsExpired() { return GetAdjustedTime() - nTime > PRIVATESEND_QUEUE_TIMEOUT; }
|
|
|
|
std::string ToString() const
|
|
{
|
|
return strprintf("nDenom=%d, nTime=%lld, fReady=%s, fTried=%s, masternode=%s",
|
|
nDenom, nTime, fReady ? "true" : "false", fTried ? "true" : "false", masternodeOutpoint.ToStringShort());
|
|
}
|
|
|
|
friend bool operator==(const CPrivateSendQueue& a, const CPrivateSendQueue& b)
|
|
{
|
|
return a.nDenom == b.nDenom && a.masternodeOutpoint == b.masternodeOutpoint && a.nTime == b.nTime && a.fReady == b.fReady;
|
|
}
|
|
};
|
|
|
|
/** Helper class to store mixing transaction (tx) information.
|
|
*/
|
|
class CPrivateSendBroadcastTx
|
|
{
|
|
private:
|
|
// memory only
|
|
// when corresponding tx is 0-confirmed or conflicted, nConfirmedHeight is -1
|
|
int nConfirmedHeight;
|
|
|
|
public:
|
|
CTransactionRef tx;
|
|
COutPoint masternodeOutpoint;
|
|
std::vector<unsigned char> vchSig;
|
|
int64_t sigTime;
|
|
|
|
CPrivateSendBroadcastTx() :
|
|
nConfirmedHeight(-1),
|
|
tx(MakeTransactionRef()),
|
|
masternodeOutpoint(),
|
|
vchSig(),
|
|
sigTime(0)
|
|
{
|
|
}
|
|
|
|
CPrivateSendBroadcastTx(const CTransactionRef& _tx, COutPoint _outpoint, int64_t _sigTime) :
|
|
nConfirmedHeight(-1),
|
|
tx(_tx),
|
|
masternodeOutpoint(_outpoint),
|
|
vchSig(),
|
|
sigTime(_sigTime)
|
|
{
|
|
}
|
|
|
|
ADD_SERIALIZE_METHODS;
|
|
|
|
template <typename Stream, typename Operation>
|
|
inline void SerializationOp(Stream& s, Operation ser_action)
|
|
{
|
|
READWRITE(tx);
|
|
READWRITE(masternodeOutpoint);
|
|
if (!(s.GetType() & SER_GETHASH)) {
|
|
READWRITE(vchSig);
|
|
}
|
|
READWRITE(sigTime);
|
|
}
|
|
|
|
friend bool operator==(const CPrivateSendBroadcastTx& a, const CPrivateSendBroadcastTx& b)
|
|
{
|
|
return *a.tx == *b.tx;
|
|
}
|
|
friend bool operator!=(const CPrivateSendBroadcastTx& a, const CPrivateSendBroadcastTx& b)
|
|
{
|
|
return !(a == b);
|
|
}
|
|
explicit operator bool() const
|
|
{
|
|
return *this != CPrivateSendBroadcastTx();
|
|
}
|
|
|
|
uint256 GetSignatureHash() const;
|
|
|
|
bool Sign();
|
|
bool CheckSignature(const CBLSPublicKey& blsPubKey) const;
|
|
|
|
void SetConfirmedHeight(int nConfirmedHeightIn) { nConfirmedHeight = nConfirmedHeightIn; }
|
|
bool IsExpired(int nHeight);
|
|
};
|
|
|
|
// base class
|
|
class CPrivateSendBaseSession
|
|
{
|
|
protected:
|
|
mutable CCriticalSection cs_privatesend;
|
|
|
|
std::vector<CPrivateSendEntry> vecEntries; // Masternode/clients entries
|
|
|
|
PoolState nState; // should be one of the POOL_STATE_XXX values
|
|
int64_t nTimeLastSuccessfulStep; // the time when last successful mixing step was performed
|
|
|
|
int nSessionID; // 0 if no mixing session is active
|
|
|
|
CMutableTransaction finalMutableTransaction; // the finalized transaction ready for signing
|
|
|
|
void SetNull();
|
|
|
|
public:
|
|
int nSessionDenom; //Users must submit a denom matching this
|
|
|
|
CPrivateSendBaseSession() :
|
|
vecEntries(),
|
|
nState(POOL_STATE_IDLE),
|
|
nTimeLastSuccessfulStep(0),
|
|
nSessionID(0),
|
|
finalMutableTransaction(),
|
|
nSessionDenom(0)
|
|
{
|
|
}
|
|
|
|
int GetState() const { return nState; }
|
|
std::string GetStateString() const;
|
|
|
|
int GetEntriesCount() const { return vecEntries.size(); }
|
|
};
|
|
|
|
// base class
|
|
class CPrivateSendBaseManager
|
|
{
|
|
protected:
|
|
mutable CCriticalSection cs_vecqueue;
|
|
|
|
// The current mixing sessions in progress on the network
|
|
std::vector<CPrivateSendQueue> vecPrivateSendQueue;
|
|
|
|
void SetNull();
|
|
void CheckQueue();
|
|
|
|
public:
|
|
CPrivateSendBaseManager() :
|
|
vecPrivateSendQueue() {}
|
|
|
|
int GetQueueSize() const { return vecPrivateSendQueue.size(); }
|
|
bool GetQueueItemAndTry(CPrivateSendQueue& dsqRet);
|
|
};
|
|
|
|
// helper class
|
|
class CPrivateSend
|
|
{
|
|
private:
|
|
// make constructor, destructor and copying not available
|
|
CPrivateSend() {}
|
|
~CPrivateSend() {}
|
|
CPrivateSend(CPrivateSend const&) = delete;
|
|
CPrivateSend& operator=(CPrivateSend const&) = delete;
|
|
|
|
// static members
|
|
static std::vector<CAmount> vecStandardDenominations;
|
|
static std::map<uint256, CPrivateSendBroadcastTx> mapDSTX;
|
|
|
|
static CCriticalSection cs_mapdstx;
|
|
|
|
static void CheckDSTXes(int nHeight);
|
|
|
|
public:
|
|
static void InitStandardDenominations();
|
|
static std::vector<CAmount> GetStandardDenominations() { return vecStandardDenominations; }
|
|
static CAmount GetSmallestDenomination() { return vecStandardDenominations.back(); }
|
|
|
|
/// 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 std::string GetDenominationsToString(int nDenom);
|
|
static bool GetDenominationsBits(int nDenom, std::vector<int>& vecBitsRet);
|
|
|
|
static std::string GetMessageByID(PoolMessage nMessageID);
|
|
|
|
/// Get the minimum/maximum number of participants for the pool
|
|
static int GetMinPoolParticipants() { return Params().PoolMinParticipants(); }
|
|
static int GetMaxPoolParticipants() { return Params().PoolMaxParticipants(); }
|
|
|
|
static CAmount GetMaxPoolAmount() { return vecStandardDenominations.empty() ? 0 : PRIVATESEND_ENTRY_MAX_SIZE * vecStandardDenominations.front(); }
|
|
|
|
/// If the collateral is valid given by a client
|
|
static bool IsCollateralValid(const CTransaction& txCollateral);
|
|
static CAmount GetCollateralAmount() { return GetSmallestDenomination() / 10; }
|
|
static CAmount GetMaxCollateralAmount() { return GetCollateralAmount() * 4; }
|
|
|
|
static bool IsCollateralAmount(CAmount nInputAmount);
|
|
|
|
static void AddDSTX(const CPrivateSendBroadcastTx& dstx);
|
|
static CPrivateSendBroadcastTx GetDSTX(const uint256& hash);
|
|
|
|
static void UpdatedBlockTip(const CBlockIndex* pindex);
|
|
static void SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int posInBlock);
|
|
};
|
|
|
|
#endif
|