Merge pull request #749 from UdjinM6/fixix_ixnotify
Refactor/fix IX & Implement ixnotify
This commit is contained in:
commit
fa0503d89b
@ -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:"));
|
||||||
|
130
src/instantx.cpp
130
src/instantx.cpp
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user