[ZMQ] Notify when an IS double spend is attempted (#2262)

* Implement IS Double spend notifications in zmq

* copy/paste error

* typo

* Send both conflicting and conflicts against as ZMQ notifications

* CTransaction based not hash based

* @UdjinM6 requested changes
This commit is contained in:
PastaPastaPasta 2018-09-12 06:12:44 -05:00 committed by UdjinM6
parent 28e0476f4c
commit 1e74bcace9
13 changed files with 87 additions and 2 deletions

View File

@ -16,11 +16,13 @@ zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashtx")
zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashtxlock") zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashtxlock")
zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashgovernancevote") zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashgovernancevote")
zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashgovernanceobject") zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashgovernanceobject")
zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashinstantsenddoublespend")
zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawblock") zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawblock")
zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawtx") zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawtx")
zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawtxlock") zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawtxlock")
zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawgovernancevote") zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawgovernancevote")
zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawgovernanceobject") zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawgovernanceobject")
zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawinstantsenddoublespend")
zmqSubSocket.connect("tcp://127.0.0.1:%i" % port) zmqSubSocket.connect("tcp://127.0.0.1:%i" % port)
try: try:
@ -52,6 +54,9 @@ try:
elif topic == "rawtxlock": elif topic == "rawtxlock":
print('- RAW TX LOCK ('+sequence+') -') print('- RAW TX LOCK ('+sequence+') -')
print(binascii.hexlify(body).decode("utf-8")) 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": elif topic == "hashgovernancevote":
print('- HASH GOVERNANCE VOTE ('+sequence+') -') print('- HASH GOVERNANCE VOTE ('+sequence+') -')
print(binascii.hexlify(body).decode("utf-8")) print(binascii.hexlify(body).decode("utf-8"))
@ -64,7 +69,9 @@ try:
elif topic == "rawgovernanceobject": elif topic == "rawgovernanceobject":
print('- RAW GOVERNANCE OBJECT ('+sequence+') -') print('- RAW GOVERNANCE OBJECT ('+sequence+') -')
print(binascii.hexlify(body).decode("utf-8")) 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: except KeyboardInterrupt:
zmqContext.destroy() zmqContext.destroy()

View File

@ -66,6 +66,8 @@ Currently, the following notifications are supported:
-zmqpubhashgovernanceobject=address -zmqpubhashgovernanceobject=address
-zmqpubrawgovernancevote=address -zmqpubrawgovernancevote=address
-zmqpubhashgovernanceobject=address -zmqpubhashgovernanceobject=address
-zmqpubrawinstantsenddoublespend=address
-zmqpubhashinstantsenddoublespend=address
The socket type is PUB and the address must be a valid ZeroMQ socket 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. address. The same address can be used in more than one notification.

View File

@ -508,9 +508,11 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-zmqpubhashtxlock=<address>", _("Enable publish hash transaction (locked via InstantSend) in <address>")); strUsage += HelpMessageOpt("-zmqpubhashtxlock=<address>", _("Enable publish hash transaction (locked via InstantSend) in <address>"));
strUsage += HelpMessageOpt("-zmqpubhashgovernancevote=<address>", _("Enable publish hash of governance votes in <address>")); strUsage += HelpMessageOpt("-zmqpubhashgovernancevote=<address>", _("Enable publish hash of governance votes in <address>"));
strUsage += HelpMessageOpt("-zmqpubhashgovernanceobject=<address>", _("Enable publish hash of governance objects (like proposals) in <address>")); strUsage += HelpMessageOpt("-zmqpubhashgovernanceobject=<address>", _("Enable publish hash of governance objects (like proposals) in <address>"));
strUsage += HelpMessageOpt("-zmqpubhashinstantsenddoublespend=<address>", _("Enable publish transaction hashes of attempted InstantSend double spend in <address>"));
strUsage += HelpMessageOpt("-zmqpubrawblock=<address>", _("Enable publish raw block in <address>")); strUsage += HelpMessageOpt("-zmqpubrawblock=<address>", _("Enable publish raw block in <address>"));
strUsage += HelpMessageOpt("-zmqpubrawtx=<address>", _("Enable publish raw transaction in <address>")); strUsage += HelpMessageOpt("-zmqpubrawtx=<address>", _("Enable publish raw transaction in <address>"));
strUsage += HelpMessageOpt("-zmqpubrawtxlock=<address>", _("Enable publish raw transaction (locked via InstantSend) in <address>")); strUsage += HelpMessageOpt("-zmqpubrawtxlock=<address>", _("Enable publish raw transaction (locked via InstantSend) in <address>"));
strUsage += HelpMessageOpt("-zmqpubrawinstantsenddoublespend=<address>", _("Enable publish raw transactions of attempted InstantSend double spend in <address>"));
#endif #endif
strUsage += HelpMessageGroup(_("Debugging/Testing options:")); strUsage += HelpMessageGroup(_("Debugging/Testing options:"));

View File

@ -117,7 +117,13 @@ bool CInstantSend::ProcessTxLockRequest(const CTxLockRequest& txLockRequest, CCo
if(hash != txLockRequest.GetHash()) { if(hash != txLockRequest.GetHash()) {
LogPrint("instantsend", "CInstantSend::ProcessTxLockRequest -- Double spend attempt! %s\n", txin.prevout.ToStringShort()); 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 // 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);
}
} }
} }
} }

View File

@ -28,6 +28,7 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.NewPoWValidBlock.connect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2)); g_signals.NewPoWValidBlock.connect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2));
g_signals.NotifyGovernanceObject.connect(boost::bind(&CValidationInterface::NotifyGovernanceObject, pwalletIn, _1)); g_signals.NotifyGovernanceObject.connect(boost::bind(&CValidationInterface::NotifyGovernanceObject, pwalletIn, _1));
g_signals.NotifyGovernanceVote.connect(boost::bind(&CValidationInterface::NotifyGovernanceVote, 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) { void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
@ -46,6 +47,7 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.AcceptedBlockHeader.disconnect(boost::bind(&CValidationInterface::AcceptedBlockHeader, pwalletIn, _1)); g_signals.AcceptedBlockHeader.disconnect(boost::bind(&CValidationInterface::AcceptedBlockHeader, pwalletIn, _1));
g_signals.NotifyGovernanceObject.disconnect(boost::bind(&CValidationInterface::NotifyGovernanceObject, pwalletIn, _1)); g_signals.NotifyGovernanceObject.disconnect(boost::bind(&CValidationInterface::NotifyGovernanceObject, pwalletIn, _1));
g_signals.NotifyGovernanceVote.disconnect(boost::bind(&CValidationInterface::NotifyGovernanceVote, 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() { void UnregisterAllValidationInterfaces() {
@ -64,4 +66,5 @@ void UnregisterAllValidationInterfaces() {
g_signals.AcceptedBlockHeader.disconnect_all_slots(); g_signals.AcceptedBlockHeader.disconnect_all_slots();
g_signals.NotifyGovernanceObject.disconnect_all_slots(); g_signals.NotifyGovernanceObject.disconnect_all_slots();
g_signals.NotifyGovernanceVote.disconnect_all_slots(); g_signals.NotifyGovernanceVote.disconnect_all_slots();
g_signals.NotifyInstantSendDoubleSpendAttempt.disconnect_all_slots();
} }

View File

@ -40,6 +40,7 @@ protected:
virtual void NotifyTransactionLock(const CTransaction &tx) {} virtual void NotifyTransactionLock(const CTransaction &tx) {}
virtual void NotifyGovernanceVote(const CGovernanceVote &vote) {} virtual void NotifyGovernanceVote(const CGovernanceVote &vote) {}
virtual void NotifyGovernanceObject(const CGovernanceObject &object) {} virtual void NotifyGovernanceObject(const CGovernanceObject &object) {}
virtual void NotifyInstantSendDoubleSpendAttempt(const CTransaction &currentTx, const CTransaction &previousTx) {}
virtual void SetBestChain(const CBlockLocator &locator) {} virtual void SetBestChain(const CBlockLocator &locator) {}
virtual bool UpdatedTransaction(const uint256 &hash) { return false;} virtual bool UpdatedTransaction(const uint256 &hash) { return false;}
virtual void Inventory(const uint256 &hash) {} virtual void Inventory(const uint256 &hash) {}
@ -77,6 +78,8 @@ struct CMainSignals {
boost::signals2::signal<void (const CGovernanceVote &)> NotifyGovernanceVote; boost::signals2::signal<void (const CGovernanceVote &)> NotifyGovernanceVote;
/** Notifies listeners of a new governance object. */ /** Notifies listeners of a new governance object. */
boost::signals2::signal<void (const CGovernanceObject &)> NotifyGovernanceObject; boost::signals2::signal<void (const CGovernanceObject &)> NotifyGovernanceObject;
/** Notifies listeners of a attempted InstantSend double spend*/
boost::signals2::signal<void(const CTransaction &currentTx, const CTransaction &previousTx)> NotifyInstantSendDoubleSpendAttempt;
/** Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). */ /** Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). */
boost::signals2::signal<bool (const uint256 &)> UpdatedTransaction; boost::signals2::signal<bool (const uint256 &)> UpdatedTransaction;
/** Notifies listeners of a new active block chain. */ /** Notifies listeners of a new active block chain. */

View File

@ -35,3 +35,8 @@ bool CZMQAbstractNotifier::NotifyGovernanceObject(const CGovernanceObject& /*obj
{ {
return true; return true;
} }
bool CZMQAbstractNotifier::NotifyInstantSendDoubleSpendAttempt(const CTransaction& /*currentTx*/, const CTransaction& /*previousTx*/)
{
return true;
}

View File

@ -39,6 +39,7 @@ public:
virtual bool NotifyTransactionLock(const CTransaction &transaction); virtual bool NotifyTransactionLock(const CTransaction &transaction);
virtual bool NotifyGovernanceVote(const CGovernanceVote &vote); virtual bool NotifyGovernanceVote(const CGovernanceVote &vote);
virtual bool NotifyGovernanceObject(const CGovernanceObject &object); virtual bool NotifyGovernanceObject(const CGovernanceObject &object);
virtual bool NotifyInstantSendDoubleSpendAttempt(const CTransaction &currentTx, const CTransaction &previousTx);
protected: protected:

View File

@ -22,6 +22,8 @@
#include "governance-object.h" #include "governance-object.h"
#include "governance-vote.h" #include "governance-vote.h"
#include "instantx.h"
void zmqError(const char *str); void zmqError(const char *str);
#endif // BITCOIN_ZMQ_ZMQCONFIG_H #endif // BITCOIN_ZMQ_ZMQCONFIG_H

View File

@ -40,11 +40,13 @@ CZMQNotificationInterface* CZMQNotificationInterface::Create()
factories["pubhashtxlock"] = CZMQAbstractNotifier::Create<CZMQPublishHashTransactionLockNotifier>; factories["pubhashtxlock"] = CZMQAbstractNotifier::Create<CZMQPublishHashTransactionLockNotifier>;
factories["pubhashgovernancevote"] = CZMQAbstractNotifier::Create<CZMQPublishHashGovernanceVoteNotifier>; factories["pubhashgovernancevote"] = CZMQAbstractNotifier::Create<CZMQPublishHashGovernanceVoteNotifier>;
factories["pubhashgovernanceobject"] = CZMQAbstractNotifier::Create<CZMQPublishHashGovernanceObjectNotifier>; factories["pubhashgovernanceobject"] = CZMQAbstractNotifier::Create<CZMQPublishHashGovernanceObjectNotifier>;
factories["pubhashinstantsenddoublespend"] = CZMQAbstractNotifier::Create<CZMQPublishHashInstantSendDoubleSpendNotifier>;
factories["pubrawblock"] = CZMQAbstractNotifier::Create<CZMQPublishRawBlockNotifier>; factories["pubrawblock"] = CZMQAbstractNotifier::Create<CZMQPublishRawBlockNotifier>;
factories["pubrawtx"] = CZMQAbstractNotifier::Create<CZMQPublishRawTransactionNotifier>; factories["pubrawtx"] = CZMQAbstractNotifier::Create<CZMQPublishRawTransactionNotifier>;
factories["pubrawtxlock"] = CZMQAbstractNotifier::Create<CZMQPublishRawTransactionLockNotifier>; factories["pubrawtxlock"] = CZMQAbstractNotifier::Create<CZMQPublishRawTransactionLockNotifier>;
factories["pubrawgovernancevote"] = CZMQAbstractNotifier::Create<CZMQPublishRawGovernanceVoteNotifier>; factories["pubrawgovernancevote"] = CZMQAbstractNotifier::Create<CZMQPublishRawGovernanceVoteNotifier>;
factories["pubrawgovernanceobject"] = CZMQAbstractNotifier::Create<CZMQPublishRawGovernanceObjectNotifier>; factories["pubrawgovernanceobject"] = CZMQAbstractNotifier::Create<CZMQPublishRawGovernanceObjectNotifier>;
factories["pubrawinstantsenddoublespend"] = CZMQAbstractNotifier::Create<CZMQPublishRawInstantSendDoubleSpendNotifier>;
for (std::map<std::string, CZMQNotifierFactory>::const_iterator i=factories.begin(); i!=factories.end(); ++i) for (std::map<std::string, CZMQNotifierFactory>::const_iterator i=factories.begin(); i!=factories.end(); ++i)
{ {
@ -217,3 +219,16 @@ void CZMQNotificationInterface::NotifyGovernanceObject(const CGovernanceObject &
} }
} }
} }
void CZMQNotificationInterface::NotifyInstantSendDoubleSpendAttempt(const CTransaction &currentTx, 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);
}
}
}

View File

@ -29,6 +29,7 @@ protected:
void NotifyTransactionLock(const CTransaction &tx) override; void NotifyTransactionLock(const CTransaction &tx) override;
void NotifyGovernanceVote(const CGovernanceVote& vote) override; void NotifyGovernanceVote(const CGovernanceVote& vote) override;
void NotifyGovernanceObject(const CGovernanceObject& object) override; void NotifyGovernanceObject(const CGovernanceObject& object) override;
void NotifyInstantSendDoubleSpendAttempt(const CTransaction &currentTx, const CTransaction &previousTx) override;
private: private:

View File

@ -15,11 +15,13 @@ static const char *MSG_HASHTX = "hashtx";
static const char *MSG_HASHTXLOCK = "hashtxlock"; static const char *MSG_HASHTXLOCK = "hashtxlock";
static const char *MSG_HASHGVOTE = "hashgovernancevote"; static const char *MSG_HASHGVOTE = "hashgovernancevote";
static const char *MSG_HASHGOBJ = "hashgovernanceobject"; static const char *MSG_HASHGOBJ = "hashgovernanceobject";
static const char *MSG_HASHISCON = "hashinstantsenddoublespend";
static const char *MSG_RAWBLOCK = "rawblock"; static const char *MSG_RAWBLOCK = "rawblock";
static const char *MSG_RAWTX = "rawtx"; static const char *MSG_RAWTX = "rawtx";
static const char *MSG_RAWTXLOCK = "rawtxlock"; static const char *MSG_RAWTXLOCK = "rawtxlock";
static const char *MSG_RAWGVOTE = "rawgovernancevote"; static const char *MSG_RAWGVOTE = "rawgovernancevote";
static const char *MSG_RAWGOBJ = "rawgovernanceobject"; static const char *MSG_RAWGOBJ = "rawgovernanceobject";
static const char *MSG_RAWISCON = "rawinstantsenddoublespend";
// Internal function to send multipart message // Internal function to send multipart message
static int zmq_send_multipart(void *sock, const void* data, size_t size, ...) 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); return SendMessage(MSG_HASHGOBJ, data, 32);
} }
bool CZMQPublishHashInstantSendDoubleSpendNotifier::NotifyInstantSendDoubleSpendAttempt(const CTransaction &currentTx, 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) bool CZMQPublishRawBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
{ {
LogPrint("zmq", "zmq: Publish rawblock %s\n", pindex->GetBlockHash().GetHex()); LogPrint("zmq", "zmq: Publish rawblock %s\n", pindex->GetBlockHash().GetHex());
@ -253,3 +269,13 @@ bool CZMQPublishRawGovernanceObjectNotifier::NotifyGovernanceObject(const CGover
ss << govobj; ss << govobj;
return SendMessage(MSG_RAWGOBJ, &(*ss.begin()), ss.size()); return SendMessage(MSG_RAWGOBJ, &(*ss.begin()), ss.size());
} }
bool CZMQPublishRawInstantSendDoubleSpendNotifier::NotifyInstantSendDoubleSpendAttempt(const CTransaction &currentTx, 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());
}

View File

@ -60,6 +60,12 @@ public:
bool NotifyGovernanceObject(const CGovernanceObject &object) override; bool NotifyGovernanceObject(const CGovernanceObject &object) override;
}; };
class CZMQPublishHashInstantSendDoubleSpendNotifier : public CZMQAbstractPublishNotifier
{
public:
bool NotifyInstantSendDoubleSpendAttempt(const CTransaction &currentTx, const CTransaction &previousTx) override;
};
class CZMQPublishRawBlockNotifier : public CZMQAbstractPublishNotifier class CZMQPublishRawBlockNotifier : public CZMQAbstractPublishNotifier
{ {
public: public:
@ -89,4 +95,10 @@ class CZMQPublishRawGovernanceObjectNotifier : public CZMQAbstractPublishNotifie
public: public:
bool NotifyGovernanceObject(const CGovernanceObject &object) override; bool NotifyGovernanceObject(const CGovernanceObject &object) override;
}; };
class CZMQPublishRawInstantSendDoubleSpendNotifier : public CZMQAbstractPublishNotifier
{
public:
bool NotifyInstantSendDoubleSpendAttempt(const CTransaction &currentTx, const CTransaction &previousTx) override;
};
#endif // BITCOIN_ZMQ_ZMQPUBLISHNOTIFIER_H #endif // BITCOIN_ZMQ_ZMQPUBLISHNOTIFIER_H