diff --git a/contrib/zmq/zmq_sub.py b/contrib/zmq/zmq_sub.py
index 7fbfbbd00..fa15f1285 100755
--- a/contrib/zmq/zmq_sub.py
+++ b/contrib/zmq/zmq_sub.py
@@ -16,11 +16,13 @@ zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashtx")
zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashtxlock")
zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashgovernancevote")
zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashgovernanceobject")
+zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashinstantsenddoublespend")
zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawblock")
zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawtx")
zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawtxlock")
zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawgovernancevote")
zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawgovernanceobject")
+zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawinstantsenddoublespend")
zmqSubSocket.connect("tcp://127.0.0.1:%i" % port)
try:
@@ -52,6 +54,9 @@ try:
elif topic == "rawtxlock":
print('- RAW TX LOCK ('+sequence+') -')
print(binascii.hexlify(body).decode("utf-8"))
+ elif topic == "rawinstantsenddoublespend":
+ print('- RAW IS DOUBLE SPEND ('+sequence+') -')
+ print(binascii.hexlify(body).decode("utf-8"))
elif topic == "hashgovernancevote":
print('- HASH GOVERNANCE VOTE ('+sequence+') -')
print(binascii.hexlify(body).decode("utf-8"))
@@ -64,7 +69,9 @@ try:
elif topic == "rawgovernanceobject":
print('- RAW GOVERNANCE OBJECT ('+sequence+') -')
print(binascii.hexlify(body).decode("utf-8"))
-
+ elif topic == "hashinstantsenddoublespend":
+ print('- HASH IS DOUBLE SPEND ('+sequence+') -')
+ print(binascii.hexlify(body).decode("utf-8"))
except KeyboardInterrupt:
zmqContext.destroy()
diff --git a/doc/zmq.md b/doc/zmq.md
index 489ab549a..627f46c2d 100644
--- a/doc/zmq.md
+++ b/doc/zmq.md
@@ -66,6 +66,8 @@ Currently, the following notifications are supported:
-zmqpubhashgovernanceobject=address
-zmqpubrawgovernancevote=address
-zmqpubhashgovernanceobject=address
+ -zmqpubrawinstantsenddoublespend=address
+ -zmqpubhashinstantsenddoublespend=address
The socket type is PUB and the address must be a valid ZeroMQ socket
address. The same address can be used in more than one notification.
diff --git a/src/init.cpp b/src/init.cpp
index c66e0039d..6d5d7c235 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -508,9 +508,11 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-zmqpubhashtxlock=
", _("Enable publish hash transaction (locked via InstantSend) in "));
strUsage += HelpMessageOpt("-zmqpubhashgovernancevote=", _("Enable publish hash of governance votes in "));
strUsage += HelpMessageOpt("-zmqpubhashgovernanceobject=", _("Enable publish hash of governance objects (like proposals) in "));
+ strUsage += HelpMessageOpt("-zmqpubhashinstantsenddoublespend=", _("Enable publish transaction hashes of attempted InstantSend double spend in "));
strUsage += HelpMessageOpt("-zmqpubrawblock=", _("Enable publish raw block in "));
strUsage += HelpMessageOpt("-zmqpubrawtx=", _("Enable publish raw transaction in "));
strUsage += HelpMessageOpt("-zmqpubrawtxlock=", _("Enable publish raw transaction (locked via InstantSend) in "));
+ strUsage += HelpMessageOpt("-zmqpubrawinstantsenddoublespend=", _("Enable publish raw transactions of attempted InstantSend double spend in "));
#endif
strUsage += HelpMessageGroup(_("Debugging/Testing options:"));
diff --git a/src/instantx.cpp b/src/instantx.cpp
index dc5431684..79412272b 100644
--- a/src/instantx.cpp
+++ b/src/instantx.cpp
@@ -117,7 +117,13 @@ bool CInstantSend::ProcessTxLockRequest(const CTxLockRequest& txLockRequest, CCo
if(hash != txLockRequest.GetHash()) {
LogPrint("instantsend", "CInstantSend::ProcessTxLockRequest -- Double spend attempt! %s\n", txin.prevout.ToStringShort());
// do not fail here, let it go and see which one will get the votes to be locked
- // TODO: notify zmq+script
+ // NOTIFY ZMQ
+ CTransaction txCurrent = *txLockRequest.tx; // currently processed tx
+ auto itPrevious = mapTxLockCandidates.find(hash);
+ if (itPrevious != mapTxLockCandidates.end() && itPrevious->second.txLockRequest) {
+ CTransaction txPrevious = *itPrevious->second.txLockRequest.tx; // previously locked one
+ GetMainSignals().NotifyInstantSendDoubleSpendAttempt(txCurrent, txPrevious);
+ }
}
}
}
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index 436cedf10..26b08647d 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -28,6 +28,7 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.NewPoWValidBlock.connect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2));
g_signals.NotifyGovernanceObject.connect(boost::bind(&CValidationInterface::NotifyGovernanceObject, pwalletIn, _1));
g_signals.NotifyGovernanceVote.connect(boost::bind(&CValidationInterface::NotifyGovernanceVote, pwalletIn, _1));
+ g_signals.NotifyInstantSendDoubleSpendAttempt.connect(boost::bind(&CValidationInterface::NotifyInstantSendDoubleSpendAttempt, pwalletIn, _1, _2));
}
void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
@@ -46,6 +47,7 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.AcceptedBlockHeader.disconnect(boost::bind(&CValidationInterface::AcceptedBlockHeader, pwalletIn, _1));
g_signals.NotifyGovernanceObject.disconnect(boost::bind(&CValidationInterface::NotifyGovernanceObject, pwalletIn, _1));
g_signals.NotifyGovernanceVote.disconnect(boost::bind(&CValidationInterface::NotifyGovernanceVote, pwalletIn, _1));
+ g_signals.NotifyInstantSendDoubleSpendAttempt.disconnect(boost::bind(&CValidationInterface::NotifyInstantSendDoubleSpendAttempt, pwalletIn, _1, _2));
}
void UnregisterAllValidationInterfaces() {
@@ -64,4 +66,5 @@ void UnregisterAllValidationInterfaces() {
g_signals.AcceptedBlockHeader.disconnect_all_slots();
g_signals.NotifyGovernanceObject.disconnect_all_slots();
g_signals.NotifyGovernanceVote.disconnect_all_slots();
+ g_signals.NotifyInstantSendDoubleSpendAttempt.disconnect_all_slots();
}
diff --git a/src/validationinterface.h b/src/validationinterface.h
index 8ce15b30c..f5f5df31c 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -40,6 +40,7 @@ protected:
virtual void NotifyTransactionLock(const CTransaction &tx) {}
virtual void NotifyGovernanceVote(const CGovernanceVote &vote) {}
virtual void NotifyGovernanceObject(const CGovernanceObject &object) {}
+ virtual void NotifyInstantSendDoubleSpendAttempt(const CTransaction ¤tTx, const CTransaction &previousTx) {}
virtual void SetBestChain(const CBlockLocator &locator) {}
virtual bool UpdatedTransaction(const uint256 &hash) { return false;}
virtual void Inventory(const uint256 &hash) {}
@@ -77,6 +78,8 @@ struct CMainSignals {
boost::signals2::signal NotifyGovernanceVote;
/** Notifies listeners of a new governance object. */
boost::signals2::signal NotifyGovernanceObject;
+ /** Notifies listeners of a attempted InstantSend double spend*/
+ boost::signals2::signal NotifyInstantSendDoubleSpendAttempt;
/** Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). */
boost::signals2::signal UpdatedTransaction;
/** Notifies listeners of a new active block chain. */
diff --git a/src/zmq/zmqabstractnotifier.cpp b/src/zmq/zmqabstractnotifier.cpp
index ea993d606..6e280eefe 100644
--- a/src/zmq/zmqabstractnotifier.cpp
+++ b/src/zmq/zmqabstractnotifier.cpp
@@ -35,3 +35,8 @@ bool CZMQAbstractNotifier::NotifyGovernanceObject(const CGovernanceObject& /*obj
{
return true;
}
+
+bool CZMQAbstractNotifier::NotifyInstantSendDoubleSpendAttempt(const CTransaction& /*currentTx*/, const CTransaction& /*previousTx*/)
+{
+ return true;
+}
diff --git a/src/zmq/zmqabstractnotifier.h b/src/zmq/zmqabstractnotifier.h
index ee6aa3ad7..6ce9d1253 100644
--- a/src/zmq/zmqabstractnotifier.h
+++ b/src/zmq/zmqabstractnotifier.h
@@ -39,6 +39,7 @@ public:
virtual bool NotifyTransactionLock(const CTransaction &transaction);
virtual bool NotifyGovernanceVote(const CGovernanceVote &vote);
virtual bool NotifyGovernanceObject(const CGovernanceObject &object);
+ virtual bool NotifyInstantSendDoubleSpendAttempt(const CTransaction ¤tTx, const CTransaction &previousTx);
protected:
diff --git a/src/zmq/zmqconfig.h b/src/zmq/zmqconfig.h
index b7168c962..c1a8e6c3f 100644
--- a/src/zmq/zmqconfig.h
+++ b/src/zmq/zmqconfig.h
@@ -22,6 +22,8 @@
#include "governance-object.h"
#include "governance-vote.h"
+#include "instantx.h"
+
void zmqError(const char *str);
#endif // BITCOIN_ZMQ_ZMQCONFIG_H
diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp
index 7895f17aa..8a15fd1a9 100644
--- a/src/zmq/zmqnotificationinterface.cpp
+++ b/src/zmq/zmqnotificationinterface.cpp
@@ -40,11 +40,13 @@ CZMQNotificationInterface* CZMQNotificationInterface::Create()
factories["pubhashtxlock"] = CZMQAbstractNotifier::Create;
factories["pubhashgovernancevote"] = CZMQAbstractNotifier::Create;
factories["pubhashgovernanceobject"] = CZMQAbstractNotifier::Create;
+ factories["pubhashinstantsenddoublespend"] = CZMQAbstractNotifier::Create;
factories["pubrawblock"] = CZMQAbstractNotifier::Create;
factories["pubrawtx"] = CZMQAbstractNotifier::Create;
factories["pubrawtxlock"] = CZMQAbstractNotifier::Create;
factories["pubrawgovernancevote"] = CZMQAbstractNotifier::Create;
factories["pubrawgovernanceobject"] = CZMQAbstractNotifier::Create;
+ factories["pubrawinstantsenddoublespend"] = CZMQAbstractNotifier::Create;
for (std::map::const_iterator i=factories.begin(); i!=factories.end(); ++i)
{
@@ -217,3 +219,16 @@ void CZMQNotificationInterface::NotifyGovernanceObject(const CGovernanceObject &
}
}
}
+
+void CZMQNotificationInterface::NotifyInstantSendDoubleSpendAttempt(const CTransaction ¤tTx, const CTransaction &previousTx)
+{
+ for (auto it = notifiers.begin(); it != notifiers.end();) {
+ CZMQAbstractNotifier *notifier = *it;
+ if (notifier->NotifyInstantSendDoubleSpendAttempt(currentTx, previousTx)) {
+ ++it;
+ } else {
+ notifier->Shutdown();
+ it = notifiers.erase(it);
+ }
+ }
+}
diff --git a/src/zmq/zmqnotificationinterface.h b/src/zmq/zmqnotificationinterface.h
index aeaed254a..f552751e9 100644
--- a/src/zmq/zmqnotificationinterface.h
+++ b/src/zmq/zmqnotificationinterface.h
@@ -29,6 +29,7 @@ protected:
void NotifyTransactionLock(const CTransaction &tx) override;
void NotifyGovernanceVote(const CGovernanceVote& vote) override;
void NotifyGovernanceObject(const CGovernanceObject& object) override;
+ void NotifyInstantSendDoubleSpendAttempt(const CTransaction ¤tTx, const CTransaction &previousTx) override;
private:
diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp
index 8a6c4167d..9f46331c0 100644
--- a/src/zmq/zmqpublishnotifier.cpp
+++ b/src/zmq/zmqpublishnotifier.cpp
@@ -15,11 +15,13 @@ static const char *MSG_HASHTX = "hashtx";
static const char *MSG_HASHTXLOCK = "hashtxlock";
static const char *MSG_HASHGVOTE = "hashgovernancevote";
static const char *MSG_HASHGOBJ = "hashgovernanceobject";
+static const char *MSG_HASHISCON = "hashinstantsenddoublespend";
static const char *MSG_RAWBLOCK = "rawblock";
static const char *MSG_RAWTX = "rawtx";
static const char *MSG_RAWTXLOCK = "rawtxlock";
static const char *MSG_RAWGVOTE = "rawgovernancevote";
static const char *MSG_RAWGOBJ = "rawgovernanceobject";
+static const char *MSG_RAWISCON = "rawinstantsenddoublespend";
// Internal function to send multipart message
static int zmq_send_multipart(void *sock, const void* data, size_t size, ...)
@@ -197,6 +199,20 @@ bool CZMQPublishHashGovernanceObjectNotifier::NotifyGovernanceObject(const CGove
return SendMessage(MSG_HASHGOBJ, data, 32);
}
+bool CZMQPublishHashInstantSendDoubleSpendNotifier::NotifyInstantSendDoubleSpendAttempt(const CTransaction ¤tTx, const CTransaction &previousTx)
+{
+ uint256 currentHash = currentTx.GetHash(), previousHash = previousTx.GetHash();
+ LogPrint("zmq", "zmq: Publish hashinstantsenddoublespend %s conflicts against %s\n", currentHash.ToString(), previousHash.ToString());
+ char dataCurrentHash[32], dataPreviousHash[32];
+ for (unsigned int i = 0; i < 32; i++) {
+ dataCurrentHash[31 - i] = currentHash.begin()[i];
+ dataPreviousHash[31 - i] = previousHash.begin()[i];
+ }
+ return SendMessage(MSG_HASHISCON, dataCurrentHash, 32)
+ && SendMessage(MSG_HASHISCON, dataPreviousHash, 32);
+}
+
+
bool CZMQPublishRawBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
{
LogPrint("zmq", "zmq: Publish rawblock %s\n", pindex->GetBlockHash().GetHex());
@@ -253,3 +269,13 @@ bool CZMQPublishRawGovernanceObjectNotifier::NotifyGovernanceObject(const CGover
ss << govobj;
return SendMessage(MSG_RAWGOBJ, &(*ss.begin()), ss.size());
}
+
+bool CZMQPublishRawInstantSendDoubleSpendNotifier::NotifyInstantSendDoubleSpendAttempt(const CTransaction ¤tTx, const CTransaction &previousTx)
+{
+ LogPrint("zmq", "zmq: Publish rawinstantsenddoublespend %s conflicts with %s\n", currentTx.GetHash().ToString(), previousTx.GetHash().ToString());
+ CDataStream ssCurrent(SER_NETWORK, PROTOCOL_VERSION), ssPrevious(SER_NETWORK, PROTOCOL_VERSION);
+ ssCurrent << currentTx;
+ ssPrevious << previousTx;
+ return SendMessage(MSG_RAWISCON, &(*ssCurrent.begin()), ssCurrent.size())
+ && SendMessage(MSG_RAWISCON, &(*ssPrevious.begin()), ssPrevious.size());
+}
diff --git a/src/zmq/zmqpublishnotifier.h b/src/zmq/zmqpublishnotifier.h
index 31c90317d..e51b6a314 100644
--- a/src/zmq/zmqpublishnotifier.h
+++ b/src/zmq/zmqpublishnotifier.h
@@ -60,6 +60,12 @@ public:
bool NotifyGovernanceObject(const CGovernanceObject &object) override;
};
+class CZMQPublishHashInstantSendDoubleSpendNotifier : public CZMQAbstractPublishNotifier
+{
+public:
+ bool NotifyInstantSendDoubleSpendAttempt(const CTransaction ¤tTx, const CTransaction &previousTx) override;
+};
+
class CZMQPublishRawBlockNotifier : public CZMQAbstractPublishNotifier
{
public:
@@ -89,4 +95,10 @@ class CZMQPublishRawGovernanceObjectNotifier : public CZMQAbstractPublishNotifie
public:
bool NotifyGovernanceObject(const CGovernanceObject &object) override;
};
+
+class CZMQPublishRawInstantSendDoubleSpendNotifier : public CZMQAbstractPublishNotifier
+{
+public:
+ bool NotifyInstantSendDoubleSpendAttempt(const CTransaction ¤tTx, const CTransaction &previousTx) override;
+};
#endif // BITCOIN_ZMQ_ZMQPUBLISHNOTIFIER_H