Merge pull request #749 from UdjinM6/fixix_ixnotify

Refactor/fix IX & Implement ixnotify
This commit is contained in:
evan82 2016-04-05 10:50:06 -07:00
commit fa0503d89b
3 changed files with 93 additions and 52 deletions

View File

@ -558,6 +558,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageGroup(_("InstantX options:")); strUsage += HelpMessageGroup(_("InstantX options:"));
strUsage += HelpMessageOpt("-enableinstantx=<n>", strprintf(_("Enable instantx, show confirmations for locked transactions (0-1, default: %u)"), fEnableInstantX)); strUsage += HelpMessageOpt("-enableinstantx=<n>", strprintf(_("Enable instantx, show confirmations for locked transactions (0-1, default: %u)"), fEnableInstantX));
strUsage += HelpMessageOpt("-instantxdepth=<n>", strprintf(_("Show N confirmations for a successfully locked transaction (0-9999, default: %u)"), nInstantXDepth)); strUsage += HelpMessageOpt("-instantxdepth=<n>", strprintf(_("Show N confirmations for a successfully locked transaction (0-9999, default: %u)"), nInstantXDepth));
strUsage += HelpMessageOpt("-ixnotify=<cmd>", _("Execute command when a wallet IX transaction is successfully locked (%s in cmd is replaced by TxID)"));
strUsage += HelpMessageGroup(_("Node relay options:")); strUsage += HelpMessageGroup(_("Node relay options:"));

View File

@ -15,7 +15,10 @@
#include "masternode-sync.h" #include "masternode-sync.h"
#include "masternodeman.h" #include "masternodeman.h"
#include "spork.h" #include "spork.h"
#include <boost/algorithm/string/replace.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>
using namespace std; using namespace std;
using namespace boost; using namespace boost;
@ -48,8 +51,11 @@ void ProcessMessageInstantX(CNode* pfrom, std::string& strCommand, CDataStream&
CTransaction tx; CTransaction tx;
vRecv >> tx; vRecv >> tx;
// FIXME: this part of simulating inv is not good actually, leaving it only for 12.1 backwards compatibility
// and since we are using invs for relaying even initial ix request, this can (and should) be safely removed in 12.2
CInv inv(MSG_TXLOCK_REQUEST, tx.GetHash()); CInv inv(MSG_TXLOCK_REQUEST, tx.GetHash());
pfrom->AddInventoryKnown(inv); pfrom->AddInventoryKnown(inv);
GetMainSignals().Inventory(inv.hash);
// have we seen it already? // have we seen it already?
if(mapTxLockReq.count(inv.hash) || mapTxLockReqRejected.count(inv.hash)) return; if(mapTxLockReq.count(inv.hash) || mapTxLockReqRejected.count(inv.hash)) return;
@ -88,6 +94,15 @@ void ProcessMessageInstantX(CNode* pfrom, std::string& strCommand, CDataStream&
tx.GetHash().ToString() tx.GetHash().ToString()
); );
// Masternodes will sometimes propagate votes before the transaction is known to the client.
// If this just happened - update transaction status, try forcing external script notification,
// lock inputs and resolve conflicting locks
if(IsLockedIXTransaction(tx.GetHash())) {
UpdateLockedTransaction(tx, true);
LockTransactionInputs(tx);
ResolveConflicts(tx);
}
return; return;
} else { } else {
@ -100,20 +115,8 @@ void ProcessMessageInstantX(CNode* pfrom, std::string& strCommand, CDataStream&
tx.GetHash().ToString() tx.GetHash().ToString()
); );
BOOST_FOREACH(const CTxIn& in, tx.vin){ LockTransactionInputs(tx);
if(!mapLockedInputs.count(in.prevout)){ ResolveConflicts(tx);
mapLockedInputs.insert(make_pair(in.prevout, tx.GetHash()));
}
}
// resolve conflicts
if (IsLockedIXTransaction(tx.GetHash()) && !CheckForConflictingLocks(tx)){
LogPrintf("ProcessMessageInstantX::ix - Found Existing Complete IX Lock\n");
//reprocess the last 15 blocks
ReprocessBlocks(15);
mapTxLockReq.insert(make_pair(tx.GetHash(), tx));
}
return; return;
} }
@ -347,46 +350,25 @@ bool ProcessConsensusVote(CNode* pnode, CConsensusVote& ctx)
if (i != mapTxLocks.end()){ if (i != mapTxLocks.end()){
(*i).second.AddSignature(ctx); (*i).second.AddSignature(ctx);
#ifdef ENABLE_WALLET
if(pwalletMain){
//when we get back signatures, we'll count them as requests. Otherwise the client will think it didn't propagate.
if(pwalletMain->mapRequestCount.count(ctx.txHash))
pwalletMain->mapRequestCount[ctx.txHash]++;
}
#endif
int nSignatures = (*i).second.CountSignatures(); int nSignatures = (*i).second.CountSignatures();
LogPrint("instantx", "InstantX::ProcessConsensusVote - Transaction Lock Votes %d - %s !\n", nSignatures, ctx.GetHash().ToString()); LogPrint("instantx", "InstantX::ProcessConsensusVote - Transaction Lock Votes %d - %s !\n", nSignatures, ctx.GetHash().ToString());
if(nSignatures >= INSTANTX_SIGNATURES_REQUIRED){ if(nSignatures >= INSTANTX_SIGNATURES_REQUIRED){
LogPrint("instantx", "InstantX::ProcessConsensusVote - Transaction Lock Is Complete %s !\n", ctx.txHash.ToString()); LogPrint("instantx", "InstantX::ProcessConsensusVote - Transaction Lock Is Complete %s !\n", ctx.txHash.ToString());
CTransaction& tx = mapTxLockReq[ctx.txHash]; // Masternodes will sometimes propagate votes before the transaction is known to the client,
if(!CheckForConflictingLocks(tx)){ // will check for conflicting locks and update transaction status on a new vote message
// only after the lock itself has arrived
if(!mapTxLockReq.count(ctx.txHash) && !mapTxLockReqRejected.count(ctx.txHash)) return true;
#ifdef ENABLE_WALLET if(!FindConflictingLocks(mapTxLockReq[ctx.txHash])) { //?????
if(pwalletMain){ if(mapTxLockReq.count(ctx.txHash)) {
if(pwalletMain->UpdatedTransaction(ctx.txHash)){ UpdateLockedTransaction(mapTxLockReq[ctx.txHash]);
// bumping this to update UI LockTransactionInputs(mapTxLockReq[ctx.txHash]);
nCompleteTXLocks++; } else if(mapTxLockReqRejected.count(ctx.txHash)) {
} ResolveConflicts(mapTxLockReqRejected[ctx.txHash]); ///?????
} } else {
#endif LogPrint("instantx", "InstantX::ProcessConsensusVote - Transaction Lock Request is missing %s ! votes %d\n", ctx.GetHash().ToString(), nSignatures);
if(mapTxLockReq.count(ctx.txHash)){
BOOST_FOREACH(const CTxIn& in, tx.vin){
if(!mapLockedInputs.count(in.prevout)){
mapLockedInputs.insert(make_pair(in.prevout, ctx.txHash));
}
}
}
// resolve conflicts
//if this tx lock was rejected, we need to remove the conflicting blocks
if(mapTxLockReqRejected.count(ctx.txHash)){
//reprocess the last 15 blocks
ReprocessBlocks(15);
} }
} }
} }
@ -397,7 +379,43 @@ bool ProcessConsensusVote(CNode* pnode, CConsensusVote& ctx)
return false; return false;
} }
bool CheckForConflictingLocks(CTransaction& tx) void UpdateLockedTransaction(CTransaction& tx, bool fForceNotification) {
// there should be no conflicting locks
if(FindConflictingLocks(tx)) return;
uint256 txHash = tx.GetHash();
// there must be a successfully verified lock request
if (!mapTxLockReq.count(txHash)) return;
#ifdef ENABLE_WALLET
if(pwalletMain && pwalletMain->UpdatedTransaction(txHash)){
// bumping this to update UI
nCompleteTXLocks++;
int nSignatures = GetTransactionLockSignatures(txHash);
// a transaction lock must have enough signatures to trigger this notification
if(nSignatures == INSTANTX_SIGNATURES_REQUIRED || (fForceNotification && nSignatures > INSTANTX_SIGNATURES_REQUIRED)) {
// notify an external script once threshold is reached
std::string strCmd = GetArg("-ixnotify", "");
if ( !strCmd.empty())
{
boost::replace_all(strCmd, "%s", txHash.GetHex());
boost::thread t(runCommand, strCmd); // thread runs free
}
}
}
#endif
}
void LockTransactionInputs(CTransaction& tx) {
if(mapTxLockReq.count(tx.GetHash())){
BOOST_FOREACH(const CTxIn& in, tx.vin){
if(!mapLockedInputs.count(in.prevout)){
mapLockedInputs.insert(make_pair(in.prevout, tx.GetHash()));
}
}
}
}
bool FindConflictingLocks(CTransaction& tx)
{ {
/* /*
It's possible (very unlikely though) to get 2 conflicting transaction locks approved by the network. It's possible (very unlikely though) to get 2 conflicting transaction locks approved by the network.
@ -409,7 +427,7 @@ bool CheckForConflictingLocks(CTransaction& tx)
BOOST_FOREACH(const CTxIn& in, tx.vin){ BOOST_FOREACH(const CTxIn& in, tx.vin){
if(mapLockedInputs.count(in.prevout)){ if(mapLockedInputs.count(in.prevout)){
if(mapLockedInputs[in.prevout] != tx.GetHash()){ if(mapLockedInputs[in.prevout] != tx.GetHash()){
LogPrintf("InstantX::CheckForConflictingLocks - found two complete conflicting locks - removing both. %s %s", tx.GetHash().ToString(), mapLockedInputs[in.prevout].ToString()); LogPrintf("InstantX::FindConflictingLocks - found two complete conflicting locks - removing both. %s %s", tx.GetHash().ToString(), mapLockedInputs[in.prevout].ToString());
if(mapTxLocks.count(tx.GetHash())) mapTxLocks[tx.GetHash()].nExpiration = GetTime(); if(mapTxLocks.count(tx.GetHash())) mapTxLocks[tx.GetHash()].nExpiration = GetTime();
if(mapTxLocks.count(mapLockedInputs[in.prevout])) mapTxLocks[mapLockedInputs[in.prevout]].nExpiration = GetTime(); if(mapTxLocks.count(mapLockedInputs[in.prevout])) mapTxLocks[mapLockedInputs[in.prevout]].nExpiration = GetTime();
return true; return true;
@ -420,6 +438,17 @@ bool CheckForConflictingLocks(CTransaction& tx)
return false; return false;
} }
void ResolveConflicts(CTransaction& tx) {
// resolve conflicts
if (IsLockedIXTransaction(tx.GetHash()) && !FindConflictingLocks(tx)){ //?????
LogPrintf("ResolveConflicts - Found Existing Complete IX Lock, resolving...\n");
//reprocess the last 15 blocks
ReprocessBlocks(15);
if(!mapTxLockReq.count(tx.GetHash())) mapTxLockReq.insert(make_pair(tx.GetHash(), tx)); //?????
}
}
int64_t GetAverageVoteTime() int64_t GetAverageVoteTime()
{ {
std::map<uint256, int64_t>::iterator it = mapUnknownVotes.begin(); std::map<uint256, int64_t>::iterator it = mapUnknownVotes.begin();
@ -464,6 +493,9 @@ void CleanTransactionLocksList()
} }
bool IsLockedIXTransaction(uint256 txHash) { bool IsLockedIXTransaction(uint256 txHash) {
// there must be a successfully verified lock request...
if (!mapTxLockReq.count(txHash)) return false;
// ...and corresponding lock must have enough signatures
std::map<uint256, CTransactionLock>::iterator i = mapTxLocks.find(txHash); std::map<uint256, CTransactionLock>::iterator i = mapTxLocks.find(txHash);
return i != mapTxLocks.end() && (*i).second.CountSignatures() >= INSTANTX_SIGNATURES_REQUIRED; return i != mapTxLocks.end() && (*i).second.CountSignatures() >= INSTANTX_SIGNATURES_REQUIRED;
} }

View File

@ -45,9 +45,6 @@ int64_t CreateNewLock(CTransaction tx);
bool IsIXTXValid(const CTransaction& txCollateral); bool IsIXTXValid(const CTransaction& txCollateral);
// if two conflicting locks are approved by the network, they will cancel out
bool CheckForConflictingLocks(CTransaction& tx);
void ProcessMessageInstantX(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); void ProcessMessageInstantX(CNode* pfrom, std::string& strCommand, CDataStream& vRecv);
//check if we need to vote on this transaction //check if we need to vote on this transaction
@ -56,6 +53,17 @@ void DoConsensusVote(CTransaction& tx, int64_t nBlockHeight);
//process consensus vote message //process consensus vote message
bool ProcessConsensusVote(CNode *pnode, CConsensusVote& ctx); bool ProcessConsensusVote(CNode *pnode, CConsensusVote& ctx);
//update UI and notify external script if any
void UpdateLockedTransaction(CTransaction& tx, bool fForceNotification = false);
void LockTransactionInputs(CTransaction& tx);
// if two conflicting locks are approved by the network, they will cancel out
bool FindConflictingLocks(CTransaction& tx);
//try to resolve conflicting locks
void ResolveConflicts(CTransaction& tx);
// keep transaction locks in memory for an hour // keep transaction locks in memory for an hour
void CleanTransactionLocksList(); void CleanTransactionLocksList();