377 lines
10 KiB
C++
377 lines
10 KiB
C++
// Copyright (c) 2014-2015 The Dash developers
|
|
// Copyright (c) 2015-2019 The PIVX developers
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#ifndef OBFUSCATION_H
|
|
#define OBFUSCATION_H
|
|
|
|
#include "main.h"
|
|
#include "masternode-payments.h"
|
|
#include "masternode-sync.h"
|
|
#include "masternodeman.h"
|
|
#include "obfuscation-relay.h"
|
|
#include "sync.h"
|
|
|
|
class CTxIn;
|
|
class CObfuscationPool;
|
|
class CObfuScationSigner;
|
|
class CMasterNodeVote;
|
|
class CBitcoinAddress;
|
|
class CObfuscationQueue;
|
|
class CObfuscationBroadcastTx;
|
|
class CActiveMasternode;
|
|
|
|
// pool states for mixing
|
|
#define POOL_STATUS_UNKNOWN 0 // waiting for update
|
|
#define POOL_STATUS_IDLE 1 // waiting for update
|
|
#define POOL_STATUS_QUEUE 2 // waiting in a queue
|
|
#define POOL_STATUS_ACCEPTING_ENTRIES 3 // accepting entries
|
|
#define POOL_STATUS_FINALIZE_TRANSACTION 4 // master node will broadcast what it accepted
|
|
#define POOL_STATUS_SIGNING 5 // check inputs/outputs, sign final tx
|
|
#define POOL_STATUS_TRANSMISSION 6 // transmit transaction
|
|
#define POOL_STATUS_ERROR 7 // error
|
|
#define POOL_STATUS_SUCCESS 8 // success
|
|
|
|
// status update message constants
|
|
#define MASTERNODE_ACCEPTED 1
|
|
#define MASTERNODE_REJECTED 0
|
|
#define MASTERNODE_RESET -1
|
|
|
|
#define OBFUSCATION_QUEUE_TIMEOUT 30
|
|
#define OBFUSCATION_SIGNING_TIMEOUT 15
|
|
|
|
// used for anonymous relaying of inputs/outputs/sigs
|
|
#define OBFUSCATION_RELAY_IN 1
|
|
#define OBFUSCATION_RELAY_OUT 2
|
|
#define OBFUSCATION_RELAY_SIG 3
|
|
|
|
static const CAmount OBFUSCATION_COLLATERAL = (10 * COIN);
|
|
static const CAmount OBFUSCATION_POOL_MAX = (99999.99 * COIN);
|
|
|
|
extern CObfuscationPool obfuScationPool;
|
|
extern CObfuScationSigner obfuScationSigner;
|
|
extern std::vector<CObfuscationQueue> vecObfuscationQueue;
|
|
extern std::string strMasterNodePrivKey;
|
|
extern map<uint256, CObfuscationBroadcastTx> mapObfuscationBroadcastTxes;
|
|
extern CActiveMasternode activeMasternode;
|
|
|
|
/** Holds an Obfuscation input
|
|
*/
|
|
class CTxDSIn : public CTxIn
|
|
{
|
|
public:
|
|
bool fHasSig; // flag to indicate if signed
|
|
|
|
CTxDSIn(const CTxIn& in)
|
|
{
|
|
prevout = in.prevout;
|
|
scriptSig = in.scriptSig;
|
|
prevPubKey = in.prevPubKey;
|
|
nSequence = in.nSequence;
|
|
fHasSig = false;
|
|
}
|
|
};
|
|
|
|
/** Holds an Obfuscation output
|
|
*/
|
|
class CTxDSOut : public CTxOut
|
|
{
|
|
public:
|
|
int nSentTimes; //times we've sent this anonymously
|
|
|
|
CTxDSOut(const CTxOut& out)
|
|
{
|
|
nValue = out.nValue;
|
|
nRounds = out.nRounds;
|
|
scriptPubKey = out.scriptPubKey;
|
|
nSentTimes = 0;
|
|
}
|
|
};
|
|
|
|
// A clients transaction in the obfuscation pool
|
|
class CObfuScationEntry
|
|
{
|
|
public:
|
|
bool isSet;
|
|
std::vector<CTxDSIn> sev;
|
|
std::vector<CTxDSOut> vout;
|
|
CAmount amount;
|
|
CTransaction collateral;
|
|
int64_t addedTime; // time in UTC milliseconds
|
|
|
|
CObfuScationEntry()
|
|
{
|
|
isSet = false;
|
|
collateral = CTransaction();
|
|
amount = 0;
|
|
}
|
|
|
|
/// Add entries to use for Obfuscation
|
|
bool Add(const std::vector<CTxIn> vinIn, int64_t amountIn, const CTransaction collateralIn, const std::vector<CTxOut> voutIn)
|
|
{
|
|
if (isSet) {
|
|
return false;
|
|
}
|
|
|
|
for (const CTxIn& in : vinIn)
|
|
sev.push_back(in);
|
|
|
|
for (const CTxOut& out : voutIn)
|
|
vout.push_back(out);
|
|
|
|
amount = amountIn;
|
|
collateral = collateralIn;
|
|
isSet = true;
|
|
addedTime = GetTime();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool IsExpired()
|
|
{
|
|
return (GetTime() - addedTime) > OBFUSCATION_QUEUE_TIMEOUT; // 120 seconds
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* A currently inprogress Obfuscation merge and denomination information
|
|
*/
|
|
class CObfuscationQueue
|
|
{
|
|
public:
|
|
CTxIn vin;
|
|
int64_t time;
|
|
int nDenom;
|
|
bool ready; //ready for submit
|
|
std::vector<unsigned char> vchSig;
|
|
|
|
CObfuscationQueue()
|
|
{
|
|
nDenom = 0;
|
|
vin = CTxIn();
|
|
time = 0;
|
|
vchSig.clear();
|
|
ready = false;
|
|
}
|
|
|
|
ADD_SERIALIZE_METHODS;
|
|
|
|
template <typename Stream, typename Operation>
|
|
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
|
|
{
|
|
READWRITE(nDenom);
|
|
READWRITE(vin);
|
|
READWRITE(time);
|
|
READWRITE(ready);
|
|
READWRITE(vchSig);
|
|
}
|
|
|
|
/** Sign this Obfuscation 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();
|
|
|
|
bool Relay();
|
|
|
|
/// Is this Obfuscation expired?
|
|
bool IsExpired()
|
|
{
|
|
return (GetTime() - time) > OBFUSCATION_QUEUE_TIMEOUT; // 120 seconds
|
|
}
|
|
|
|
};
|
|
|
|
/** Helper class to store Obfuscation transaction (tx) information.
|
|
*/
|
|
class CObfuscationBroadcastTx
|
|
{
|
|
public:
|
|
CTransaction tx;
|
|
CTxIn vin;
|
|
vector<unsigned char> vchSig;
|
|
int64_t sigTime;
|
|
};
|
|
|
|
/** Helper object for signing and checking signatures
|
|
*/
|
|
class CObfuScationSigner
|
|
{
|
|
public:
|
|
/// Is the inputs associated with this public key? (and there is 10000 AGR - checking if valid masternode)
|
|
bool IsVinAssociatedWithPubkey(CTxIn& vin, CPubKey& pubkey);
|
|
/// Set the private/public key values, returns true if successful
|
|
bool GetKeysFromSecret(std::string strSecret, CKey& keyRet, CPubKey& pubkeyRet);
|
|
/// Set the private/public key values, returns true if successful
|
|
bool SetKey(std::string strSecret, std::string& errorMessage, CKey& key, CPubKey& pubkey);
|
|
/// Sign the message, returns true if successful
|
|
bool SignMessage(std::string strMessage, std::string& errorMessage, std::vector<unsigned char>& vchSig, CKey key);
|
|
/// Verify the message, returns true if succcessful
|
|
bool VerifyMessage(CPubKey pubkey, std::vector<unsigned char>& vchSig, std::string strMessage, std::string& errorMessage);
|
|
};
|
|
|
|
/** Used to keep track of current status of Obfuscation pool
|
|
*/
|
|
class CObfuscationPool
|
|
{
|
|
private:
|
|
|
|
std::vector<CObfuScationEntry> entries; // Masternode/clients entries
|
|
CMutableTransaction finalTransaction; // the finalized transaction ready for signing
|
|
|
|
int64_t lastTimeChanged; // last time the 'state' changed, in UTC milliseconds
|
|
|
|
unsigned int state; // should be one of the POOL_STATUS_XXX values
|
|
unsigned int entriesCount;
|
|
unsigned int lastEntryAccepted;
|
|
unsigned int countEntriesAccepted;
|
|
|
|
std::vector<CTxIn> lockedCoins;
|
|
|
|
std::string lastMessage;
|
|
|
|
int sessionID;
|
|
|
|
int sessionUsers; //N Users have said they'll join
|
|
std::vector<CTransaction> vecSessionCollateral;
|
|
|
|
int cachedLastSuccess;
|
|
|
|
int minBlockSpacing; //required blocks between mixes
|
|
|
|
int64_t lastNewBlock;
|
|
|
|
//debugging data
|
|
std::string strAutoDenomResult;
|
|
|
|
public:
|
|
enum messages {
|
|
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,
|
|
ERR_QUEUE_FULL,
|
|
ERR_RECENT,
|
|
ERR_SESSION,
|
|
ERR_MISSING_TX,
|
|
ERR_VERSION,
|
|
MSG_NOERR,
|
|
MSG_SUCCESS,
|
|
MSG_ENTRIES_ADDED
|
|
};
|
|
|
|
// where collateral should be made out to
|
|
CScript collateralPubKey;
|
|
|
|
CMasternode* pSubmittedToMasternode;
|
|
int sessionDenom; //Users must submit an denom matching this
|
|
|
|
CObfuscationPool()
|
|
{
|
|
/* Obfuscation uses collateral addresses to trust parties entering the pool
|
|
to behave themselves. If they don't it takes their money. */
|
|
|
|
cachedLastSuccess = 0;
|
|
minBlockSpacing = 0;
|
|
lastNewBlock = 0;
|
|
|
|
SetNull();
|
|
}
|
|
|
|
void InitCollateralAddress()
|
|
{
|
|
SetCollateralAddress(Params().ObfuscationPoolDummyAddress());
|
|
}
|
|
|
|
bool SetCollateralAddress(std::string strAddress);
|
|
void Reset();
|
|
void SetNull();
|
|
|
|
void UnlockCoins();
|
|
|
|
bool IsNull() const
|
|
{
|
|
return state == POOL_STATUS_ACCEPTING_ENTRIES && entries.empty();
|
|
}
|
|
|
|
int GetState() const
|
|
{
|
|
return state;
|
|
}
|
|
|
|
int GetEntriesCount() const
|
|
{
|
|
return entries.size();
|
|
}
|
|
|
|
/// Get the count of the accepted entries
|
|
int GetCountEntriesAccepted() const
|
|
{
|
|
return countEntriesAccepted;
|
|
}
|
|
|
|
// Set the 'state' value, with some logging and capturing when the state changed
|
|
void UpdateState(unsigned int newState)
|
|
{
|
|
if (fMasterNode && (newState == POOL_STATUS_ERROR || newState == POOL_STATUS_SUCCESS)) {
|
|
// LogPrint("obfuscation", "CObfuscationPool::UpdateState() - Can't set state to ERROR or SUCCESS as a Masternode. \n");
|
|
return;
|
|
}
|
|
|
|
// LogPrintf("CObfuscationPool::UpdateState() == %d | %d \n", state, newState);
|
|
if (state != newState) {
|
|
lastTimeChanged = GetTimeMillis();
|
|
if (fMasterNode) {
|
|
RelayStatus(obfuScationPool.sessionID, obfuScationPool.GetState(), obfuScationPool.GetEntriesCount(), MASTERNODE_RESET);
|
|
}
|
|
}
|
|
state = newState;
|
|
}
|
|
|
|
/// Get the maximum number of transactions for the pool
|
|
int GetMaxPoolTransactions()
|
|
{
|
|
return Params().PoolMaxTransactions();
|
|
}
|
|
|
|
/// Check for process in Obfuscation
|
|
void Check();
|
|
void CheckFinalTransaction();
|
|
/// Charge fees to bad actors (Charge clients a fee if they're abusive)
|
|
void ChargeFees();
|
|
/// Rarely charge fees to pay miners
|
|
void ChargeRandomFees();
|
|
void CheckTimeout();
|
|
void CheckForCompleteQueue();
|
|
/// Check that all inputs are signed. (Are all inputs signed?)
|
|
bool SignaturesComplete();
|
|
/// Process a new block
|
|
void NewBlock();
|
|
|
|
//
|
|
// Relay Obfuscation Messages
|
|
//
|
|
void RelayFinalTransaction(const int sessionID, const CTransaction& txNew);
|
|
void RelayStatus(const int sessionID, const int newState, const int newEntriesCount, const int newAccepted, const int errorID = MSG_NOERR);
|
|
void RelayCompletedTransaction(const int sessionID, const bool error, const int errorID);
|
|
};
|
|
|
|
void ThreadCheckObfuScationPool();
|
|
|
|
#endif
|