Skip to content

Commit

Permalink
Merge pull request #749 from UdjinM6/fixix_ixnotify
Browse files Browse the repository at this point in the history
Refactor/fix IX & Implement ixnotify
  • Loading branch information
evan82 committed Apr 5, 2016
2 parents dee72ad + 00915ca commit fa0503d
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 53 deletions.
1 change: 1 addition & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageGroup(_("InstantX options:"));
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("-ixnotify=<cmd>", _("Execute command when a wallet IX transaction is successfully locked (%s in cmd is replaced by TxID)"));


strUsage += HelpMessageGroup(_("Node relay options:"));
Expand Down
132 changes: 82 additions & 50 deletions src/instantx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
#include "masternode-sync.h"
#include "masternodeman.h"
#include "spork.h"

#include <boost/algorithm/string/replace.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>

using namespace std;
using namespace boost;
Expand Down Expand Up @@ -48,8 +51,11 @@ void ProcessMessageInstantX(CNode* pfrom, std::string& strCommand, CDataStream&
CTransaction 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());
pfrom->AddInventoryKnown(inv);
GetMainSignals().Inventory(inv.hash);

// have we seen it already?
if(mapTxLockReq.count(inv.hash) || mapTxLockReqRejected.count(inv.hash)) return;
Expand Down Expand Up @@ -88,6 +94,15 @@ void ProcessMessageInstantX(CNode* pfrom, std::string& strCommand, CDataStream&
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;

} else {
Expand All @@ -100,20 +115,8 @@ void ProcessMessageInstantX(CNode* pfrom, std::string& strCommand, CDataStream&
tx.GetHash().ToString()
);

BOOST_FOREACH(const CTxIn& in, tx.vin){
if(!mapLockedInputs.count(in.prevout)){
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));
}
LockTransactionInputs(tx);
ResolveConflicts(tx);

return;
}
Expand Down Expand Up @@ -347,46 +350,25 @@ bool ProcessConsensusVote(CNode* pnode, CConsensusVote& ctx)
if (i != mapTxLocks.end()){
(*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();
LogPrint("instantx", "InstantX::ProcessConsensusVote - Transaction Lock Votes %d - %s !\n", nSignatures, ctx.GetHash().ToString());

if(nSignatures >= INSTANTX_SIGNATURES_REQUIRED){
LogPrint("instantx", "InstantX::ProcessConsensusVote - Transaction Lock Is Complete %s !\n", ctx.txHash.ToString());

CTransaction& tx = mapTxLockReq[ctx.txHash];
if(!CheckForConflictingLocks(tx)){

#ifdef ENABLE_WALLET
if(pwalletMain){
if(pwalletMain->UpdatedTransaction(ctx.txHash)){
// bumping this to update UI
nCompleteTXLocks++;
}
}
#endif

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);
// Masternodes will sometimes propagate votes before the transaction is known to the client,
// 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;

if(!FindConflictingLocks(mapTxLockReq[ctx.txHash])) { //?????
if(mapTxLockReq.count(ctx.txHash)) {
UpdateLockedTransaction(mapTxLockReq[ctx.txHash]);
LockTransactionInputs(mapTxLockReq[ctx.txHash]);
} else if(mapTxLockReqRejected.count(ctx.txHash)) {
ResolveConflicts(mapTxLockReqRejected[ctx.txHash]); ///?????
} else {
LogPrint("instantx", "InstantX::ProcessConsensusVote - Transaction Lock Request is missing %s ! votes %d\n", ctx.GetHash().ToString(), nSignatures);
}
}
}
Expand All @@ -397,7 +379,43 @@ bool ProcessConsensusVote(CNode* pnode, CConsensusVote& ctx)
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.
Expand All @@ -409,7 +427,7 @@ bool CheckForConflictingLocks(CTransaction& tx)
BOOST_FOREACH(const CTxIn& in, tx.vin){
if(mapLockedInputs.count(in.prevout)){
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(mapLockedInputs[in.prevout])) mapTxLocks[mapLockedInputs[in.prevout]].nExpiration = GetTime();
return true;
Expand All @@ -420,6 +438,17 @@ bool CheckForConflictingLocks(CTransaction& tx)
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()
{
std::map<uint256, int64_t>::iterator it = mapUnknownVotes.begin();
Expand Down Expand Up @@ -464,6 +493,9 @@ void CleanTransactionLocksList()
}

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);
return i != mapTxLocks.end() && (*i).second.CountSignatures() >= INSTANTX_SIGNATURES_REQUIRED;
}
Expand Down
14 changes: 11 additions & 3 deletions src/instantx.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,6 @@ 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);

void ProcessMessageInstantX(CNode* pfrom, std::string& strCommand, CDataStream& vRecv);

//check if we need to vote on this transaction
Expand All @@ -56,6 +53,17 @@ void DoConsensusVote(CTransaction& tx, int64_t nBlockHeight);
//process consensus vote message
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
void CleanTransactionLocksList();

Expand Down

0 comments on commit fa0503d

Please sign in to comment.