diff --git a/contrib/zmq/zmq_sub.py b/contrib/zmq/zmq_sub.py index 7fbfbbd005..fa15f1285c 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 489ab549a0..627f46c2d1 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 c66e0039d5..6d5d7c235a 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 dc5431684b..79412272b6 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 436cedf101..26b08647da 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 8ce15b30cd..f5f5df31c0 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 ea993d6061..6e280eefea 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 ee6aa3ad7e..6ce9d12531 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 b7168c962d..c1a8e6c3f6 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 7895f17aaa..8a15fd1a9e 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 aeaed254a0..f552751e97 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 8a6c4167d0..9f46331c0d 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 31c90317d8..e51b6a3145 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