Commit e1b9d218 authored by Ashot's avatar Ashot
Browse files

Merge branch 'broadcast-locked-transactions' into 'Current-dev'

Broadcast locked transactions

See merge request crown/crown-core!116
parents 76b5804d 733b2c9e
Showing with 604 additions and 836 deletions
+604 -836
......@@ -167,7 +167,9 @@ BITCOIN_CORE_H = \
compat/byteswap.h \
compat/endian.h \
compat/sanity.h \
updater.h
updater.h \
dbdetails.h \
dbmanager.h
JSON_H = \
json/json_spirit.h \
......@@ -218,6 +220,7 @@ libbitcoin_server_a_SOURCES = \
timedata.cpp \
txdb.cpp \
txmempool.cpp \
dbdetails.cpp \
$(JSON_H) \
$(BITCOIN_CORE_H)
......
......@@ -73,7 +73,8 @@ BITCOIN_TESTS =\
test/timedata_tests.cpp \
test/transaction_tests.cpp \
test/univalue_tests.cpp \
test/util_tests.cpp
test/util_tests.cpp \
test/db_tests.cpp
if ENABLE_WALLET
BITCOIN_TESTS += \
......
......@@ -119,26 +119,14 @@ int CMerkleTx::GetTransactionLockSignatures() const
if(!IsSporkActive(SPORK_2_INSTANTX)) return -3;
if(!fEnableInstantX) return -1;
//compile consessus vote
std::map<uint256, CTransactionLock>::iterator i = mapTxLocks.find(GetHash());
if (i != mapTxLocks.end()){
return (*i).second.CountSignatures();
}
return -1;
return GetInstantSend().GetSignaturesCount(GetHash());
}
bool CMerkleTx::IsTransactionLockTimedOut() const
{
if(!fEnableInstantX) return 0;
//compile consessus vote
std::map<uint256, CTransactionLock>::iterator i = mapTxLocks.find(GetHash());
if (i != mapTxLocks.end()){
return GetTime() > (*i).second.nTimeout;
}
return false;
return GetInstantSend().IsLockTimedOut(GetHash());
}
bool
......@@ -244,4 +232,4 @@ CAuxPow::getExpectedIndex (uint32_t nNonce, int nChainId, unsigned h)
rand = rand * 1103515245 + 12345;
return rand % (1 << h);
}
\ No newline at end of file
}
// Copyright (c) 2014-2018 The Crown developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "dbdetails.h"
namespace details
{
ReadResult ReadStream(CDataStream& stream, const std::string& filename)
{
boost::filesystem::path pathDb = GetDataDir() / filename;
// open input file, and associate with CAutoFile
FILE *file = fopen(pathDb.string().c_str(), "rb");
CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
if (filein.IsNull())
{
error("%s: Failed to open file %s", __func__, pathDb.string());
return FileError;
}
// use file size to size memory buffer
int fileSize = boost::filesystem::file_size(pathDb);
int dataSize = fileSize - sizeof(uint256);
// Don't try to resize to a negative number if file is small
if (dataSize < 0)
dataSize = 0;
std::vector<unsigned char> vchData(dataSize);
uint256 hashIn;
// read data and checksum from file
try
{
filein.read((char *)&vchData[0], dataSize);
filein >> hashIn;
}
catch (const std::exception &e)
{
error("%s: Deserialize or I/O error - %s", __func__, e.what());
return HashReadError;
}
filein.fclose();
stream = CDataStream(vchData, SER_DISK, CLIENT_VERSION);
// verify stored checksum matches input data
uint256 hashTmp = Hash(stream.begin(), stream.end());
if (hashIn != hashTmp)
{
error("%s: Checksum mismatch, data corrupted", __func__);
return IncorrectHash;
}
return Ok;
}
}
// Copyright (c) 2014-2018 The Crown developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DB_DETAILS_H
#define DB_DETAILS_H
#include "chainparams.h"
#include "clientversion.h"
#include "hash.h"
#include "streams.h"
#include "util.h"
#include <boost/filesystem.hpp>
namespace details
{
enum ReadResult
{
Ok,
FileError,
HashReadError,
IncorrectHash,
IncorrectMagicMessage,
IncorrectMagicNumber,
IncorrectFormat
};
ReadResult ReadStream(CDataStream& stream, const std::string& filename);
template <typename T>
ReadResult DeserializeStream(CDataStream& stream, const std::string& magicMessage, T& objToLoad)
{
unsigned char pchMsgTmp[4];
std::string strMagicMessageTmp;
// de-serialize file header (file specific magic message) and ..
stream >> strMagicMessageTmp;
// ... verify the message matches predefined one
if (magicMessage != strMagicMessageTmp)
{
error("%s: Invalid magic message", __func__);
return IncorrectMagicMessage;
}
// de-serialize file header (network specific magic number) and ..
stream >> FLATDATA(pchMsgTmp);
// ... verify the network matches ours
if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
{
error("%s: Invalid network magic number", __func__);
return IncorrectMagicNumber;
}
// de-serialize data into T object
stream >> objToLoad;
return Ok;
}
template <typename T>
ReadResult Read(T& objToLoad, const std::string& filename, const std::string& magicMessage, bool fDryRun = false)
{
CDataStream ssObj(SER_DISK, CLIENT_VERSION);
ReadResult result = ReadStream(ssObj, filename);
if (result != Ok)
return result;
try
{
result = DeserializeStream(ssObj, magicMessage, objToLoad);
if (result != Ok)
return result;
}
catch (const std::exception &e)
{
objToLoad.Clear();
error("%s: Deserialize or I/O error - %s", __func__, e.what());
return IncorrectFormat;
}
int64_t nStart = GetTimeMillis();
LogPrintf("Loaded info from %s %dms\n", filename, GetTimeMillis() - nStart);
LogPrintf(" %s\n", objToLoad.ToString());
if(!fDryRun)
{
LogPrintf("%s: Cleaning....\n", __func__);
objToLoad.CheckAndRemove();
LogPrintf(" %s\n", objToLoad.ToString());
}
return Ok;
}
template <typename T>
bool Write(const T& objToSave, const std::string& filename, const std::string& magicMessage)
{
int64_t nStart = GetTimeMillis();
// serialize, checksum data up to that point, then append checksum
CDataStream ssObj(SER_DISK, CLIENT_VERSION);
ssObj << magicMessage; // specific magic message for this type of object
ssObj << FLATDATA(Params().MessageStart()); // network specific magic number
ssObj << objToSave;
uint256 hash = Hash(ssObj.begin(), ssObj.end());
ssObj << hash;
// open output file, and associate with CAutoFile
boost::filesystem::path pathDb = GetDataDir() / filename;
FILE *file = fopen(pathDb.string().c_str(), "wb");
CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
if (fileout.IsNull())
return error("%s: Failed to open file %s", __func__, pathDb.string());
// Write and commit header, data
try
{
fileout << ssObj;
}
catch (const std::exception &e)
{
return error("%s: Serialize or I/O error - %s", __func__, e.what());
}
fileout.fclose();
LogPrintf("Written info to %s %dms\n", filename, GetTimeMillis() - nStart);
LogPrintf(" %s\n", objToSave.ToString());
return true;
}
}
#endif
// Copyright (c) 2014-2018 The Crown developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DB_MANAGER_H
#define DB_MANAGER_H
#include "dbdetails.h"
template <typename T>
bool Load(T& objToLoad, const std::string& filename, const std::string& magicMessage, bool fDryRun = false)
{
LogPrintf("Reading info from %s...\n", filename);
::details::ReadResult readResult = ::details::Read(objToLoad, filename, magicMessage, fDryRun);
switch(readResult)
{
case ::details::Ok:
return true;
case ::details::FileError:
LogPrintf("Missing file %s, will try to recreate\n", filename);
break;
case ::details::IncorrectFormat:
LogPrintf("Error reading %s: ", filename);
LogPrintf("%s: Magic is ok but data has invalid format, will try to recreate\n", __func__);
break;
default:
LogPrintf("Error reading %s: ", filename);
LogPrintf("%s: File format is unknown or invalid, please fix it manually\n", __func__);
return false;
}
return true;
}
template <typename T>
bool Dump(const T& objToSave, const std::string& filename, const std::string& magicMessage)
{
int64_t nStart = GetTimeMillis();
LogPrintf("Verifying %s format...\n", filename);
T tmpObjToLoad;
if (!Load(tmpObjToLoad, filename, magicMessage, true))
return false;
LogPrintf("Writing info to %s...\n", filename);
::details::Write(objToSave, filename, magicMessage);
LogPrintf("%s dump finished %dms\n", filename, GetTimeMillis() - nStart);
return true;
}
#endif
......@@ -37,6 +37,8 @@
#include "systemnodeconfig.h"
#include "spork.h"
#include "utilmoneystr.h"
#include "dbmanager.h"
#include "instantx.h"
#ifdef ENABLE_WALLET
#include "db.h"
#include "wallet.h"
......@@ -160,6 +162,8 @@ static CCoinsViewDB *pcoinsdbview = NULL;
static CCoinsViewErrorCatcher *pcoinscatcher = NULL;
static boost::scoped_ptr<ECCVerifyHandle> globalVerifyHandle;
void DumpData();
/** Preparing steps before shutting down or restarting the wallet */
void PrepareShutdown()
{
......@@ -185,11 +189,7 @@ void PrepareShutdown()
GenerateBitcoins(false, NULL, 0);
#endif
StopNode();
DumpMasternodes();
DumpBudgets();
DumpMasternodePayments();
DumpSystemnodes();
DumpSystemnodePayments();
DumpData();
UnregisterNodeSignals(GetNodeSignals());
if (fFeeEstimatesInitialized)
......@@ -586,7 +586,64 @@ bool InitSanityCheck(void)
return true;
}
bool LoadData()
{
std::string strDBName;
boost::filesystem::path pathDB = GetDataDir();
uiInterface.InitMessage(_("Loading masternode cache..."));
if (!Load(mnodeman, "mncache.dat", "MasternodeCache"))
{
return InitError(_("Failed to load masternode cache from") + "\n" + (pathDB / strDBName).string());
}
uiInterface.InitMessage(_("Loading systemnode cache..."));
if (!Load(snodeman, "sncache.dat", "SystemnodeCache"))
{
return InitError(_("Failed to load systemnode cache from") + "\n" + (pathDB / strDBName).string());
}
uiInterface.InitMessage(_("Loading budget cache..."));
if (!Load(budget, "budget.dat", "MasternodeBudget"))
{
return InitError(_("Failed to load systemnode cache from") + "\n" + (pathDB / strDBName).string());
}
//flag our cached items so we send them to our peers
budget.ResetSync();
budget.ClearSeen();
uiInterface.InitMessage(_("Loading masternode payment cache..."));
if (!Load(masternodePayments, "mnpayments.dat", "MasternodePayments"))
{
return InitError(_("Failed to load systemnode cache from") + "\n" + (pathDB / strDBName).string());
}
uiInterface.InitMessage(_("Loading systemnode payment cache..."));
if (!Load(systemnodePayments, "snpayments.dat", "SystemnodePayments"))
{
return InitError(_("Failed to load systemnode cache from") + "\n" + (pathDB / strDBName).string());
}
uiInterface.InitMessage(_("Loading instant send cache..."));
if (!Load(GetInstantSend(), "ixcache.dat", "InstantSend"))
{
return InitError(_("Failed to load systemnode cache from") + "\n" + (pathDB / strDBName).string());
}
return true;
}
void DumpData()
{
Dump(mnodeman, "mncache.dat", "MasternodeCache");
Dump(budget, "budget.dat", "MasternodeBudget");
Dump(masternodePayments, "mnpayments.dat", "MasternodePayments");
Dump(snodeman, "sncache.dat", "SystemnodeCache");
Dump(systemnodePayments, "snpayments.dat", "SystemnodePayments");
Dump(GetInstantSend(), "ixcache.dat", "InstantSend");
}
/** Initialize crown.
* @pre Parameters should be parsed and config file should be read.
......@@ -1439,89 +1496,8 @@ bool AppInit2(boost::thread_group& threadGroup)
// ********************************************************* Step 10: setup Budgets
uiInterface.InitMessage(_("Loading masternode cache..."));
CMasternodeDB mndb;
CMasternodeDB::ReadResult readResult = mndb.Read(mnodeman);
if (readResult == CMasternodeDB::FileError)
LogPrintf("Missing masternode cache file - mncache.dat, will try to recreate\n");
else if (readResult != CMasternodeDB::Ok)
{
LogPrintf("Error reading mncache.dat: ");
if(readResult == CMasternodeDB::IncorrectFormat)
LogPrintf("magic is ok but data has invalid format, will try to recreate\n");
else
LogPrintf("file format is unknown or invalid, please fix it manually\n");
}
uiInterface.InitMessage(_("Loading systemnode cache..."));
CSystemnodeDB sndb;
CSystemnodeDB::ReadResult readResultS = sndb.Read(snodeman);
if (readResultS == CSystemnodeDB::FileError)
LogPrintf("Missing systemnode cache file - sncache.dat, will try to recreate\n");
else if (readResultS != CSystemnodeDB::Ok)
{
LogPrintf("Error reading sncache.dat: ");
if(readResultS == CSystemnodeDB::IncorrectFormat)
LogPrintf("magic is ok but data has invalid format, will try to recreate\n");
else
LogPrintf("file format is unknown or invalid, please fix it manually\n");
}
uiInterface.InitMessage(_("Loading budget cache..."));
CBudgetDB budgetdb;
CBudgetDB::ReadResult readResult2 = budgetdb.Read(budget);
if (readResult2 == CBudgetDB::FileError)
LogPrintf("Missing budget cache - budget.dat, will try to recreate\n");
else if (readResult2 != CBudgetDB::Ok)
{
LogPrintf("Error reading budget.dat: ");
if(readResult2 == CBudgetDB::IncorrectFormat)
LogPrintf("magic is ok but data has invalid format, will try to recreate\n");
else
LogPrintf("file format is unknown or invalid, please fix it manually\n");
}
//flag our cached items so we send them to our peers
budget.ResetSync();
budget.ClearSeen();
uiInterface.InitMessage(_("Loading masternode payment cache..."));
CMasternodePaymentDB mnpayments;
CMasternodePaymentDB::ReadResult readResult3 = mnpayments.Read(masternodePayments);
if (readResult3 == CMasternodePaymentDB::FileError)
LogPrintf("Missing masternode payment cache - mnpayments.dat, will try to recreate\n");
else if (readResult3 != CMasternodePaymentDB::Ok)
{
LogPrintf("Error reading mnpayments.dat: ");
if(readResult3 == CMasternodePaymentDB::IncorrectFormat)
LogPrintf("magic is ok but data has invalid format, will try to recreate\n");
else
LogPrintf("file format is unknown or invalid, please fix it manually\n");
}
uiInterface.InitMessage(_("Loading systemnode payment cache..."));
CSystemnodePaymentDB snpayments;
CSystemnodePaymentDB::ReadResult readResult4 = snpayments.Read(systemnodePayments);
if (readResult4 == CSystemnodePaymentDB::FileError)
LogPrintf("Missing systemnode payment cache - snpayments.dat, will try to recreate\n");
else if (readResult4 != CSystemnodePaymentDB::Ok)
{
LogPrintf("Error reading snpayments.dat: ");
if(readResult4 == CSystemnodePaymentDB::IncorrectFormat)
LogPrintf("magic is ok but data has invalid format, will try to recreate\n");
else
LogPrintf("file format is unknown or invalid, please fix it manually\n");
}
if (!LoadData())
return false;
fMasterNode = GetBoolArg("-masternode", false);
fSystemNode = GetBoolArg("-systemnode", false);
......
......@@ -19,22 +19,14 @@
using namespace std;
using namespace boost;
std::map<uint256, CTransaction> mapTxLockReq;
std::map<uint256, CTransaction> mapTxLockReqRejected;
std::map<uint256, CConsensusVote> mapTxLockVote;
std::map<uint256, CTransactionLock> mapTxLocks;
std::map<COutPoint, uint256> mapLockedInputs;
std::map<uint256, int64_t> mapUnknownVotes; //track votes with no tx for DOS
int nCompleteTXLocks;
//txlock - Locks transaction
//
std::auto_ptr<InstantSend> g_instantSend;
//step 1.) Broadcast intention to lock transaction inputs, "txlreg", CTransaction
//step 2.) Top INSTANTX_SIGNATURES_TOTAL masternodes, open connect to top 1 masternode.
// Send "txvote", CTransaction, Signature, Approve
//step 3.) Top 1 masternode, waits for INSTANTX_SIGNATURES_REQUIRED messages. Upon success, sends "txlock'
void ProcessMessageInstantX(CNode* pfrom, std::string& strCommand, CDataStream& vRecv)
void InstantSend::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv)
{
if(fLiteMode) return; //disable all masternode related functionality
if(!IsSporkActive(SPORK_2_INSTANTX)) return;
......@@ -50,11 +42,11 @@ void ProcessMessageInstantX(CNode* pfrom, std::string& strCommand, CDataStream&
CInv inv(MSG_TXLOCK_REQUEST, tx.GetHash());
pfrom->AddInventoryKnown(inv);
if(mapTxLockReq.count(tx.GetHash()) || mapTxLockReqRejected.count(tx.GetHash())){
if(m_txLockReq.count(tx.GetHash()) || m_txLockReqRejected.count(tx.GetHash())){
return;
}
if(!IsIXTXValid(tx)){
if(!IsIxTxValid(tx)){
return;
}
......@@ -83,7 +75,7 @@ void ProcessMessageInstantX(CNode* pfrom, std::string& strCommand, CDataStream&
DoConsensusVote(tx, nBlockHeight);
mapTxLockReq.insert(make_pair(tx.GetHash(), tx));
m_txLockReq.insert(make_pair(tx.GetHash(), tx));
IXLogPrintf("ProcessMessageInstantX::ix - Transaction Lock Request: %s %s : accepted %s\n",
pfrom->addr.ToString().c_str(), pfrom->cleanSubVer.c_str(),
......@@ -93,7 +85,7 @@ void ProcessMessageInstantX(CNode* pfrom, std::string& strCommand, CDataStream&
return;
} else {
mapTxLockReqRejected.insert(make_pair(tx.GetHash(), tx));
m_txLockReqRejected.insert(make_pair(tx.GetHash(), tx));
// can we get the conflicting transaction as proof?
......@@ -103,14 +95,14 @@ void ProcessMessageInstantX(CNode* pfrom, std::string& strCommand, CDataStream&
);
BOOST_FOREACH(const CTxIn& in, tx.vin){
if(!mapLockedInputs.count(in.prevout)){
mapLockedInputs.insert(make_pair(in.prevout, tx.GetHash()));
if(!m_lockedInputs.count(in.prevout)){
m_lockedInputs.insert(make_pair(in.prevout, tx.GetHash()));
}
}
// resolve conflicts
std::map<uint256, CTransactionLock>::iterator i = mapTxLocks.find(tx.GetHash());
if (i != mapTxLocks.end()){
std::map<uint256, CTransactionLock>::iterator i = m_txLocks.find(tx.GetHash());
if (i != m_txLocks.end()){
//we only care if we have a complete tx lock
if((*i).second.CountSignatures() >= INSTANTX_SIGNATURES_REQUIRED){
if(!CheckForConflictingLocks(tx)){
......@@ -118,7 +110,7 @@ void ProcessMessageInstantX(CNode* pfrom, std::string& strCommand, CDataStream&
//reprocess the last 15 blocks
ReprocessBlocks(15);
mapTxLockReq.insert(make_pair(tx.GetHash(), tx));
m_txLockReq.insert(make_pair(tx.GetHash(), tx));
}
}
}
......@@ -134,11 +126,11 @@ void ProcessMessageInstantX(CNode* pfrom, std::string& strCommand, CDataStream&
CInv inv(MSG_TXLOCK_VOTE, ctx.GetHash());
pfrom->AddInventoryKnown(inv);
if(mapTxLockVote.count(ctx.GetHash())){
if(m_txLockVote.count(ctx.GetHash())){
return;
}
mapTxLockVote.insert(make_pair(ctx.GetHash(), ctx));
m_txLockVote.insert(make_pair(ctx.GetHash(), ctx));
if(ProcessConsensusVote(pfrom, ctx)){
//Spam/Dos protection
......@@ -147,20 +139,20 @@ void ProcessMessageInstantX(CNode* pfrom, std::string& strCommand, CDataStream&
This tracks those messages and allows it at the same rate of the rest of the network, if
a peer violates it, it will simply be ignored
*/
if(!mapTxLockReq.count(ctx.txHash) && !mapTxLockReqRejected.count(ctx.txHash)){
if(!mapUnknownVotes.count(ctx.vinMasternode.prevout.hash)){
mapUnknownVotes[ctx.vinMasternode.prevout.hash] = GetTime()+(60*10);
if(!m_txLockReq.count(ctx.txHash) && !m_txLockReqRejected.count(ctx.txHash)){
if(!m_unknownVotes.count(ctx.vinMasternode.prevout.hash)){
m_unknownVotes[ctx.vinMasternode.prevout.hash] = GetTime()+(60*10);
}
if(mapUnknownVotes[ctx.vinMasternode.prevout.hash] > GetTime() &&
mapUnknownVotes[ctx.vinMasternode.prevout.hash] - GetAverageVoteTime() > 60*10){
if(m_unknownVotes[ctx.vinMasternode.prevout.hash] > GetTime() &&
m_unknownVotes[ctx.vinMasternode.prevout.hash] - GetAverageVoteTime() > 60*10){
IXLogPrintf("ProcessMessageInstantX::ix - masternode is spamming transaction votes: %s %s\n",
ctx.vinMasternode.ToString().c_str(),
ctx.txHash.ToString().c_str()
);
return;
} else {
mapUnknownVotes[ctx.vinMasternode.prevout.hash] = GetTime()+(60*10);
m_unknownVotes[ctx.vinMasternode.prevout.hash] = GetTime()+(60*10);
}
}
RelayInv(inv);
......@@ -168,9 +160,21 @@ void ProcessMessageInstantX(CNode* pfrom, std::string& strCommand, CDataStream&
return;
}
else if (strCommand == "txllist") //Get InstantX Locked list
{
std::map<uint256, CConsensusVote>::const_iterator it = m_txLockVote.begin();
for (; it != m_txLockVote.end(); ++it)
{
CInv inv(MSG_TXLOCK_VOTE, it->second.GetHash());
pfrom->AddInventoryKnown(inv);
RelayInv(inv);
}
}
}
bool IsIXTXValid(const CTransaction& txCollateral){
bool InstantSend::IsIxTxValid(const CTransaction& txCollateral) const
{
if(txCollateral.vout.size() < 1) return false;
if(txCollateral.nLockTime != 0) return false;
......@@ -194,12 +198,12 @@ bool IsIXTXValid(const CTransaction& txCollateral){
}
if(nValueOut > GetSporkValue(SPORK_5_MAX_VALUE)*COIN){
LogPrint("instantx", "IsIXTXValid - Transaction value too high - %s\n", txCollateral.ToString().c_str());
LogPrint("instantx", "IsIxTxValid - Transaction value too high - %s\n", txCollateral.ToString().c_str());
return false;
}
if(missingTx){
LogPrint("instantx", "IsIXTXValid - Unknown inputs in IX transaction - %s\n", txCollateral.ToString().c_str());
LogPrint("instantx", "IsIxTxValid - Unknown inputs in IX transaction - %s\n", txCollateral.ToString().c_str());
/*
This happens sometimes for an unknown reason, so we'll return that it's a valid transaction.
If someone submits an invalid transaction it will be rejected by the network anyway and this isn't
......@@ -211,7 +215,7 @@ bool IsIXTXValid(const CTransaction& txCollateral){
return true;
}
int64_t CreateNewLock(CTransaction tx)
int64_t InstantSend::CreateNewLock(const CTransaction& tx)
{
int64_t nTxAge = 0;
BOOST_REVERSE_FOREACH(CTxIn i, tx.vin){
......@@ -230,7 +234,7 @@ int64_t CreateNewLock(CTransaction tx)
*/
int nBlockHeight = (chainActive.Tip()->nHeight - nTxAge)+4;
if (!mapTxLocks.count(tx.GetHash())){
if (!m_txLocks.count(tx.GetHash())){
IXLogPrintf("CreateNewLock - New Transaction Lock %s !\n", tx.GetHash().ToString().c_str());
CTransactionLock newLock;
......@@ -238,17 +242,18 @@ int64_t CreateNewLock(CTransaction tx)
newLock.nExpiration = GetTime() + (60 * 60); //locks expire after 60 minutes (24 confirmations)
newLock.nTimeout = GetTime() + (60 * 5);
newLock.txHash = tx.GetHash();
mapTxLocks.insert(make_pair(tx.GetHash(), newLock));
m_txLocks.insert(make_pair(tx.GetHash(), newLock));
} else {
mapTxLocks[tx.GetHash()].nBlockHeight = nBlockHeight;
m_txLocks[tx.GetHash()].nBlockHeight = nBlockHeight;
LogPrint("instantx", "CreateNewLock - Transaction Lock Exists %s !\n", tx.GetHash().ToString().c_str());
}
m_txLockReq.insert(make_pair(tx.GetHash(), tx));
return nBlockHeight;
}
// check if we need to vote on this transaction
void DoConsensusVote(CTransaction& tx, int64_t nBlockHeight)
void InstantSend::DoConsensusVote(const CTransaction& tx, int64_t nBlockHeight)
{
if(!fMasterNode) return;
......@@ -284,14 +289,14 @@ void DoConsensusVote(CTransaction& tx, int64_t nBlockHeight)
return;
}
mapTxLockVote[ctx.GetHash()] = ctx;
m_txLockVote[ctx.GetHash()] = ctx;
CInv inv(MSG_TXLOCK_VOTE, ctx.GetHash());
RelayInv(inv);
}
//received a consensus vote
bool ProcessConsensusVote(CNode* pnode, CConsensusVote& ctx)
bool InstantSend::ProcessConsensusVote(CNode* pnode, const CConsensusVote& ctx)
{
int n = mnodeman.GetMasternodeRank(ctx.vinMasternode, ctx.nBlockHeight, MIN_INSTANTX_PROTO_VERSION);
......@@ -320,7 +325,7 @@ bool ProcessConsensusVote(CNode* pnode, CConsensusVote& ctx)
return false;
}
if (!mapTxLocks.count(ctx.txHash)){
if (!m_txLocks.count(ctx.txHash)){
IXLogPrintf("InstantX::ProcessConsensusVote - New Transaction Lock %s !\n", ctx.txHash.ToString().c_str());
CTransactionLock newLock;
......@@ -328,13 +333,13 @@ bool ProcessConsensusVote(CNode* pnode, CConsensusVote& ctx)
newLock.nExpiration = GetTime()+(60*60);
newLock.nTimeout = GetTime()+(60*5);
newLock.txHash = ctx.txHash;
mapTxLocks.insert(make_pair(ctx.txHash, newLock));
m_txLocks.insert(make_pair(ctx.txHash, newLock));
} else
LogPrint("instantx", "InstantX::ProcessConsensusVote - Transaction Lock Exists %s !\n", ctx.txHash.ToString().c_str());
//compile consessus vote
std::map<uint256, CTransactionLock>::iterator i = mapTxLocks.find(ctx.txHash);
if (i != mapTxLocks.end()){
std::map<uint256, CTransactionLock>::iterator i = m_txLocks.find(ctx.txHash);
if (i != m_txLocks.end()){
(*i).second.AddSignature(ctx);
#ifdef ENABLE_WALLET
......@@ -351,21 +356,21 @@ bool ProcessConsensusVote(CNode* pnode, CConsensusVote& ctx)
IXLogPrintf("InstantX::ProcessConsensusVote - Transaction Lock Is Complete \n");
LogPrint("instantx", "InstantX::ProcessConsensusVote - Transaction Lock Is Complete %s !\n", (*i).second.GetHash().ToString().c_str());
CTransaction& tx = mapTxLockReq[ctx.txHash];
CTransaction& tx = m_txLockReq[ctx.txHash];
if(!CheckForConflictingLocks(tx)){
#ifdef ENABLE_WALLET
if(pwalletMain){
if(pwalletMain->UpdatedTransaction((*i).second.txHash)){
nCompleteTXLocks++;
m_completeTxLocks++;
}
}
#endif
if(mapTxLockReq.count(ctx.txHash)){
if(m_txLockReq.count(ctx.txHash)){
BOOST_FOREACH(const CTxIn& in, tx.vin){
if(!mapLockedInputs.count(in.prevout)){
mapLockedInputs.insert(make_pair(in.prevout, ctx.txHash));
if(!m_lockedInputs.count(in.prevout)){
m_lockedInputs.insert(make_pair(in.prevout, ctx.txHash));
}
}
}
......@@ -373,7 +378,7 @@ bool ProcessConsensusVote(CNode* pnode, CConsensusVote& ctx)
// resolve conflicts
//if this tx lock was rejected, we need to remove the conflicting blocks
if(mapTxLockReqRejected.count((*i).second.txHash)){
if(m_txLockReqRejected.count((*i).second.txHash)){
//reprocess the last 15 blocks
ReprocessBlocks(15);
}
......@@ -386,7 +391,7 @@ bool ProcessConsensusVote(CNode* pnode, CConsensusVote& ctx)
return false;
}
bool CheckForConflictingLocks(CTransaction& tx)
bool InstantSend::CheckForConflictingLocks(const CTransaction& tx)
{
/*
It's possible (very unlikely though) to get 2 conflicting transaction locks approved by the network.
......@@ -396,11 +401,11 @@ bool CheckForConflictingLocks(CTransaction& tx)
rescan the blocks and find they're acceptable and then take the chain with the most work.
*/
BOOST_FOREACH(const CTxIn& in, tx.vin){
if(mapLockedInputs.count(in.prevout)){
if(mapLockedInputs[in.prevout] != tx.GetHash()){
IXLogPrintf("InstantX::CheckForConflictingLocks - found two complete conflicting locks - removing both. %s %s", tx.GetHash().ToString().c_str(), mapLockedInputs[in.prevout].ToString().c_str());
if(mapTxLocks.count(tx.GetHash())) mapTxLocks[tx.GetHash()].nExpiration = GetTime();
if(mapTxLocks.count(mapLockedInputs[in.prevout])) mapTxLocks[mapLockedInputs[in.prevout]].nExpiration = GetTime();
if(m_lockedInputs.count(in.prevout)){
if(m_lockedInputs[in.prevout] != tx.GetHash()){
IXLogPrintf("InstantX::CheckForConflictingLocks - found two complete conflicting locks - removing both. %s %s", tx.GetHash().ToString().c_str(), m_lockedInputs[in.prevout].ToString().c_str());
if(m_txLocks.count(tx.GetHash())) m_txLocks[tx.GetHash()].nExpiration = GetTime();
if(m_txLocks.count(m_lockedInputs[in.prevout])) m_txLocks[m_lockedInputs[in.prevout]].nExpiration = GetTime();
return true;
}
}
......@@ -409,13 +414,13 @@ bool CheckForConflictingLocks(CTransaction& tx)
return false;
}
int64_t GetAverageVoteTime()
int64_t InstantSend::GetAverageVoteTime() const
{
std::map<uint256, int64_t>::iterator it = mapUnknownVotes.begin();
std::map<uint256, int64_t>::const_iterator it = m_unknownVotes.begin();
int64_t total = 0;
int64_t count = 0;
while(it != mapUnknownVotes.end()) {
while(it != m_unknownVotes.end()) {
total+= it->second;
count++;
it++;
......@@ -424,30 +429,30 @@ int64_t GetAverageVoteTime()
return total / count;
}
void CleanTransactionLocksList()
void InstantSend::CheckAndRemove()
{
if(chainActive.Tip() == NULL) return;
std::map<uint256, CTransactionLock>::iterator it = mapTxLocks.begin();
std::map<uint256, CTransactionLock>::iterator it = m_txLocks.begin();
while(it != mapTxLocks.end()) {
while(it != m_txLocks.end()) {
if(GetTime() > it->second.nExpiration){ //keep them for an hour
IXLogPrintf("Removing old transaction lock %s\n", it->second.txHash.ToString().c_str());
if(mapTxLockReq.count(it->second.txHash)){
CTransaction& tx = mapTxLockReq[it->second.txHash];
if(m_txLockReq.count(it->second.txHash)){
CTransaction& tx = m_txLockReq[it->second.txHash];
BOOST_FOREACH(const CTxIn& in, tx.vin)
mapLockedInputs.erase(in.prevout);
m_lockedInputs.erase(in.prevout);
mapTxLockReq.erase(it->second.txHash);
mapTxLockReqRejected.erase(it->second.txHash);
m_txLockReq.erase(it->second.txHash);
m_txLockReqRejected.erase(it->second.txHash);
BOOST_FOREACH(CConsensusVote& v, it->second.vecConsensusVotes)
mapTxLockVote.erase(v.GetHash());
m_txLockVote.erase(v.GetHash());
}
mapTxLocks.erase(it++);
m_txLocks.erase(it++);
} else {
it++;
}
......@@ -455,13 +460,92 @@ void CleanTransactionLocksList()
}
int InstantSend::GetSignaturesCount(uint256 txHash) const
{
std::map<uint256, CTransactionLock>::const_iterator i = m_txLocks.find(txHash);
if (i != m_txLocks.end()) {
return (*i).second.CountSignatures();
}
return -1;
}
bool InstantSend::IsLockTimedOut(uint256 txHash) const
{
std::map<uint256, CTransactionLock>::const_iterator i = m_txLocks.find(txHash);
if (i != m_txLocks.end()) {
return GetTime() > (*i).second.nTimeout;
}
return false;
}
bool InstantSend::TxLockRequested(uint256 txHash) const
{
return m_txLockReq.count(txHash) || m_txLockReqRejected.count(txHash);
}
boost::optional<uint256> InstantSend::GetLockedTx(const COutPoint& out) const
{
std::map<COutPoint, uint256>::const_iterator it = m_lockedInputs.find(out);
if (it != m_lockedInputs.end())
return boost::optional<uint256>(it->second);
return boost::optional<uint256>();
}
boost::optional<CConsensusVote> InstantSend::GetLockVote(uint256 txHash) const
{
std::map<uint256, CConsensusVote>::const_iterator it = m_txLockVote.find(txHash);
if (it != m_txLockVote.end())
return boost::optional<CConsensusVote>(it->second);
return boost::optional<CConsensusVote>();
}
boost::optional<CTransaction> InstantSend::GetLockReq(uint256 txHash) const
{
std::map<uint256, CTransaction>::const_iterator it = m_txLockReq.find(txHash);
if (it != m_txLockReq.end())
return boost::optional<CTransaction>(it->second);
return boost::optional<CTransaction>();
}
bool InstantSend::AlreadyHave(uint256 txHash) const
{
return m_txLockVote.find(txHash) != m_txLockVote.end();
}
std::string InstantSend::ToString() const
{
std::ostringstream info;
info << "Transaction lock requests: " << m_txLockReq.size() <<
", Transaction locks: " << m_txLocks.size() <<
", Locked Inputs: " << m_lockedInputs.size() <<
", Transaction lock votes: " << m_txLockVote.size();
return info.str();
}
void InstantSend::Clear()
{
m_lockedInputs.clear();
m_txLockVote.clear();
m_txLockReq.clear();
m_txLocks.clear();
m_unknownVotes.clear();
m_txLockReqRejected.clear();
}
int InstantSend::GetCompleteLocksCount() const
{
return m_completeTxLocks;
}
uint256 CConsensusVote::GetHash() const
{
return ArithToUint256(UintToArith256(vinMasternode.prevout.hash) + vinMasternode.prevout.n + UintToArith256(txHash));
}
bool CConsensusVote::SignatureValid()
bool CConsensusVote::SignatureValid() const
{
std::string errorMessage;
std::string strMessage = txHash.ToString().c_str() + boost::lexical_cast<std::string>(nBlockHeight);
......@@ -513,7 +597,7 @@ bool CConsensusVote::Sign()
}
bool CTransactionLock::SignaturesValid()
bool CTransactionLock::SignaturesValid() const
{
BOOST_FOREACH(CConsensusVote vote, vecConsensusVotes)
......@@ -541,12 +625,12 @@ bool CTransactionLock::SignaturesValid()
return true;
}
void CTransactionLock::AddSignature(CConsensusVote& cv)
void CTransactionLock::AddSignature(const CConsensusVote& cv)
{
vecConsensusVotes.push_back(cv);
}
int CTransactionLock::CountSignatures()
int CTransactionLock::CountSignatures() const
{
/*
Only count signatures where the BlockHeight matches the transaction's blockheight.
......@@ -563,3 +647,17 @@ int CTransactionLock::CountSignatures()
}
return n;
}
uint256 CTransactionLock::GetHash() const
{
return txHash;
}
InstantSend& GetInstantSend()
{
if (g_instantSend.get() == NULL)
{
g_instantSend.reset(new InstantSend);
}
return *g_instantSend;
}
......@@ -25,54 +25,68 @@
#define INSTANTX_SIGNATURES_REQUIRED 6
#define INSTANTX_SIGNATURES_TOTAL 10
using namespace std;
using namespace boost;
class CConsensusVote;
class CTransaction;
class CTransactionLock;
class InstantSend;
static const int MIN_INSTANTX_PROTO_VERSION = 70040;
extern map<uint256, CTransaction> mapTxLockReq;
extern map<uint256, CTransaction> mapTxLockReqRejected;
extern map<uint256, CConsensusVote> mapTxLockVote;
extern map<uint256, CTransactionLock> mapTxLocks;
extern std::map<COutPoint, uint256> mapLockedInputs;
extern int nCompleteTXLocks;
int64_t CreateNewLock(CTransaction tx);
bool IsIXTXValid(const CTransaction& txCollateral);
// if two conflicting locks are approved by the network, they will cancel out
bool CheckForConflictingLocks(CTransaction& tx);
extern std::auto_ptr<InstantSend> g_instantSend;
void ProcessMessageInstantX(CNode* pfrom, std::string& strCommand, CDataStream& vRecv);
InstantSend& GetInstantSend();
//check if we need to vote on this transaction
void DoConsensusVote(CTransaction& tx, int64_t nBlockHeight);
class InstantSend
{
public:
void ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv);
void CheckAndRemove();
void Clear();
int64_t CreateNewLock(const CTransaction& tx);
int GetSignaturesCount(uint256 txHash) const;
int GetCompleteLocksCount() const;
bool IsLockTimedOut(uint256 txHash) const;
bool TxLockRequested(uint256 txHash) const;
bool AlreadyHave(uint256 txHash) const;
std::string ToString() const;
boost::optional<uint256> GetLockedTx(const COutPoint& out) const;
boost::optional<CConsensusVote> GetLockVote(uint256 txHash) const;
boost::optional<CTransaction> GetLockReq(uint256 txHash) const;
//process consensus vote message
bool ProcessConsensusVote(CNode *pnode, CConsensusVote& ctx);
ADD_SERIALIZE_METHODS;
// keep transaction locks in memory for an hour
void CleanTransactionLocksList();
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(m_lockedInputs);
READWRITE(m_txLockVote);
READWRITE(m_txLockReq);
READWRITE(m_txLocks);
READWRITE(m_unknownVotes);
READWRITE(m_txLockReqRejected);
READWRITE(m_completeTxLocks);
}
int64_t GetAverageVoteTime();
private:
void DoConsensusVote(const CTransaction& tx, int64_t nBlockHeight);
bool IsIxTxValid(const CTransaction& txCollateral) const;
bool ProcessConsensusVote(CNode* pnode, const CConsensusVote& ctx);
bool CheckForConflictingLocks(const CTransaction& tx);
int64_t GetAverageVoteTime() const;
private:
std::map<COutPoint, uint256> m_lockedInputs;
std::map<uint256, CConsensusVote> m_txLockVote;
std::map<uint256, CTransaction> m_txLockReq;
std::map<uint256, CTransactionLock> m_txLocks;
std::map<uint256, int64_t> m_unknownVotes; //track votes with no tx for DOS
std::map<uint256, CTransaction> m_txLockReqRejected;
int m_completeTxLocks;
};
class CConsensusVote
{
public:
CTxIn vinMasternode;
uint256 txHash;
int nBlockHeight;
std::vector<unsigned char> vchMasterNodeSignature;
uint256 GetHash() const;
bool SignatureValid();
bool SignatureValid() const;
bool Sign();
ADD_SERIALIZE_METHODS;
......@@ -84,26 +98,39 @@ public:
READWRITE(vchMasterNodeSignature);
READWRITE(nBlockHeight);
}
public:
CTxIn vinMasternode;
uint256 txHash;
int nBlockHeight;
std::vector<unsigned char> vchMasterNodeSignature;
};
class CTransactionLock
{
public:
bool SignaturesValid() const;
int CountSignatures() const;
void AddSignature(const CConsensusVote& cv);
uint256 GetHash() const;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(nBlockHeight);
READWRITE(txHash);
READWRITE(vecConsensusVotes);
READWRITE(nExpiration);
READWRITE(nTimeout);
}
public:
int nBlockHeight;
uint256 txHash;
std::vector<CConsensusVote> vecConsensusVotes;
int nExpiration;
int nTimeout;
bool SignaturesValid();
int CountSignatures();
void AddSignature(CConsensusVote& cv);
uint256 GetHash()
{
return txHash;
}
};
#endif
......@@ -139,8 +139,8 @@ void ThreadCheckLegacySigner()
{
mnodeman.CheckAndRemove();
mnodeman.ProcessMasternodeConnections();
masternodePayments.CleanPaymentList();
CleanTransactionLocksList();
masternodePayments.CheckAndRemove();
GetInstantSend().CheckAndRemove();
}
//if(c1 % MASTERNODES_DUMP_SECONDS == 0) DumpMasternodes();
......@@ -158,8 +158,8 @@ void ThreadCheckLegacySigner()
{
snodeman.CheckAndRemove();
snodeman.ProcessSystemnodeConnections();
systemnodePayments.CleanPaymentList();
CleanTransactionLocksList();
systemnodePayments.CheckAndRemove();
GetInstantSend().CheckAndRemove();
}
//if(c2 % SYSTEMNODES_DUMP_SECONDS == 0) DumpSystemnodes();
......
......@@ -898,15 +898,11 @@ int GetInputAge(const CTxIn& vin)
int GetInputAgeIX(uint256 nTXHash, const CTxIn& vin)
{
int sigs = 0;
int nResult = GetInputAge(vin);
if(nResult < 0) nResult = 0;
if (nResult < 6){
std::map<uint256, CTransactionLock>::iterator i = mapTxLocks.find(nTXHash);
if (i != mapTxLocks.end()){
sigs = (*i).second.CountSignatures();
}
int sigs = GetInstantSend().GetSignaturesCount(nTXHash);
if(sigs >= INSTANTX_SIGNATURES_REQUIRED){
return nInstantXDepth+nResult;
}
......@@ -917,12 +913,8 @@ int GetInputAgeIX(uint256 nTXHash, const CTxIn& vin)
int GetIXConfirmations(uint256 nTXHash)
{
int sigs = 0;
int sigs = GetInstantSend().GetSignaturesCount(nTXHash);
std::map<uint256, CTransactionLock>::iterator i = mapTxLocks.find(nTXHash);
if (i != mapTxLocks.end()){
sigs = (*i).second.CountSignatures();
}
if(sigs >= INSTANTX_SIGNATURES_REQUIRED){
return nInstantXDepth;
}
......@@ -1047,13 +1039,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// ----------- instantX transaction scanning -----------
BOOST_FOREACH(const CTxIn& in, tx.vin){
if(mapLockedInputs.count(in.prevout)){
if(mapLockedInputs[in.prevout] != tx.GetHash()){
return state.DoS(0,
error("AcceptToMemoryPool : conflicts with existing transaction lock: %s", reason),
REJECT_INVALID, "tx-lock-conflict");
}
BOOST_FOREACH(const CTxIn& in, tx.vin)
{
boost::optional<uint256> txHash = GetInstantSend().GetLockedTx(in.prevout);
if (txHash && txHash.get() != tx.GetHash())
{
return state.DoS(0, error("AcceptToMemoryPool : conflicts with existing transaction lock: %s", reason),
REJECT_INVALID, "tx-lock-conflict");
}
}
......@@ -1239,13 +1231,13 @@ bool AcceptableInputs(CTxMemPool& pool, CValidationState &state, const CTransact
// ----------- instantX transaction scanning -----------
BOOST_FOREACH(const CTxIn& in, tx.vin){
if(mapLockedInputs.count(in.prevout)){
if(mapLockedInputs[in.prevout] != tx.GetHash()){
return state.DoS(0,
error("AcceptableInputs : conflicts with existing transaction lock: %s", reason),
REJECT_INVALID, "tx-lock-conflict");
}
BOOST_FOREACH(const CTxIn& in, tx.vin)
{
boost::optional<uint256> txHash = GetInstantSend().GetLockedTx(in.prevout);
if (txHash && txHash.get() != tx.GetHash())
{
return state.DoS(0, error("AcceptableInputs : conflicts with existing transaction lock: %s", reason),
REJECT_INVALID, "tx-lock-conflict");
}
}
......@@ -3033,14 +3025,16 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo
BOOST_FOREACH(const CTransaction& tx, block.vtx){
if (!tx.IsCoinBase()){
//only reject blocks when it's based on complete consensus
BOOST_FOREACH(const CTxIn& in, tx.vin){
if(mapLockedInputs.count(in.prevout)){
if(mapLockedInputs[in.prevout] != tx.GetHash()){
mapRejectedBlocks.insert(make_pair(block.GetHash(), GetTime()));
LogPrintf("CheckBlock() : found conflicting transaction with transaction lock %s %s\n", mapLockedInputs[in.prevout].ToString(), tx.GetHash().ToString());
return state.DoS(0, error("CheckBlock() : found conflicting transaction with transaction lock"),
REJECT_INVALID, "conflicting-tx-ix");
}
BOOST_FOREACH(const CTxIn& in, tx.vin)
{
boost::optional<uint256> txHash = GetInstantSend().GetLockedTx(in.prevout);
if (txHash && txHash.get() != tx.GetHash())
{
mapRejectedBlocks.insert(make_pair(block.GetHash(), GetTime()));
LogPrintf("CheckBlock() : found conflicting transaction with transaction lock %s %s\n",
txHash.get().ToString(), tx.GetHash().ToString());
return state.DoS(0, error("CheckBlock() : found conflicting transaction with transaction lock"),
REJECT_INVALID, "conflicting-tx-ix");
}
}
}
......@@ -4065,10 +4059,9 @@ bool static AlreadyHave(const CInv& inv)
case MSG_BLOCK:
return mapBlockIndex.count(inv.hash);
case MSG_TXLOCK_REQUEST:
return mapTxLockReq.count(inv.hash) ||
mapTxLockReqRejected.count(inv.hash);
return GetInstantSend().TxLockRequested(inv.hash);
case MSG_TXLOCK_VOTE:
return mapTxLockVote.count(inv.hash);
return GetInstantSend().AlreadyHave(inv.hash);
case MSG_SPORK:
return mapSporks.count(inv.hash);
case MSG_MASTERNODE_WINNER:
......@@ -4234,19 +4227,23 @@ void static ProcessGetData(CNode* pfrom)
}
}
if (!pushed && inv.type == MSG_TXLOCK_VOTE) {
if(mapTxLockVote.count(inv.hash)){
boost::optional<CConsensusVote> vote = GetInstantSend().GetLockVote(inv.hash);
if (vote)
{
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss.reserve(1000);
ss << mapTxLockVote[inv.hash];
ss << vote.get();
pfrom->PushMessage("txlvote", ss);
pushed = true;
}
}
if (!pushed && inv.type == MSG_TXLOCK_REQUEST) {
if(mapTxLockReq.count(inv.hash)){
boost::optional<CTransaction> lockedTx = GetInstantSend().GetLockReq(inv.hash);
if (lockedTx)
{
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss.reserve(1000);
ss << mapTxLockReq[inv.hash];
ss << lockedTx.get();
pfrom->PushMessage("ix", ss);
pushed = true;
}
......@@ -5203,7 +5200,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
budget.ProcessMessage(pfrom, strCommand, vRecv);
masternodePayments.ProcessMessageMasternodePayments(pfrom, strCommand, vRecv);
systemnodePayments.ProcessMessageSystemnodePayments(pfrom, strCommand, vRecv);
ProcessMessageInstantX(pfrom, strCommand, vRecv);
GetInstantSend().ProcessMessage(pfrom, strCommand, vRecv);
ProcessSpork(pfrom, strCommand, vRecv);
masternodeSync.ProcessMessage(pfrom, strCommand, vRecv);
systemnodeSync.ProcessMessage(pfrom, strCommand, vRecv);
......
......@@ -269,177 +269,6 @@ void CBudgetManager::SubmitFinalBudget()
LogPrintf("CBudgetManager::SubmitFinalBudget - Done! %s\n", finalizedBudgetBroadcast.GetHash().ToString());
}
//
// CBudgetDB
//
CBudgetDB::CBudgetDB()
: pathDB(GetDataDir() / "budget.dat")
, strMagicMessage("MasternodeBudget")
{
}
CBudgetDB::CBudgetDB(const boost::filesystem::path& dbPath)
: pathDB(dbPath)
, strMagicMessage("MasternodeBudget")
{
}
bool CBudgetDB::Write(const CBudgetManager& objToSave)
{
LOCK(objToSave.cs);
int64_t nStart = GetTimeMillis();
// serialize, checksum data up to that point, then append checksum
CDataStream ssObj(SER_DISK, CLIENT_VERSION);
ssObj << strMagicMessage; // masternode cache file specific magic message
ssObj << FLATDATA(Params().MessageStart()); // network specific magic number
ssObj << objToSave;
uint256 hash = Hash(ssObj.begin(), ssObj.end());
ssObj << hash;
// open output file, and associate with CAutoFile
FILE *file = fopen(pathDB.string().c_str(), "wb");
CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
if (fileout.IsNull())
return error("%s : Failed to open file %s", __func__, pathDB.string());
// Write and commit header, data
try {
fileout << ssObj;
}
catch (std::exception &e) {
return error("%s : Serialize or I/O error - %s", __func__, e.what());
}
fileout.fclose();
LogPrintf("Written info to budget.dat %dms\n", GetTimeMillis() - nStart);
return true;
}
CBudgetDB::ReadResult CBudgetDB::Read(CBudgetManager& objToLoad, bool fDryRun)
{
LOCK(objToLoad.cs);
int64_t nStart = GetTimeMillis();
// open input file, and associate with CAutoFile
FILE *file = fopen(pathDB.string().c_str(), "rb");
CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
if (filein.IsNull())
{
error("%s : Failed to open file %s", __func__, pathDB.string());
return FileError;
}
// use file size to size memory buffer
int fileSize = boost::filesystem::file_size(pathDB);
int dataSize = fileSize - sizeof(uint256);
// Don't try to resize to a negative number if file is small
if (dataSize < 0)
dataSize = 0;
vector<unsigned char> vchData;
vchData.resize(dataSize);
uint256 hashIn;
// read data and checksum from file
try {
filein.read((char *)&vchData[0], dataSize);
filein >> hashIn;
}
catch (std::exception &e) {
error("%s : Deserialize or I/O error - %s", __func__, e.what());
return HashReadError;
}
filein.fclose();
CDataStream ssObj(vchData, SER_DISK, CLIENT_VERSION);
// verify stored checksum matches input data
uint256 hashTmp = Hash(ssObj.begin(), ssObj.end());
if (hashIn != hashTmp)
{
error("%s : Checksum mismatch, data corrupted", __func__);
return IncorrectHash;
}
unsigned char pchMsgTmp[4];
std::string strMagicMessageTmp;
try {
// de-serialize file header (masternode cache file specific magic message) and ..
ssObj >> strMagicMessageTmp;
// ... verify the message matches predefined one
if (strMagicMessage != strMagicMessageTmp)
{
error("%s : Invalid masternode cache magic message", __func__);
return IncorrectMagicMessage;
}
// de-serialize file header (network specific magic number) and ..
ssObj >> FLATDATA(pchMsgTmp);
// ... verify the network matches ours
if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
{
error("%s : Invalid network magic number", __func__);
return IncorrectMagicNumber;
}
// de-serialize data into CBudgetManager object
ssObj >> objToLoad;
}
catch (std::exception &e) {
objToLoad.Clear();
error("%s : Deserialize or I/O error - %s", __func__, e.what());
return IncorrectFormat;
}
LogPrintf("Loaded info from budget.dat %dms\n", GetTimeMillis() - nStart);
LogPrintf(" %s\n", objToLoad.ToString());
if(!fDryRun) {
LogPrintf("Budget manager - cleaning....\n");
objToLoad.CheckAndRemove();
LogPrintf("Budget manager - result:\n");
LogPrintf(" %s\n", objToLoad.ToString());
}
return Ok;
}
void DumpBudgets()
{
int64_t nStart = GetTimeMillis();
CBudgetDB budgetdb;
CBudgetManager tempBudget;
LogPrintf("Verifying budget.dat format...\n");
CBudgetDB::ReadResult readResult = budgetdb.Read(tempBudget, true);
// there was an error and it was not an error on file opening => do not proceed
if (readResult == CBudgetDB::FileError)
LogPrintf("Missing budgets file - budget.dat, will try to recreate\n");
else if (readResult != CBudgetDB::Ok)
{
LogPrintf("Error reading budget.dat: ");
if(readResult == CBudgetDB::IncorrectFormat)
LogPrintf("magic is ok but data has invalid format, will try to recreate\n");
else
{
LogPrintf("file format is unknown or invalid, please fix it manually\n");
return;
}
}
LogPrintf("Writting info to budget.dat...\n");
budgetdb.Write(budget);
LogPrintf("Budget dump finished %dms\n", GetTimeMillis() - nStart);
}
bool CBudgetManager::AddFinalizedBudget(CFinalizedBudget& finalizedBudget, bool checkCollateral)
{
std::string strError = "";
......
......@@ -43,7 +43,6 @@ extern std::vector<CBudgetProposalBroadcast> vecImmatureBudgetProposals;
extern std::vector<CFinalizedBudgetBroadcast> vecImmatureFinalizedBudgets;
extern CBudgetManager budget;
void DumpBudgets();
// Define amount of blocks in budget payment cycle
int GetBudgetPaymentCycleBlocks();
......@@ -51,32 +50,6 @@ int GetBudgetPaymentCycleBlocks();
//Check the collateral transaction for the budget proposal/finalized budget
bool IsBudgetCollateralValid(uint256 nTxCollateralHash, uint256 nExpectedHash, std::string& strError, int64_t& nTime, int& nConf);
/** Save Budget Manager (budget.dat)
*/
class CBudgetDB
{
private:
boost::filesystem::path pathDB;
std::string strMagicMessage;
public:
enum ReadResult {
Ok,
FileError,
HashReadError,
IncorrectHash,
IncorrectMagicMessage,
IncorrectMagicNumber,
IncorrectFormat
};
CBudgetDB();
CBudgetDB(const boost::filesystem::path& pathDb);
bool Write(const CBudgetManager &objToSave);
ReadResult Read(CBudgetManager& objToLoad, bool fDryRun = false);
};
//
// Budget Manager : Contains all proposals for the budget
//
......
......@@ -22,167 +22,6 @@ CCriticalSection cs_vecPayments;
CCriticalSection cs_mapMasternodeBlocks;
CCriticalSection cs_mapMasternodePayeeVotes;
//
// CMasternodePaymentDB
//
CMasternodePaymentDB::CMasternodePaymentDB()
{
pathDB = GetDataDir() / "mnpayments.dat";
strMagicMessage = "MasternodePayments";
}
bool CMasternodePaymentDB::Write(const CMasternodePayments& objToSave)
{
int64_t nStart = GetTimeMillis();
// serialize, checksum data up to that point, then append checksum
CDataStream ssObj(SER_DISK, CLIENT_VERSION);
ssObj << strMagicMessage; // masternode cache file specific magic message
ssObj << FLATDATA(Params().MessageStart()); // network specific magic number
ssObj << objToSave;
uint256 hash = Hash(ssObj.begin(), ssObj.end());
ssObj << hash;
// open output file, and associate with CAutoFile
FILE *file = fopen(pathDB.string().c_str(), "wb");
CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
if (fileout.IsNull())
return error("%s : Failed to open file %s", __func__, pathDB.string());
// Write and commit header, data
try {
fileout << ssObj;
}
catch (std::exception &e) {
return error("%s : Serialize or I/O error - %s", __func__, e.what());
}
fileout.fclose();
LogPrintf("Written info to mnpayments.dat %dms\n", GetTimeMillis() - nStart);
return true;
}
CMasternodePaymentDB::ReadResult CMasternodePaymentDB::Read(CMasternodePayments& objToLoad, bool fDryRun)
{
int64_t nStart = GetTimeMillis();
// open input file, and associate with CAutoFile
FILE *file = fopen(pathDB.string().c_str(), "rb");
CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
if (filein.IsNull())
{
error("%s : Failed to open file %s", __func__, pathDB.string());
return FileError;
}
// use file size to size memory buffer
int fileSize = boost::filesystem::file_size(pathDB);
int dataSize = fileSize - sizeof(uint256);
// Don't try to resize to a negative number if file is small
if (dataSize < 0)
dataSize = 0;
vector<unsigned char> vchData;
vchData.resize(dataSize);
uint256 hashIn;
// read data and checksum from file
try {
filein.read((char *)&vchData[0], dataSize);
filein >> hashIn;
}
catch (std::exception &e) {
error("%s : Deserialize or I/O error - %s", __func__, e.what());
return HashReadError;
}
filein.fclose();
CDataStream ssObj(vchData, SER_DISK, CLIENT_VERSION);
// verify stored checksum matches input data
uint256 hashTmp = Hash(ssObj.begin(), ssObj.end());
if (hashIn != hashTmp)
{
error("%s : Checksum mismatch, data corrupted", __func__);
return IncorrectHash;
}
unsigned char pchMsgTmp[4];
std::string strMagicMessageTmp;
try {
// de-serialize file header (masternode cache file specific magic message) and ..
ssObj >> strMagicMessageTmp;
// ... verify the message matches predefined one
if (strMagicMessage != strMagicMessageTmp)
{
error("%s : Invalid masternode payement cache magic message", __func__);
return IncorrectMagicMessage;
}
// de-serialize file header (network specific magic number) and ..
ssObj >> FLATDATA(pchMsgTmp);
// ... verify the network matches ours
if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
{
error("%s : Invalid network magic number", __func__);
return IncorrectMagicNumber;
}
// de-serialize data into CMasternodePayments object
ssObj >> objToLoad;
}
catch (std::exception &e) {
objToLoad.Clear();
error("%s : Deserialize or I/O error - %s", __func__, e.what());
return IncorrectFormat;
}
LogPrintf("Loaded info from mnpayments.dat %dms\n", GetTimeMillis() - nStart);
LogPrintf(" %s\n", objToLoad.ToString());
if(!fDryRun) {
LogPrintf("Masternode payments manager - cleaning....\n");
objToLoad.CleanPaymentList();
LogPrintf("Masternode payments manager - result:\n");
LogPrintf(" %s\n", objToLoad.ToString());
}
return Ok;
}
void DumpMasternodePayments()
{
int64_t nStart = GetTimeMillis();
CMasternodePaymentDB paymentdb;
CMasternodePayments tempPayments;
LogPrintf("Verifying mnpayments.dat format...\n");
CMasternodePaymentDB::ReadResult readResult = paymentdb.Read(tempPayments, true);
// there was an error and it was not an error on file opening => do not proceed
if (readResult == CMasternodePaymentDB::FileError)
LogPrintf("Missing budgets file - mnpayments.dat, will try to recreate\n");
else if (readResult != CMasternodePaymentDB::Ok)
{
LogPrintf("Error reading mnpayments.dat: ");
if(readResult == CMasternodePaymentDB::IncorrectFormat)
LogPrintf("magic is ok but data has invalid format, will try to recreate\n");
else
{
LogPrintf("file format is unknown or invalid, please fix it manually\n");
return;
}
}
LogPrintf("Writting info to mnpayments.dat...\n");
paymentdb.Write(masternodePayments);
LogPrintf("Budget dump finished %dms\n", GetTimeMillis() - nStart);
}
bool IsBlockValueValid(const CBlock& block, int64_t nExpectedValue){
CBlockIndex* pindexPrev = chainActive.Tip();
if(pindexPrev == NULL) return true;
......@@ -613,7 +452,7 @@ bool CMasternodePayments::IsTransactionValid(const CTransaction& txNew, int nBlo
return true;
}
void CMasternodePayments::CleanPaymentList()
void CMasternodePayments::CheckAndRemove()
{
LOCK2(cs_mapMasternodePayeeVotes, cs_mapMasternodeBlocks);
......
......@@ -33,31 +33,6 @@ std::string GetRequiredPaymentsString(int nBlockHeight);
bool IsBlockValueValid(const CBlock& block, int64_t nExpectedValue);
void FillBlockPayee(CMutableTransaction& txNew, int64_t nFees);
void DumpMasternodePayments();
/** Save Masternode Payment Data (mnpayments.dat)
*/
class CMasternodePaymentDB
{
private:
boost::filesystem::path pathDB;
std::string strMagicMessage;
public:
enum ReadResult {
Ok,
FileError,
HashReadError,
IncorrectHash,
IncorrectMagicMessage,
IncorrectMagicNumber,
IncorrectFormat
};
CMasternodePaymentDB();
bool Write(const CMasternodePayments &objToSave);
ReadResult Read(CMasternodePayments& objToLoad, bool fDryRun = false);
};
class CMasternodePayee
{
public:
......@@ -244,7 +219,7 @@ public:
bool ProcessBlock(int nBlockHeight);
void Sync(CNode* node, int nCountNeeded);
void CleanPaymentList();
void CheckAndRemove();
int LastPayment(CMasternode& mn);
bool GetBlockPayee(int nBlockHeight, CScript& payee);
......
......@@ -44,166 +44,6 @@ struct CompareScoreMN
}
};
//
// CMasternodeDB
//
CMasternodeDB::CMasternodeDB()
{
pathMN = GetDataDir() / "mncache.dat";
strMagicMessage = "MasternodeCache";
}
bool CMasternodeDB::Write(const CMasternodeMan& mnodemanToSave)
{
int64_t nStart = GetTimeMillis();
// serialize, checksum data up to that point, then append checksum
CDataStream ssMasternodes(SER_DISK, CLIENT_VERSION);
ssMasternodes << strMagicMessage; // masternode cache file specific magic message
ssMasternodes << FLATDATA(Params().MessageStart()); // network specific magic number
ssMasternodes << mnodemanToSave;
uint256 hash = Hash(ssMasternodes.begin(), ssMasternodes.end());
ssMasternodes << hash;
// open output file, and associate with CAutoFile
FILE *file = fopen(pathMN.string().c_str(), "wb");
CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
if (fileout.IsNull())
return error("%s : Failed to open file %s", __func__, pathMN.string());
// Write and commit header, data
try {
fileout << ssMasternodes;
}
catch (std::exception &e) {
return error("%s : Serialize or I/O error - %s", __func__, e.what());
}
// FileCommit(fileout);
fileout.fclose();
LogPrintf("Written info to mncache.dat %dms\n", GetTimeMillis() - nStart);
LogPrintf(" %s\n", mnodemanToSave.ToString());
return true;
}
CMasternodeDB::ReadResult CMasternodeDB::Read(CMasternodeMan& mnodemanToLoad, bool fDryRun)
{
int64_t nStart = GetTimeMillis();
// open input file, and associate with CAutoFile
FILE *file = fopen(pathMN.string().c_str(), "rb");
CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
if (filein.IsNull())
{
error("%s : Failed to open file %s", __func__, pathMN.string());
return FileError;
}
// use file size to size memory buffer
int fileSize = boost::filesystem::file_size(pathMN);
int dataSize = fileSize - sizeof(uint256);
// Don't try to resize to a negative number if file is small
if (dataSize < 0)
dataSize = 0;
vector<unsigned char> vchData;
vchData.resize(dataSize);
uint256 hashIn;
// read data and checksum from file
try {
filein.read((char *)&vchData[0], dataSize);
filein >> hashIn;
}
catch (std::exception &e) {
error("%s : Deserialize or I/O error - %s", __func__, e.what());
return HashReadError;
}
filein.fclose();
CDataStream ssMasternodes(vchData, SER_DISK, CLIENT_VERSION);
// verify stored checksum matches input data
uint256 hashTmp = Hash(ssMasternodes.begin(), ssMasternodes.end());
if (hashIn != hashTmp)
{
error("%s : Checksum mismatch, data corrupted", __func__);
return IncorrectHash;
}
unsigned char pchMsgTmp[4];
std::string strMagicMessageTmp;
try {
// de-serialize file header (masternode cache file specific magic message) and ..
ssMasternodes >> strMagicMessageTmp;
// ... verify the message matches predefined one
if (strMagicMessage != strMagicMessageTmp)
{
error("%s : Invalid masternode cache magic message", __func__);
return IncorrectMagicMessage;
}
// de-serialize file header (network specific magic number) and ..
ssMasternodes >> FLATDATA(pchMsgTmp);
// ... verify the network matches ours
if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
{
error("%s : Invalid network magic number", __func__);
return IncorrectMagicNumber;
}
// de-serialize data into CMasternodeMan object
ssMasternodes >> mnodemanToLoad;
}
catch (std::exception &e) {
mnodemanToLoad.Clear();
error("%s : Deserialize or I/O error - %s", __func__, e.what());
return IncorrectFormat;
}
LogPrintf("Loaded info from mncache.dat %dms\n", GetTimeMillis() - nStart);
LogPrintf(" %s\n", mnodemanToLoad.ToString());
if(!fDryRun) {
LogPrintf("Masternode manager - cleaning....\n");
mnodemanToLoad.CheckAndRemove(true);
LogPrintf("Masternode manager - result:\n");
LogPrintf(" %s\n", mnodemanToLoad.ToString());
}
return Ok;
}
void DumpMasternodes()
{
int64_t nStart = GetTimeMillis();
CMasternodeDB mndb;
CMasternodeMan tempMnodeman;
LogPrintf("Verifying mncache.dat format...\n");
CMasternodeDB::ReadResult readResult = mndb.Read(tempMnodeman, true);
// there was an error and it was not an error on file opening => do not proceed
if (readResult == CMasternodeDB::FileError)
LogPrintf("Missing masternode cache file - mncache.dat, will try to recreate\n");
else if (readResult != CMasternodeDB::Ok)
{
LogPrintf("Error reading mncache.dat: ");
if(readResult == CMasternodeDB::IncorrectFormat)
LogPrintf("magic is ok but data has invalid format, will try to recreate\n");
else
{
LogPrintf("file format is unknown or invalid, please fix it manually\n");
return;
}
}
LogPrintf("Writting info to mncache.dat...\n");
mndb.Write(mnodeman);
LogPrintf("Masternode dump finished %dms\n", GetTimeMillis() - nStart);
}
CMasternodeMan::CMasternodeMan() {
nDsqCount = 0;
}
......@@ -226,7 +66,7 @@ bool CMasternodeMan::Add(const CMasternode &mn)
return false;
}
void CMasternodeMan::AskForMN(CNode* pnode, CTxIn &vin)
void CMasternodeMan::AskForMN(CNode* pnode, const CTxIn &vin)
{
std::map<COutPoint, int64_t>::iterator i = mWeAskedForMasternodeListEntry.find(vin.prevout);
if (i != mWeAskedForMasternodeListEntry.end())
......
......@@ -24,29 +24,6 @@ class CMasternodeMan;
extern CMasternodeMan mnodeman;
void DumpMasternodes();
/** Access to the MN database (mncache.dat)
*/
class CMasternodeDB
{
private:
boost::filesystem::path pathMN;
std::string strMagicMessage;
public:
enum ReadResult {
Ok,
FileError,
HashReadError,
IncorrectHash,
IncorrectMagicMessage,
IncorrectMagicNumber,
IncorrectFormat
};
CMasternodeDB();
bool Write(const CMasternodeMan &mnodemanToSave);
ReadResult Read(CMasternodeMan& mnodemanToLoad, bool fDryRun = false);
};
class CMasternodeMan
{
private:
......@@ -96,7 +73,7 @@ public:
bool Add(const CMasternode &mn);
/// Ask (source) node for mnb
void AskForMN(CNode *pnode, CTxIn &vin);
void AskForMN(CNode *pnode, const CTxIn &vin);
/// Check all Masternodes
void Check();
......
......@@ -467,6 +467,11 @@ void CNode::CloseSocketDisconnect()
vRecvMsg.clear();
}
void CNode::PushTxLockedList()
{
PushMessage("txllist");
}
void CNode::PushVersion()
{
int nBestHeight = g_signals.GetHeight().get_value_or(0);
......@@ -2069,7 +2074,10 @@ CNode::CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn, bool fIn
// Be shy and don't send version until we hear
if (hSocket != INVALID_SOCKET && !fInbound)
{
PushVersion();
PushTxLockedList();
}
GetNodeSignals().InitializeNode(GetId(), this);
}
......
......@@ -422,6 +422,7 @@ public:
void EndMessage() UNLOCK_FUNCTION(cs_vSend);
void PushVersion();
void PushTxLockedList();
void PushMessage(const char* pszCommand)
......
......@@ -188,8 +188,8 @@ void OverviewPage::setBalance(const CAmount& balance, const CAmount& unconfirmed
static int cachedTxLocks = 0;
if(cachedTxLocks != nCompleteTXLocks){
cachedTxLocks = nCompleteTXLocks;
if(cachedTxLocks != GetInstantSend().GetCompleteLocksCount()){
cachedTxLocks = GetInstantSend().GetCompleteLocksCount();
ui->listTransactions->update();
}
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment