706 lines
23 KiB
C++
706 lines
23 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.
|
|
|
|
#include "obfuscation.h"
|
|
#include "coincontrol.h"
|
|
#include "init.h"
|
|
#include "main.h"
|
|
#include "masternodeman.h"
|
|
#include "script/sign.h"
|
|
#include "swifttx.h"
|
|
#include "guiinterface.h"
|
|
#include "util.h"
|
|
#include <boost/algorithm/string/replace.hpp>
|
|
#include <boost/filesystem.hpp>
|
|
#include <boost/filesystem/fstream.hpp>
|
|
#include <boost/foreach.hpp>
|
|
|
|
#include <algorithm>
|
|
#include <boost/assign/list_of.hpp>
|
|
#include <openssl/rand.h>
|
|
|
|
using namespace std;
|
|
using namespace boost;
|
|
|
|
// The main object for accessing Obfuscation
|
|
CObfuscationPool obfuScationPool;
|
|
// A helper object for signing messages from Masternodes
|
|
CObfuScationSigner obfuScationSigner;
|
|
// The current Obfuscations in progress on the network
|
|
std::vector<CObfuscationQueue> vecObfuscationQueue;
|
|
// Keep track of the used Masternodes
|
|
std::vector<CTxIn> vecMasternodesUsed;
|
|
// Keep track of the scanning errors I've seen
|
|
map<uint256, CObfuscationBroadcastTx> mapObfuscationBroadcastTxes;
|
|
// Keep track of the active Masternode
|
|
CActiveMasternode activeMasternode;
|
|
|
|
int randomizeList(int i) { return std::rand() % i; }
|
|
|
|
void CObfuscationPool::Reset()
|
|
{
|
|
cachedLastSuccess = 0;
|
|
lastNewBlock = 0;
|
|
vecMasternodesUsed.clear();
|
|
UnlockCoins();
|
|
SetNull();
|
|
}
|
|
|
|
void CObfuscationPool::SetNull()
|
|
{
|
|
// MN side
|
|
sessionUsers = 0;
|
|
vecSessionCollateral.clear();
|
|
|
|
// Client side
|
|
entriesCount = 0;
|
|
lastEntryAccepted = 0;
|
|
countEntriesAccepted = 0;
|
|
|
|
// Both sides
|
|
state = POOL_STATUS_IDLE;
|
|
sessionID = 0;
|
|
sessionDenom = 0;
|
|
entries.clear();
|
|
finalTransaction.vin.clear();
|
|
finalTransaction.vout.clear();
|
|
lastTimeChanged = GetTimeMillis();
|
|
|
|
// -- seed random number generator (used for ordering output lists)
|
|
unsigned int seed = 0;
|
|
RAND_bytes((unsigned char*)&seed, sizeof(seed));
|
|
std::srand(seed);
|
|
}
|
|
|
|
bool CObfuscationPool::SetCollateralAddress(std::string strAddress)
|
|
{
|
|
CBitcoinAddress address;
|
|
if (!address.SetString(strAddress)) {
|
|
LogPrintf("CObfuscationPool::SetCollateralAddress - Invalid Obfuscation collateral address\n");
|
|
return false;
|
|
}
|
|
collateralPubKey = GetScriptForDestination(address.Get());
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Unlock coins after Obfuscation fails or succeeds
|
|
//
|
|
void CObfuscationPool::UnlockCoins()
|
|
{
|
|
if (!pwalletMain)
|
|
return;
|
|
|
|
while (true) {
|
|
TRY_LOCK(pwalletMain->cs_wallet, lockWallet);
|
|
if (!lockWallet) {
|
|
MilliSleep(50);
|
|
continue;
|
|
}
|
|
for (CTxIn v : lockedCoins)
|
|
pwalletMain->UnlockCoin(v.prevout);
|
|
break;
|
|
}
|
|
|
|
lockedCoins.clear();
|
|
}
|
|
|
|
//
|
|
// Check the Obfuscation progress and send client updates if a Masternode
|
|
//
|
|
void CObfuscationPool::Check()
|
|
{
|
|
if (fMasterNode) LogPrint("obfuscation", "CObfuscationPool::Check() - entries count %lu\n", entries.size());
|
|
//printf("CObfuscationPool::Check() %d - %d - %d\n", state, anonTx.CountEntries(), GetTimeMillis()-lastTimeChanged);
|
|
|
|
if (fMasterNode) {
|
|
LogPrint("obfuscation", "CObfuscationPool::Check() - entries count %lu\n", entries.size());
|
|
|
|
// If entries is full, then move on to the next phase
|
|
if (state == POOL_STATUS_ACCEPTING_ENTRIES && (int)entries.size() >= GetMaxPoolTransactions()) {
|
|
LogPrint("obfuscation", "CObfuscationPool::Check() -- TRYING TRANSACTION \n");
|
|
UpdateState(POOL_STATUS_FINALIZE_TRANSACTION);
|
|
}
|
|
}
|
|
|
|
// create the finalized transaction for distribution to the clients
|
|
if (state == POOL_STATUS_FINALIZE_TRANSACTION) {
|
|
LogPrint("obfuscation", "CObfuscationPool::Check() -- FINALIZE TRANSACTIONS\n");
|
|
UpdateState(POOL_STATUS_SIGNING);
|
|
|
|
if (fMasterNode) {
|
|
CMutableTransaction txNew;
|
|
|
|
// make our new transaction
|
|
for (unsigned int i = 0; i < entries.size(); i++) {
|
|
for (const CTxOut& v : entries[i].vout)
|
|
txNew.vout.push_back(v);
|
|
|
|
for (const CTxDSIn& s : entries[i].sev)
|
|
txNew.vin.push_back(s);
|
|
}
|
|
|
|
// shuffle the outputs for improved anonymity
|
|
std::random_shuffle(txNew.vin.begin(), txNew.vin.end(), randomizeList);
|
|
std::random_shuffle(txNew.vout.begin(), txNew.vout.end(), randomizeList);
|
|
|
|
|
|
LogPrint("obfuscation", "Transaction 1: %s\n", txNew.ToString());
|
|
finalTransaction = txNew;
|
|
|
|
// request signatures from clients
|
|
RelayFinalTransaction(sessionID, finalTransaction);
|
|
}
|
|
}
|
|
|
|
// If we have all of the signatures, try to compile the transaction
|
|
if (fMasterNode && state == POOL_STATUS_SIGNING && SignaturesComplete()) {
|
|
LogPrint("obfuscation", "CObfuscationPool::Check() -- SIGNING\n");
|
|
UpdateState(POOL_STATUS_TRANSMISSION);
|
|
|
|
CheckFinalTransaction();
|
|
}
|
|
|
|
// reset if we're here for 10 seconds
|
|
if ((state == POOL_STATUS_ERROR || state == POOL_STATUS_SUCCESS) && GetTimeMillis() - lastTimeChanged >= 10000) {
|
|
LogPrint("obfuscation", "CObfuscationPool::Check() -- timeout, RESETTING\n");
|
|
UnlockCoins();
|
|
SetNull();
|
|
if (fMasterNode) RelayStatus(sessionID, GetState(), GetEntriesCount(), MASTERNODE_RESET);
|
|
}
|
|
}
|
|
|
|
void CObfuscationPool::CheckFinalTransaction()
|
|
{
|
|
if (!fMasterNode) return; // check and relay final tx only on masternode
|
|
|
|
CWalletTx txNew = CWalletTx(pwalletMain, finalTransaction);
|
|
|
|
LOCK2(cs_main, pwalletMain->cs_wallet);
|
|
{
|
|
LogPrint("obfuscation", "Transaction 2: %s\n", txNew.ToString());
|
|
|
|
// See if the transaction is valid
|
|
if (!txNew.AcceptToMemoryPool(false, true, true)) {
|
|
LogPrintf("CObfuscationPool::Check() - CommitTransaction : Error: Transaction not valid\n");
|
|
SetNull();
|
|
|
|
// not much we can do in this case
|
|
UpdateState(POOL_STATUS_ACCEPTING_ENTRIES);
|
|
RelayCompletedTransaction(sessionID, true, ERR_INVALID_TX);
|
|
return;
|
|
}
|
|
|
|
LogPrintf("CObfuscationPool::Check() -- IS MASTER -- TRANSMITTING OBFUSCATION\n");
|
|
|
|
// sign a message
|
|
|
|
int64_t sigTime = GetAdjustedTime();
|
|
std::string strMessage = txNew.GetHash().ToString() + std::to_string(sigTime);
|
|
std::string strError = "";
|
|
std::vector<unsigned char> vchSig;
|
|
CKey key2;
|
|
CPubKey pubkey2;
|
|
|
|
if (!obfuScationSigner.SetKey(strMasterNodePrivKey, strError, key2, pubkey2)) {
|
|
LogPrintf("CObfuscationPool::Check() - ERROR: Invalid Masternodeprivkey: '%s'\n", strError);
|
|
return;
|
|
}
|
|
|
|
if (!obfuScationSigner.SignMessage(strMessage, strError, vchSig, key2)) {
|
|
LogPrintf("CObfuscationPool::Check() - Sign message failed\n");
|
|
return;
|
|
}
|
|
|
|
if (!obfuScationSigner.VerifyMessage(pubkey2, vchSig, strMessage, strError)) {
|
|
LogPrintf("CObfuscationPool::Check() - Verify message failed\n");
|
|
return;
|
|
}
|
|
|
|
if (!mapObfuscationBroadcastTxes.count(txNew.GetHash())) {
|
|
CObfuscationBroadcastTx dstx;
|
|
dstx.tx = txNew;
|
|
dstx.vin = activeMasternode.vin;
|
|
dstx.vchSig = vchSig;
|
|
dstx.sigTime = sigTime;
|
|
|
|
mapObfuscationBroadcastTxes.insert(make_pair(txNew.GetHash(), dstx));
|
|
}
|
|
|
|
CInv inv(MSG_DSTX, txNew.GetHash());
|
|
RelayInv(inv);
|
|
|
|
// Tell the clients it was successful
|
|
RelayCompletedTransaction(sessionID, false, MSG_SUCCESS);
|
|
|
|
// Randomly charge clients
|
|
ChargeRandomFees();
|
|
|
|
// Reset
|
|
LogPrint("obfuscation", "CObfuscationPool::Check() -- COMPLETED -- RESETTING\n");
|
|
SetNull();
|
|
RelayStatus(sessionID, GetState(), GetEntriesCount(), MASTERNODE_RESET);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Charge clients a fee if they're abusive
|
|
//
|
|
// Why bother? Obfuscation uses collateral to ensure abuse to the process is kept to a minimum.
|
|
// The submission and signing stages in Obfuscation are completely separate. In the cases where
|
|
// a client submits a transaction then refused to sign, there must be a cost. Otherwise they
|
|
// would be able to do this over and over again and bring the mixing to a hault.
|
|
//
|
|
// How does this work? Messages to Masternodes come in via "dsi", these require a valid collateral
|
|
// transaction for the client to be able to enter the pool. This transaction is kept by the Masternode
|
|
// until the transaction is either complete or fails.
|
|
//
|
|
void CObfuscationPool::ChargeFees()
|
|
{
|
|
if (!fMasterNode) return;
|
|
|
|
//we don't need to charge collateral for every offence.
|
|
int offences = 0;
|
|
int r = rand() % 100;
|
|
if (r > 33) return;
|
|
|
|
if (state == POOL_STATUS_ACCEPTING_ENTRIES) {
|
|
for (const CTransaction& txCollateral : vecSessionCollateral) {
|
|
bool found = false;
|
|
for (const CObfuScationEntry& v : entries) {
|
|
if (v.collateral == txCollateral) {
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
// This queue entry didn't send us the promised transaction
|
|
if (!found) {
|
|
LogPrintf("CObfuscationPool::ChargeFees -- found uncooperative node (didn't send transaction). Found offence.\n");
|
|
offences++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (state == POOL_STATUS_SIGNING) {
|
|
// who didn't sign?
|
|
for (const CObfuScationEntry &v : entries) {
|
|
for (const CTxDSIn &s : v.sev) {
|
|
if (!s.fHasSig) {
|
|
LogPrintf("CObfuscationPool::ChargeFees -- found uncooperative node (didn't sign). Found offence\n");
|
|
offences++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
r = rand() % 100;
|
|
int target = 0;
|
|
|
|
//mostly offending?
|
|
if (offences >= Params().PoolMaxTransactions() - 1 && r > 33) return;
|
|
|
|
//everyone is an offender? That's not right
|
|
if (offences >= Params().PoolMaxTransactions()) return;
|
|
|
|
//charge one of the offenders randomly
|
|
if (offences > 1) target = 50;
|
|
|
|
//pick random client to charge
|
|
r = rand() % 100;
|
|
|
|
if (state == POOL_STATUS_ACCEPTING_ENTRIES) {
|
|
for (const CTransaction& txCollateral : vecSessionCollateral) {
|
|
bool found = false;
|
|
for (const CObfuScationEntry& v : entries) {
|
|
if (v.collateral == txCollateral) {
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
// This queue entry didn't send us the promised transaction
|
|
if (!found && r > target) {
|
|
LogPrintf("CObfuscationPool::ChargeFees -- found uncooperative node (didn't send transaction). charging fees.\n");
|
|
|
|
CWalletTx wtxCollateral = CWalletTx(pwalletMain, txCollateral);
|
|
|
|
// Broadcast
|
|
if (!wtxCollateral.AcceptToMemoryPool(true)) {
|
|
// This must not fail. The transaction has already been signed and recorded.
|
|
LogPrintf("CObfuscationPool::ChargeFees() : Error: Transaction not valid");
|
|
}
|
|
wtxCollateral.RelayWalletTransaction();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (state == POOL_STATUS_SIGNING) {
|
|
// who didn't sign?
|
|
for (const CObfuScationEntry &v : entries) {
|
|
for (const CTxDSIn &s : v.sev) {
|
|
if (!s.fHasSig && r > target) {
|
|
LogPrintf("CObfuscationPool::ChargeFees -- found uncooperative node (didn't sign). charging fees.\n");
|
|
|
|
CWalletTx wtxCollateral = CWalletTx(pwalletMain, v.collateral);
|
|
|
|
// Broadcast
|
|
if (!wtxCollateral.AcceptToMemoryPool(false)) {
|
|
// This must not fail. The transaction has already been signed and recorded.
|
|
LogPrintf("CObfuscationPool::ChargeFees() : Error: Transaction not valid");
|
|
}
|
|
wtxCollateral.RelayWalletTransaction();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// charge the collateral randomly
|
|
// - Obfuscation is completely free, to pay miners we randomly pay the collateral of users.
|
|
void CObfuscationPool::ChargeRandomFees()
|
|
{
|
|
if (fMasterNode) {
|
|
int i = 0;
|
|
|
|
for (const CTransaction& txCollateral : vecSessionCollateral) {
|
|
int r = rand() % 100;
|
|
|
|
/*
|
|
Collateral Fee Charges:
|
|
|
|
Being that Obfuscation has "no fees" we need to have some kind of cost associated
|
|
with using it to stop abuse. Otherwise it could serve as an attack vector and
|
|
allow endless transaction that would bloat Agrarian and make it unusable. To
|
|
stop these kinds of attacks 1 in 10 successful transactions are charged. This
|
|
adds up to a cost of 0.001 AGR per transaction on average.
|
|
*/
|
|
if (r <= 10) {
|
|
LogPrintf("CObfuscationPool::ChargeRandomFees -- charging random fees. %u\n", i);
|
|
|
|
CWalletTx wtxCollateral = CWalletTx(pwalletMain, txCollateral);
|
|
|
|
// Broadcast
|
|
if (!wtxCollateral.AcceptToMemoryPool(true)) {
|
|
// This must not fail. The transaction has already been signed and recorded.
|
|
LogPrintf("CObfuscationPool::ChargeRandomFees() : Error: Transaction not valid");
|
|
}
|
|
wtxCollateral.RelayWalletTransaction();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for various timeouts (queue objects, Obfuscation, etc)
|
|
//
|
|
void CObfuscationPool::CheckTimeout()
|
|
{
|
|
if (!fEnableZeromint && !fMasterNode) return;
|
|
|
|
// catching hanging sessions
|
|
if (!fMasterNode) {
|
|
switch (state) {
|
|
case POOL_STATUS_TRANSMISSION:
|
|
LogPrint("obfuscation", "CObfuscationPool::CheckTimeout() -- Session complete -- Running Check()\n");
|
|
Check();
|
|
break;
|
|
case POOL_STATUS_ERROR:
|
|
LogPrint("obfuscation", "CObfuscationPool::CheckTimeout() -- Pool error -- Running Check()\n");
|
|
Check();
|
|
break;
|
|
case POOL_STATUS_SUCCESS:
|
|
LogPrint("obfuscation", "CObfuscationPool::CheckTimeout() -- Pool success -- Running Check()\n");
|
|
Check();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// check Obfuscation queue objects for timeouts
|
|
int c = 0;
|
|
vector<CObfuscationQueue>::iterator it = vecObfuscationQueue.begin();
|
|
while (it != vecObfuscationQueue.end()) {
|
|
if ((*it).IsExpired()) {
|
|
LogPrint("obfuscation", "CObfuscationPool::CheckTimeout() : Removing expired queue entry - %d\n", c);
|
|
it = vecObfuscationQueue.erase(it);
|
|
} else
|
|
++it;
|
|
c++;
|
|
}
|
|
|
|
int addLagTime = 0;
|
|
if (!fMasterNode) addLagTime = 10000; //if we're the client, give the server a few extra seconds before resetting.
|
|
|
|
if (state == POOL_STATUS_ACCEPTING_ENTRIES || state == POOL_STATUS_QUEUE) {
|
|
c = 0;
|
|
|
|
// check for a timeout and reset if needed
|
|
vector<CObfuScationEntry>::iterator it2 = entries.begin();
|
|
while (it2 != entries.end()) {
|
|
if ((*it2).IsExpired()) {
|
|
LogPrint("obfuscation", "CObfuscationPool::CheckTimeout() : Removing expired entry - %d\n", c);
|
|
it2 = entries.erase(it2);
|
|
if (entries.size() == 0) {
|
|
UnlockCoins();
|
|
SetNull();
|
|
}
|
|
if (fMasterNode) {
|
|
RelayStatus(sessionID, GetState(), GetEntriesCount(), MASTERNODE_RESET);
|
|
}
|
|
} else
|
|
++it2;
|
|
c++;
|
|
}
|
|
|
|
if (GetTimeMillis() - lastTimeChanged >= (OBFUSCATION_QUEUE_TIMEOUT * 1000) + addLagTime) {
|
|
UnlockCoins();
|
|
SetNull();
|
|
}
|
|
} else if (GetTimeMillis() - lastTimeChanged >= (OBFUSCATION_QUEUE_TIMEOUT * 1000) + addLagTime) {
|
|
LogPrint("obfuscation", "CObfuscationPool::CheckTimeout() -- Session timed out (%ds) -- resetting\n", OBFUSCATION_QUEUE_TIMEOUT);
|
|
UnlockCoins();
|
|
SetNull();
|
|
|
|
UpdateState(POOL_STATUS_ERROR);
|
|
lastMessage = _("Session timed out.");
|
|
}
|
|
|
|
if (state == POOL_STATUS_SIGNING && GetTimeMillis() - lastTimeChanged >= (OBFUSCATION_SIGNING_TIMEOUT * 1000) + addLagTime) {
|
|
LogPrint("obfuscation", "CObfuscationPool::CheckTimeout() -- Session timed out (%ds) -- restting\n", OBFUSCATION_SIGNING_TIMEOUT);
|
|
ChargeFees();
|
|
UnlockCoins();
|
|
SetNull();
|
|
|
|
UpdateState(POOL_STATUS_ERROR);
|
|
lastMessage = _("Signing timed out.");
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for complete queue
|
|
//
|
|
void CObfuscationPool::CheckForCompleteQueue()
|
|
{
|
|
if (!fEnableZeromint && !fMasterNode) return;
|
|
|
|
/* Check to see if we're ready for submissions from clients */
|
|
//
|
|
// After receiving multiple dsa messages, the queue will switch to "accepting entries"
|
|
// which is the active state right before merging the transaction
|
|
//
|
|
if (state == POOL_STATUS_QUEUE && sessionUsers == GetMaxPoolTransactions()) {
|
|
UpdateState(POOL_STATUS_ACCEPTING_ENTRIES);
|
|
|
|
CObfuscationQueue dsq;
|
|
dsq.nDenom = sessionDenom;
|
|
dsq.vin = activeMasternode.vin;
|
|
dsq.time = GetTime();
|
|
dsq.ready = true;
|
|
dsq.Sign();
|
|
dsq.Relay();
|
|
}
|
|
}
|
|
|
|
|
|
// Check to make sure everything is signed
|
|
bool CObfuscationPool::SignaturesComplete()
|
|
{
|
|
for (const CObfuScationEntry& v : entries) {
|
|
for (const CTxDSIn& s : v.sev) {
|
|
if (!s.fHasSig) return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void CObfuscationPool::NewBlock()
|
|
{
|
|
LogPrint("obfuscation", "CObfuscationPool::NewBlock \n");
|
|
|
|
//we we're processing lots of blocks, we'll just leave
|
|
if (GetTime() - lastNewBlock < 10) return;
|
|
lastNewBlock = GetTime();
|
|
|
|
obfuScationPool.CheckTimeout();
|
|
}
|
|
|
|
bool CObfuScationSigner::IsVinAssociatedWithPubkey(CTxIn& vin, CPubKey& pubkey)
|
|
{
|
|
CScript payee2;
|
|
payee2 = GetScriptForDestination(pubkey.GetID());
|
|
|
|
CTransaction txVin;
|
|
uint256 hash;
|
|
if (GetTransaction(vin.prevout.hash, txVin, hash, true)) {
|
|
for (CTxOut out : txVin.vout) {
|
|
if (out.nValue == 10000 * COIN) {
|
|
if (out.scriptPubKey == payee2) return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CObfuScationSigner::SetKey(std::string strSecret, std::string& errorMessage, CKey& key, CPubKey& pubkey)
|
|
{
|
|
CBitcoinSecret vchSecret;
|
|
bool fGood = vchSecret.SetString(strSecret);
|
|
|
|
if (!fGood) {
|
|
errorMessage = _("Invalid private key.");
|
|
return false;
|
|
}
|
|
|
|
key = vchSecret.GetKey();
|
|
pubkey = key.GetPubKey();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CObfuScationSigner::GetKeysFromSecret(std::string strSecret, CKey& keyRet, CPubKey& pubkeyRet)
|
|
{
|
|
CBitcoinSecret vchSecret;
|
|
|
|
if (!vchSecret.SetString(strSecret)) return false;
|
|
|
|
keyRet = vchSecret.GetKey();
|
|
pubkeyRet = keyRet.GetPubKey();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CObfuScationSigner::SignMessage(std::string strMessage, std::string& errorMessage, vector<unsigned char>& vchSig, CKey key)
|
|
{
|
|
CHashWriter ss(SER_GETHASH, 0);
|
|
ss << strMessageMagic;
|
|
ss << strMessage;
|
|
|
|
if (!key.SignCompact(ss.GetHash(), vchSig)) {
|
|
errorMessage = _("Signing failed.");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CObfuScationSigner::VerifyMessage(CPubKey pubkey, vector<unsigned char>& vchSig, std::string strMessage, std::string& errorMessage)
|
|
{
|
|
CHashWriter ss(SER_GETHASH, 0);
|
|
ss << strMessageMagic;
|
|
ss << strMessage;
|
|
|
|
CPubKey pubkey2;
|
|
if (!pubkey2.RecoverCompact(ss.GetHash(), vchSig)) {
|
|
errorMessage = _("Error recovering public key.");
|
|
return false;
|
|
}
|
|
|
|
if (fDebug && pubkey2.GetID() != pubkey.GetID())
|
|
LogPrintf("CObfuScationSigner::VerifyMessage -- keys don't match: %s %s\n", pubkey2.GetID().ToString(), pubkey.GetID().ToString());
|
|
|
|
return (pubkey2.GetID() == pubkey.GetID());
|
|
}
|
|
|
|
bool CObfuscationQueue::Sign()
|
|
{
|
|
if (!fMasterNode) return false;
|
|
|
|
std::string strMessage = vin.ToString() + std::to_string(nDenom) + std::to_string(time) + std::to_string(ready);
|
|
|
|
CKey key2;
|
|
CPubKey pubkey2;
|
|
std::string errorMessage = "";
|
|
|
|
if (!obfuScationSigner.SetKey(strMasterNodePrivKey, errorMessage, key2, pubkey2)) {
|
|
LogPrintf("CObfuscationQueue():Relay - ERROR: Invalid Masternodeprivkey: '%s'\n", errorMessage);
|
|
return false;
|
|
}
|
|
|
|
if (!obfuScationSigner.SignMessage(strMessage, errorMessage, vchSig, key2)) {
|
|
LogPrintf("CObfuscationQueue():Relay - Sign message failed");
|
|
return false;
|
|
}
|
|
|
|
if (!obfuScationSigner.VerifyMessage(pubkey2, vchSig, strMessage, errorMessage)) {
|
|
LogPrintf("CObfuscationQueue():Relay - Verify message failed");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CObfuscationQueue::Relay()
|
|
{
|
|
LOCK(cs_vNodes);
|
|
for (CNode* pnode : vNodes) {
|
|
// always relay to everyone
|
|
pnode->PushMessage("dsq", (*this));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CObfuscationPool::RelayFinalTransaction(const int sessionID, const CTransaction& txNew)
|
|
{
|
|
LOCK(cs_vNodes);
|
|
for (CNode* pnode : vNodes) {
|
|
pnode->PushMessage("dsf", sessionID, txNew);
|
|
}
|
|
}
|
|
|
|
void CObfuscationPool::RelayStatus(const int sessionID, const int newState, const int newEntriesCount, const int newAccepted, const int errorID)
|
|
{
|
|
LOCK(cs_vNodes);
|
|
for (CNode* pnode : vNodes)
|
|
pnode->PushMessage("dssu", sessionID, newState, newEntriesCount, newAccepted, errorID);
|
|
}
|
|
|
|
void CObfuscationPool::RelayCompletedTransaction(const int sessionID, const bool error, const int errorID)
|
|
{
|
|
LOCK(cs_vNodes);
|
|
for (CNode* pnode : vNodes)
|
|
pnode->PushMessage("dsc", sessionID, error, errorID);
|
|
}
|
|
|
|
//TODO: Rename/move to core
|
|
void ThreadCheckObfuScationPool()
|
|
{
|
|
if (fLiteMode) return; //disable all Obfuscation/Masternode related functionality
|
|
|
|
// Make this thread recognisable as the wallet flushing thread
|
|
RenameThread("agrarian-obfuscation");
|
|
|
|
unsigned int c = 0;
|
|
|
|
while (true) {
|
|
MilliSleep(1000);
|
|
//LogPrintf("ThreadCheckObfuScationPool::check timeout\n");
|
|
|
|
// try to sync from all available nodes, one step at a time
|
|
masternodeSync.Process();
|
|
|
|
if (masternodeSync.IsBlockchainSynced()) {
|
|
c++;
|
|
|
|
// check if we should activate or ping every few minutes,
|
|
// start right after sync is considered to be done
|
|
if (c % MASTERNODE_PING_SECONDS == 1) activeMasternode.ManageStatus();
|
|
|
|
if (c % 60 == 0) {
|
|
mnodeman.CheckAndRemove();
|
|
mnodeman.ProcessMasternodeConnections();
|
|
masternodePayments.CleanPaymentList();
|
|
CleanTransactionLocksList();
|
|
}
|
|
|
|
//if(c % MASTERNODES_DUMP_SECONDS == 0) DumpMasternodes();
|
|
|
|
obfuScationPool.CheckTimeout();
|
|
obfuScationPool.CheckForCompleteQueue();
|
|
}
|
|
}
|
|
}
|