From 2bbac8ff77fbe5ca301ecdf4b1ffc20c16df3202 Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Tue, 26 Feb 2019 07:13:18 +0100
Subject: [PATCH 01/33] Introduce NotifyChainLock signal and invoke it when
 CLSIGs get processed

---
 src/llmq/quorums_chainlocks.cpp | 10 ++++++++++
 src/llmq/quorums_chainlocks.h   |  1 +
 src/validationinterface.cpp     |  3 +++
 src/validationinterface.h       |  3 +++
 4 files changed, 17 insertions(+)

diff --git a/src/llmq/quorums_chainlocks.cpp b/src/llmq/quorums_chainlocks.cpp
index 94f251117d05..5a51f98e9ca7 100644
--- a/src/llmq/quorums_chainlocks.cpp
+++ b/src/llmq/quorums_chainlocks.cpp
@@ -149,6 +149,11 @@ void CChainLocksHandler::ProcessNewChainLock(NodeId from, const llmq::CChainLock
 
     LogPrintf("CChainLocksHandler::%s -- processed new CLSIG (%s), peer=%d\n",
               __func__, clsig.ToString(), from);
+
+    if (lastNotifyChainLockBlockIndex != bestChainLockBlockIndex) {
+        lastNotifyChainLockBlockIndex = bestChainLockBlockIndex;
+        GetMainSignals().NotifyChainLock(bestChainLockBlockIndex);
+    }
 }
 
 void CChainLocksHandler::AcceptedBlockHeader(const CBlockIndex* pindexNew)
@@ -204,6 +209,11 @@ void CChainLocksHandler::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBl
         if (bestChainLockBlockIndex == pindexNew) {
             // we first got the CLSIG, then the header, and then the block was connected.
             // In this case there is no need to continue here.
+            // However, NotifyChainLock might not have been called yet, so call it now if needed
+            if (lastNotifyChainLockBlockIndex != bestChainLockBlockIndex) {
+                lastNotifyChainLockBlockIndex = bestChainLockBlockIndex;
+                GetMainSignals().NotifyChainLock(bestChainLockBlockIndex);
+            }
             return;
         }
 
diff --git a/src/llmq/quorums_chainlocks.h b/src/llmq/quorums_chainlocks.h
index 31065a478f9d..b7cd441e040a 100644
--- a/src/llmq/quorums_chainlocks.h
+++ b/src/llmq/quorums_chainlocks.h
@@ -55,6 +55,7 @@ class CChainLocksHandler : public CRecoveredSigsListener
 
     CChainLockSig bestChainLockWithKnownBlock;
     const CBlockIndex* bestChainLockBlockIndex{nullptr};
+    const CBlockIndex* lastNotifyChainLockBlockIndex{nullptr};
 
     int32_t lastSignedHeight{-1};
     uint256 lastSignedRequestId;
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index 86f374ec16a1..dc928f1b428b 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -18,6 +18,7 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) {
     g_signals.UpdatedBlockTip.connect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3));
     g_signals.SyncTransaction.connect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2, _3));
     g_signals.NotifyTransactionLock.connect(boost::bind(&CValidationInterface::NotifyTransactionLock, pwalletIn, _1));
+    g_signals.NotifyChainLock.connect(boost::bind(&CValidationInterface::NotifyChainLock, pwalletIn, _1));
     g_signals.UpdatedTransaction.connect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1));
     g_signals.SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
     g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
@@ -40,6 +41,7 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
     g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
     g_signals.SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
     g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1));
+    g_signals.NotifyChainLock.disconnect(boost::bind(&CValidationInterface::NotifyChainLock, pwalletIn, _1));
     g_signals.NotifyTransactionLock.disconnect(boost::bind(&CValidationInterface::NotifyTransactionLock, pwalletIn, _1));
     g_signals.SyncTransaction.disconnect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2, _3));
     g_signals.UpdatedBlockTip.disconnect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3));
@@ -61,6 +63,7 @@ void UnregisterAllValidationInterfaces() {
     g_signals.SetBestChain.disconnect_all_slots();
     g_signals.UpdatedTransaction.disconnect_all_slots();
     g_signals.NotifyTransactionLock.disconnect_all_slots();
+    g_signals.NotifyChainLock.disconnect_all_slots();
     g_signals.SyncTransaction.disconnect_all_slots();
     g_signals.UpdatedBlockTip.disconnect_all_slots();
     g_signals.NewPoWValidBlock.disconnect_all_slots();
diff --git a/src/validationinterface.h b/src/validationinterface.h
index f28144ed9724..5ad8e6ac773b 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -39,6 +39,7 @@ class CValidationInterface {
     virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {}
     virtual void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock) {}
     virtual void NotifyTransactionLock(const CTransaction &tx) {}
+    virtual void NotifyChainLock(const CBlockIndex* pindex) {}
     virtual void NotifyGovernanceVote(const CGovernanceVote &vote) {}
     virtual void NotifyGovernanceObject(const CGovernanceObject &object) {}
     virtual void NotifyInstantSendDoubleSpendAttempt(const CTransaction &currentTx, const CTransaction &previousTx) {}
@@ -76,6 +77,8 @@ struct CMainSignals {
     boost::signals2::signal<void (const CTransaction &, const CBlockIndex *pindex, int posInBlock)> SyncTransaction;
     /** Notifies listeners of an updated transaction lock without new data. */
     boost::signals2::signal<void (const CTransaction &)> NotifyTransactionLock;
+    /** Notifies listeners of a ChainLock. */
+    boost::signals2::signal<void (const CBlockIndex* pindex)> NotifyChainLock;
     /** Notifies listeners of a new governance vote. */
     boost::signals2::signal<void (const CGovernanceVote &)> NotifyGovernanceVote;
     /** Notifies listeners of a new governance object. */

From 5bbc1227490b562ff5fb88e795f57323c3401c0f Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Tue, 26 Feb 2019 07:46:04 +0100
Subject: [PATCH 02/33] Implement PushReconstructedRecoveredSig in
 CSigningManager

We can reconstruct recovered sigs from other P2P messages to avoid
re-validation of those. We will do this later in InstantSend code.
---
 src/llmq/quorums_signing.cpp | 20 ++++++++++++++++++++
 src/llmq/quorums_signing.h   |  6 ++++++
 2 files changed, 26 insertions(+)

diff --git a/src/llmq/quorums_signing.cpp b/src/llmq/quorums_signing.cpp
index 2239d364da03..fc394533c49c 100644
--- a/src/llmq/quorums_signing.cpp
+++ b/src/llmq/quorums_signing.cpp
@@ -440,11 +440,25 @@ void CSigningManager::CollectPendingRecoveredSigsToVerify(
     }
 }
 
+bool CSigningManager::ProcessPendingReconstructedRecoveredSigs()
+{
+    decltype(pendingReconstructedRecoveredSigs) l;
+    {
+        LOCK(cs);
+        l = std::move(pendingReconstructedRecoveredSigs);
+    }
+    for (auto& p : l) {
+        ProcessRecoveredSig(-1, p.first, p.second, *g_connman);
+    }
+}
+
 bool CSigningManager::ProcessPendingRecoveredSigs(CConnman& connman)
 {
     std::unordered_map<NodeId, std::list<CRecoveredSig>> recSigsByNode;
     std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher> quorums;
 
+    ProcessPendingReconstructedRecoveredSigs();
+
     CollectPendingRecoveredSigsToVerify(32, recSigsByNode, quorums);
     if (recSigsByNode.empty()) {
         return false;
@@ -550,6 +564,12 @@ void CSigningManager::ProcessRecoveredSig(NodeId nodeId, const CRecoveredSig& re
     }
 }
 
+void CSigningManager::PushReconstructedRecoveredSig(const llmq::CRecoveredSig& recoveredSig, const llmq::CQuorumCPtr& quorum)
+{
+    LOCK(cs);
+    pendingReconstructedRecoveredSigs.emplace_back(recoveredSig, quorum);
+}
+
 void CSigningManager::Cleanup()
 {
     int64_t now = GetTimeMillis();
diff --git a/src/llmq/quorums_signing.h b/src/llmq/quorums_signing.h
index 1d002435159f..6a7325de6139 100644
--- a/src/llmq/quorums_signing.h
+++ b/src/llmq/quorums_signing.h
@@ -126,6 +126,7 @@ class CSigningManager
 
     // Incoming and not verified yet
     std::unordered_map<NodeId, std::list<CRecoveredSig>> pendingRecoveredSigs;
+    std::list<std::pair<CRecoveredSig, CQuorumCPtr>> pendingReconstructedRecoveredSigs;
 
     // must be protected by cs
     FastRandomContext rnd;
@@ -142,6 +143,10 @@ class CSigningManager
 
     void ProcessMessage(CNode* pnode, const std::string& strCommand, CDataStream& vRecv, CConnman& connman);
 
+    // This is called when a recovered signature was was reconstructed from another P2P message and is known to be valid
+    // This is the case for example when a signature appears as part of InstantSend or ChainLocks
+    void PushReconstructedRecoveredSig(const CRecoveredSig& recoveredSig, const CQuorumCPtr& quorum);
+
 private:
     void ProcessMessageRecoveredSig(CNode* pfrom, const CRecoveredSig& recoveredSig, CConnman& connman);
     bool PreVerifyRecoveredSig(NodeId nodeId, const CRecoveredSig& recoveredSig, bool& retBan);
@@ -149,6 +154,7 @@ class CSigningManager
     void CollectPendingRecoveredSigsToVerify(size_t maxUniqueSessions,
             std::unordered_map<NodeId, std::list<CRecoveredSig>>& retSigShares,
             std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher>& retQuorums);
+    bool ProcessPendingReconstructedRecoveredSigs();
     bool ProcessPendingRecoveredSigs(CConnman& connman); // called from the worker thread of CSigSharesManager
     void ProcessRecoveredSig(NodeId nodeId, const CRecoveredSig& recoveredSig, const CQuorumCPtr& quorum, CConnman& connman);
     void Cleanup(); // called from the worker thread of CSigSharesManager

From 83dbcc483f38d6c481c90791e153a8ac3cda6cd7 Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Fri, 21 Sep 2018 16:01:04 +0200
Subject: [PATCH 03/33] Implement CInstantSendManager and related P2P messages

---
 src/Makefile.am                  |   2 +
 src/chainparams.cpp              |   2 +
 src/consensus/params.h           |   1 +
 src/dsnotificationinterface.cpp  |   7 +
 src/dsnotificationinterface.h    |   1 +
 src/llmq/quorums_init.cpp        |  10 +
 src/llmq/quorums_instantsend.cpp | 883 +++++++++++++++++++++++++++++++
 src/llmq/quorums_instantsend.h   | 149 ++++++
 src/net_processing.cpp           |  15 +
 src/protocol.cpp                 |   3 +
 src/protocol.h                   |   2 +
 11 files changed, 1075 insertions(+)
 create mode 100644 src/llmq/quorums_instantsend.cpp
 create mode 100644 src/llmq/quorums_instantsend.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 04a5efa3a8e7..e87247f8b49f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -173,6 +173,7 @@ BITCOIN_CORE_H = \
   llmq/quorums_dkgsessionmgr.h \
   llmq/quorums_dkgsession.h \
   llmq/quorums_init.h \
+  llmq/quorums_instantsend.h \
   llmq/quorums_signing.h \
   llmq/quorums_signing_shares.h \
   llmq/quorums_utils.h \
@@ -291,6 +292,7 @@ libdash_server_a_SOURCES = \
   llmq/quorums_dkgsessionmgr.cpp \
   llmq/quorums_dkgsession.cpp \
   llmq/quorums_init.cpp \
+  llmq/quorums_instantsend.cpp \
   llmq/quorums_signing.cpp \
   llmq/quorums_signing_shares.cpp \
   llmq/quorums_utils.cpp \
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 2cc5aafb2f88..314584f7d788 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -623,6 +623,7 @@ class CDevNetParams : public CChainParams {
         consensus.llmqs[Consensus::LLMQ_400_60] = llmq400_60;
         consensus.llmqs[Consensus::LLMQ_400_85] = llmq400_85;
         consensus.llmqChainLocks = Consensus::LLMQ_50_60;
+        consensus.llmqForInstantSend = Consensus::LLMQ_50_60;
 
         fMiningRequiresPeers = true;
         fDefaultConsistencyChecks = false;
@@ -787,6 +788,7 @@ class CRegTestParams : public CChainParams {
         consensus.llmqs[Consensus::LLMQ_10_60] = llmq10_60;
         consensus.llmqs[Consensus::LLMQ_50_60] = llmq50_60;
         consensus.llmqChainLocks = Consensus::LLMQ_10_60;
+        consensus.llmqForInstantSend = Consensus::LLMQ_10_60;
     }
 
     void UpdateBIP9Parameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout, int64_t nWindowSize, int64_t nThreshold)
diff --git a/src/consensus/params.h b/src/consensus/params.h
index 083cbacfa20c..b2efd4ca755c 100644
--- a/src/consensus/params.h
+++ b/src/consensus/params.h
@@ -174,6 +174,7 @@ struct Params {
 
     std::map<LLMQType, LLMQParams> llmqs;
     LLMQType llmqChainLocks;
+    LLMQType llmqForInstantSend{LLMQ_NONE};
 };
 } // namespace Consensus
 
diff --git a/src/dsnotificationinterface.cpp b/src/dsnotificationinterface.cpp
index 9eeac0218ef5..954de50d0bae 100644
--- a/src/dsnotificationinterface.cpp
+++ b/src/dsnotificationinterface.cpp
@@ -18,6 +18,7 @@
 
 #include "llmq/quorums.h"
 #include "llmq/quorums_chainlocks.h"
+#include "llmq/quorums_instantsend.h"
 #include "llmq/quorums_dkgsessionmgr.h"
 
 void CDSNotificationInterface::InitializeCurrentBlockTip()
@@ -72,6 +73,7 @@ void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, con
 
 void CDSNotificationInterface::SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock)
 {
+    llmq::quorumInstantSendManager->SyncTransaction(tx, pindex, posInBlock);
     instantsend.SyncTransaction(tx, pindex, posInBlock);
     CPrivateSend::SyncTransaction(tx, pindex, posInBlock);
 }
@@ -82,3 +84,8 @@ void CDSNotificationInterface::NotifyMasternodeListChanged(const CDeterministicM
     governance.CheckMasternodeOrphanVotes(connman);
     governance.UpdateCachesAndClean();
 }
+
+void CDSNotificationInterface::NotifyChainLock(const CBlockIndex* pindex)
+{
+    llmq::quorumInstantSendManager->NotifyChainLock(pindex);
+}
diff --git a/src/dsnotificationinterface.h b/src/dsnotificationinterface.h
index d800289d1215..f4e97d7ed5c6 100644
--- a/src/dsnotificationinterface.h
+++ b/src/dsnotificationinterface.h
@@ -23,6 +23,7 @@ class CDSNotificationInterface : public CValidationInterface
     void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override;
     void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock) override;
     void NotifyMasternodeListChanged(const CDeterministicMNList& newList) override;
+    void NotifyChainLock(const CBlockIndex* pindex);
 
 private:
     CConnman& connman;
diff --git a/src/llmq/quorums_init.cpp b/src/llmq/quorums_init.cpp
index ec03b46403e9..a2620683b6fc 100644
--- a/src/llmq/quorums_init.cpp
+++ b/src/llmq/quorums_init.cpp
@@ -10,6 +10,7 @@
 #include "quorums_chainlocks.h"
 #include "quorums_debug.h"
 #include "quorums_dkgsessionmgr.h"
+#include "quorums_instantsend.h"
 #include "quorums_signing.h"
 #include "quorums_signing_shares.h"
 
@@ -29,10 +30,13 @@ void InitLLMQSystem(CEvoDB& evoDb, CScheduler* scheduler, bool unitTests)
     quorumSigSharesManager = new CSigSharesManager();
     quorumSigningManager = new CSigningManager(unitTests);
     chainLocksHandler = new CChainLocksHandler(scheduler);
+    quorumInstantSendManager = new CInstantSendManager(scheduler);
 }
 
 void DestroyLLMQSystem()
 {
+    delete quorumInstantSendManager;
+    quorumInstantSendManager = nullptr;
     delete chainLocksHandler;
     chainLocksHandler = nullptr;
     delete quorumSigningManager;
@@ -64,10 +68,16 @@ void StartLLMQSystem()
     if (chainLocksHandler) {
         chainLocksHandler->RegisterAsRecoveredSigsListener();
     }
+    if (quorumInstantSendManager) {
+        quorumInstantSendManager->RegisterAsRecoveredSigsListener();
+    }
 }
 
 void StopLLMQSystem()
 {
+    if (quorumInstantSendManager) {
+        quorumInstantSendManager->UnregisterAsRecoveredSigsListener();
+    }
     if (chainLocksHandler) {
         chainLocksHandler->UnregisterAsRecoveredSigsListener();
     }
diff --git a/src/llmq/quorums_instantsend.cpp b/src/llmq/quorums_instantsend.cpp
new file mode 100644
index 000000000000..737c22b29a10
--- /dev/null
+++ b/src/llmq/quorums_instantsend.cpp
@@ -0,0 +1,883 @@
+// Copyright (c) 2019 The Dash Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "quorums_chainlocks.h"
+#include "quorums_instantsend.h"
+#include "quorums_utils.h"
+
+#include "bls/bls_batchverifier.h"
+#include "chainparams.h"
+#include "coins.h"
+#include "txmempool.h"
+#include "masternode-sync.h"
+#include "net_processing.h"
+#include "scheduler.h"
+#include "spork.h"
+#include "validation.h"
+#include "wallet/wallet.h"
+
+// needed for nCompleteTXLocks
+#include "instantx.h"
+
+#include <boost/algorithm/string/replace.hpp>
+
+namespace llmq
+{
+
+static const std::string INPUTLOCK_REQUESTID_PREFIX = "inlock";
+static const std::string IXLOCK_REQUESTID_PREFIX = "ixlock";
+
+CInstantSendManager* quorumInstantSendManager;
+
+uint256 CInstantXLock::GetRequestId() const
+{
+    CHashWriter hw(SER_GETHASH, 0);
+    hw << IXLOCK_REQUESTID_PREFIX;
+    hw << inputs;
+    return hw.GetHash();
+}
+
+CInstantSendManager::CInstantSendManager(CScheduler* _scheduler) :
+    scheduler(_scheduler)
+{
+}
+
+CInstantSendManager::~CInstantSendManager()
+{
+}
+
+void CInstantSendManager::RegisterAsRecoveredSigsListener()
+{
+    quorumSigningManager->RegisterRecoveredSigsListener(this);
+}
+
+void CInstantSendManager::UnregisterAsRecoveredSigsListener()
+{
+    quorumSigningManager->UnregisterRecoveredSigsListener(this);
+}
+
+bool CInstantSendManager::ProcessTx(CNode* pfrom, const CTransaction& tx, CConnman& connman, const Consensus::Params& params)
+{
+    if (!IsNewInstantSendEnabled()) {
+        return true;
+    }
+
+    auto llmqType = params.llmqForInstantSend;
+    if (llmqType == Consensus::LLMQ_NONE) {
+        return true;
+    }
+    if (!fMasternodeMode) {
+        return true;
+    }
+
+    // Ignore any InstantSend messages until blockchain is synced
+    if (!masternodeSync.IsBlockchainSynced()) {
+        return true;
+    }
+
+    if (IsConflicted(tx)) {
+        return false;
+    }
+
+    if (!CheckCanLock(tx, true, params)) {
+        return false;
+    }
+
+    std::vector<uint256> ids;
+    ids.reserve(tx.vin.size());
+
+    for (const auto& in : tx.vin) {
+        auto id = ::SerializeHash(std::make_pair(INPUTLOCK_REQUESTID_PREFIX, in.prevout));
+        ids.emplace_back(id);
+    }
+
+    {
+        LOCK(cs);
+        size_t alreadyVotedCount = 0;
+        for (size_t i = 0; i < ids.size(); i++) {
+            auto it = inputVotes.find(ids[i]);
+            if (it != inputVotes.end()) {
+                if (it->second != tx.GetHash()) {
+                    LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s: input %s is conflicting with ixlock %s\n", __func__,
+                            tx.GetHash().ToString(), tx.vin[i].prevout.ToStringShort(), it->second.ToString());
+                    return false;
+                }
+                alreadyVotedCount++;
+            }
+        }
+        if (alreadyVotedCount == ids.size()) {
+            return true;
+        }
+
+        for (auto& id : ids) {
+            inputVotes.emplace(id, tx.GetHash());
+        }
+    }
+
+    // don't even try the actual signing if any input is conflicting
+    for (auto& id : ids) {
+        if (quorumSigningManager->IsConflicting(llmqType, id, tx.GetHash())) {
+            return false;
+        }
+    }
+    for (auto& id : ids) {
+        quorumSigningManager->AsyncSignIfMember(llmqType, id, tx.GetHash());
+    }
+
+    // We might have received all input locks before we got the corresponding TX. In this case, we have to sign the
+    // ixlock now instead of waiting for the input locks.
+    TrySignInstantXLock(tx);
+
+    return true;
+}
+
+bool CInstantSendManager::CheckCanLock(const CTransaction& tx, bool printDebug, const Consensus::Params& params)
+{
+    int nInstantSendConfirmationsRequired = params.nInstantSendConfirmationsRequired;
+
+    uint256 txHash = tx.GetHash();
+    CAmount nValueIn = 0;
+    for (const auto& in : tx.vin) {
+        CAmount v = 0;
+        if (!CheckCanLock(in.prevout, printDebug, &txHash, &v, params)) {
+            return false;
+        }
+
+        nValueIn += v;
+    }
+
+    // TODO decide if we should limit max input values. This was ok to do in the old system, but in the new system
+    // where we want to have all TXs locked at some point, this is counterproductive (especially when ChainLocks later
+    // depend on all TXs being locked first)
+//    CAmount maxValueIn = sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE);
+//    if (nValueIn > maxValueIn * COIN) {
+//        if (printDebug) {
+//            LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s: TX input value too high. nValueIn=%f, maxValueIn=%d", __func__,
+//                     tx.GetHash().ToString(), nValueIn / (double)COIN, maxValueIn);
+//        }
+//        return false;
+//    }
+
+    return true;
+}
+
+bool CInstantSendManager::CheckCanLock(const COutPoint& outpoint, bool printDebug, const uint256* _txHash, CAmount* retValue, const Consensus::Params& params)
+{
+    int nInstantSendConfirmationsRequired = params.nInstantSendConfirmationsRequired;
+
+    if (IsLocked(outpoint.hash)) {
+        // if prevout was ix locked, allow locking of descendants (no matter if prevout is in mempool or already mined)
+        return true;
+    }
+
+    static uint256 txHashNull;
+    const uint256* txHash = &txHashNull;
+    if (_txHash) {
+        txHash = _txHash;
+    }
+
+    auto mempoolTx = mempool.get(outpoint.hash);
+    if (mempoolTx) {
+        if (printDebug) {
+            LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s: parent mempool TX %s is not locked\n", __func__,
+                     txHash->ToString(), outpoint.hash.ToString());
+        }
+        return false;
+    }
+
+    Coin coin;
+    const CBlockIndex* pindexMined = nullptr;
+    {
+        LOCK(cs_main);
+        if (!pcoinsTip->GetCoin(outpoint, coin) || coin.IsSpent()) {
+            if (printDebug) {
+                LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s: failed to find UTXO %s\n", __func__,
+                         txHash->ToString(), outpoint.ToStringShort());
+            }
+            return false;
+        }
+        pindexMined = chainActive[coin.nHeight];
+    }
+
+    int nTxAge = chainActive.Height() - coin.nHeight + 1;
+    // 1 less than the "send IX" gui requires, in case of a block propagating the network at the time
+    int nConfirmationsRequired = nInstantSendConfirmationsRequired - 1;
+
+    if (nTxAge < nConfirmationsRequired) {
+        if (!llmq::chainLocksHandler->HasChainLock(pindexMined->nHeight, pindexMined->GetBlockHash())) {
+            if (printDebug) {
+                LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s: outpoint %s too new and not ChainLocked. nTxAge=%d, nConfirmationsRequired=%d\n", __func__,
+                         txHash->ToString(), outpoint.ToStringShort(), nTxAge, nConfirmationsRequired);
+            }
+            return false;
+        }
+    }
+
+    if (retValue) {
+        *retValue = coin.out.nValue;
+    }
+
+    return true;
+}
+
+void CInstantSendManager::HandleNewRecoveredSig(const CRecoveredSig& recoveredSig)
+{
+    if (!IsNewInstantSendEnabled()) {
+        return;
+    }
+
+    auto llmqType = Params().GetConsensus().llmqForInstantSend;
+    if (llmqType == Consensus::LLMQ_NONE) {
+        return;
+    }
+    auto& params = Params().GetConsensus().llmqs.at(llmqType);
+
+    uint256 txid;
+    bool isInstantXLock = false;
+    {
+        LOCK(cs);
+        auto it = inputVotes.find(recoveredSig.id);
+        if (it != inputVotes.end()) {
+            txid = it->second;
+        }
+        if (creatingInstantXLocks.count(recoveredSig.id)) {
+            isInstantXLock = true;
+        }
+    }
+    if (!txid.IsNull()) {
+        HandleNewInputLockRecoveredSig(recoveredSig, txid);
+    } else if (isInstantXLock) {
+        HandleNewInstantXLockRecoveredSig(recoveredSig);
+    }
+}
+
+void CInstantSendManager::HandleNewInputLockRecoveredSig(const CRecoveredSig& recoveredSig, const uint256& txid)
+{
+    auto llmqType = Params().GetConsensus().llmqForInstantSend;
+
+    CTransactionRef tx;
+    uint256 hashBlock;
+    if (!GetTransaction(txid, tx, Params().GetConsensus(), hashBlock, true)) {
+        return;
+    }
+
+    if (LogAcceptCategory("instantsend")) {
+        for (auto& in : tx->vin) {
+            auto id = ::SerializeHash(std::make_pair(INPUTLOCK_REQUESTID_PREFIX, in.prevout));
+            if (id == recoveredSig.id) {
+                LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s: got recovered sig for input %s\n", __func__,
+                         txid.ToString(), in.prevout.ToStringShort());
+                break;
+            }
+        }
+    }
+
+    TrySignInstantXLock(*tx);
+}
+
+void CInstantSendManager::TrySignInstantXLock(const CTransaction& tx)
+{
+    auto llmqType = Params().GetConsensus().llmqForInstantSend;
+
+    for (auto& in : tx.vin) {
+        auto id = ::SerializeHash(std::make_pair(INPUTLOCK_REQUESTID_PREFIX, in.prevout));
+        if (!quorumSigningManager->HasRecoveredSig(llmqType, id, tx.GetHash())) {
+            return;
+        }
+    }
+
+    LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s: got all recovered sigs, creating CInstantXLock\n", __func__,
+            tx.GetHash().ToString());
+
+    CInstantXLockInfo ixlockInfo;
+    ixlockInfo.time = GetTimeMillis();
+    ixlockInfo.ixlock.txid = tx.GetHash();
+    for (auto& in : tx.vin) {
+        ixlockInfo.ixlock.inputs.emplace_back(in.prevout);
+    }
+
+    auto id = ixlockInfo.ixlock.GetRequestId();
+
+    if (quorumSigningManager->HasRecoveredSigForId(llmqType, id)) {
+        return;
+    }
+
+    {
+        LOCK(cs);
+        auto e = creatingInstantXLocks.emplace(id, ixlockInfo);
+        if (!e.second) {
+            return;
+        }
+        txToCreatingInstantXLocks.emplace(tx.GetHash(), &e.first->second);
+    }
+
+    quorumSigningManager->AsyncSignIfMember(llmqType, id, tx.GetHash());
+}
+
+void CInstantSendManager::HandleNewInstantXLockRecoveredSig(const llmq::CRecoveredSig& recoveredSig)
+{
+    CInstantXLockInfo ixlockInfo;
+
+    {
+        LOCK(cs);
+        auto it = creatingInstantXLocks.find(recoveredSig.id);
+        if (it == creatingInstantXLocks.end()) {
+            return;
+        }
+
+        ixlockInfo = std::move(it->second);
+        creatingInstantXLocks.erase(it);
+        txToCreatingInstantXLocks.erase(ixlockInfo.ixlock.txid);
+    }
+
+    if (ixlockInfo.ixlock.txid != recoveredSig.msgHash) {
+        LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s: ixlock conflicts with %s, dropping own version", __func__,
+                ixlockInfo.ixlock.txid.ToString(), recoveredSig.msgHash.ToString());
+        return;
+    }
+
+    ixlockInfo.ixlock.sig = recoveredSig.sig;
+    ProcessInstantXLock(-1, ::SerializeHash(ixlockInfo.ixlock), ixlockInfo.ixlock);
+}
+
+void CInstantSendManager::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman)
+{
+    if (!IsNewInstantSendEnabled()) {
+        return;
+    }
+
+    if (strCommand == NetMsgType::IXLOCK) {
+        CInstantXLock ixlock;
+        vRecv >> ixlock;
+        ProcessMessageInstantXLock(pfrom, ixlock, connman);
+    }
+}
+
+void CInstantSendManager::ProcessMessageInstantXLock(CNode* pfrom, const llmq::CInstantXLock& ixlock, CConnman& connman)
+{
+    bool ban = false;
+    if (!PreVerifyInstantXLock(pfrom->id, ixlock, ban)) {
+        if (ban) {
+            LOCK(cs_main);
+            Misbehaving(pfrom->id, 100);
+        }
+        return;
+    }
+
+    auto hash = ::SerializeHash(ixlock);
+
+    LOCK(cs);
+    if (pendingInstantXLocks.count(hash) || finalInstantXLocks.count(hash)) {
+        return;
+    }
+
+    LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, ixlock=%s: received ixlock, peer=%d\n", __func__,
+            ixlock.txid.ToString(), hash.ToString(), pfrom->id);
+
+    pendingInstantXLocks.emplace(hash, std::make_pair(pfrom->id, std::move(ixlock)));
+
+    if (!hasScheduledProcessPending) {
+        hasScheduledProcessPending = true;
+        scheduler->schedule([&] {
+            ProcessPendingInstantXLocks();
+        }, boost::chrono::system_clock::now() + boost::chrono::milliseconds(100));
+    }
+}
+
+bool CInstantSendManager::PreVerifyInstantXLock(NodeId nodeId, const llmq::CInstantXLock& ixlock, bool& retBan)
+{
+    retBan = false;
+
+    if (ixlock.txid.IsNull() || !ixlock.sig.IsValid() || ixlock.inputs.empty()) {
+        retBan = true;
+        return false;
+    }
+
+    std::set<COutPoint> dups;
+    for (auto& o : ixlock.inputs) {
+        if (!dups.emplace(o).second) {
+            retBan = true;
+            return false;
+        }
+    }
+
+    return true;
+}
+
+void CInstantSendManager::ProcessPendingInstantXLocks()
+{
+    auto llmqType = Params().GetConsensus().llmqForInstantSend;
+
+    decltype(pendingInstantXLocks) pend;
+
+    {
+        LOCK(cs);
+        hasScheduledProcessPending = false;
+        pend = std::move(pendingInstantXLocks);
+    }
+
+    if (!IsNewInstantSendEnabled()) {
+        return;
+    }
+
+    int tipHeight;
+    {
+        LOCK(cs_main);
+        tipHeight = chainActive.Height();
+    }
+
+    CBLSBatchVerifier<NodeId, uint256> batchVerifier(false, true, 8);
+    std::unordered_map<uint256, std::pair<CQuorumCPtr, CRecoveredSig>> recSigs;
+
+    for (const auto& p : pend) {
+        auto& hash = p.first;
+        auto nodeId = p.second.first;
+        auto& ixlock = p.second.second;
+
+        auto id = ixlock.GetRequestId();
+
+        // no need to verify an IXLOCK if we already have verified the recovered sig that belongs to it
+        if (quorumSigningManager->HasRecoveredSig(llmqType, id, ixlock.txid)) {
+            continue;
+        }
+
+        auto quorum = quorumSigningManager->SelectQuorumForSigning(llmqType, tipHeight, id);
+        if (!quorum) {
+            // should not happen, but if one fails to select, all others will also fail to select
+            return;
+        }
+        uint256 signHash = CLLMQUtils::BuildSignHash(llmqType, quorum->quorumHash, id, ixlock.txid);
+        batchVerifier.PushMessage(nodeId, hash, signHash, ixlock.sig, quorum->quorumPublicKey);
+
+        // We can reconstruct the CRecoveredSig objects from the ixlock and pass it to the signing manager, which
+        // avoids unnecessary double-verification of the signature. We however only do this when verification here
+        // turns out to be good (which is checked further down)
+        if (!quorumSigningManager->HasRecoveredSigForId(llmqType, id)) {
+            CRecoveredSig recSig;
+            recSig.llmqType = llmqType;
+            recSig.quorumHash = quorum->quorumHash;
+            recSig.id = id;
+            recSig.msgHash = ixlock.txid;
+            recSig.sig = ixlock.sig;
+            recSigs.emplace(std::piecewise_construct,
+                    std::forward_as_tuple(hash),
+                    std::forward_as_tuple(std::move(quorum), std::move(recSig)));
+        }
+    }
+
+    batchVerifier.Verify();
+
+    if (!batchVerifier.badSources.empty()) {
+        LOCK(cs_main);
+        for (auto& nodeId : batchVerifier.badSources) {
+            // Let's not be too harsh, as the peer might simply be unlucky and might have sent us an old lock which
+            // does not validate anymore due to changed quorums
+            Misbehaving(nodeId, 20);
+        }
+    }
+    for (const auto& p : pend) {
+        auto& hash = p.first;
+        auto nodeId = p.second.first;
+        auto& ixlock = p.second.second;
+
+        if (batchVerifier.badMessages.count(hash)) {
+            LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, ixlock=%s: invalid sig in ixlock, peer=%d\n", __func__,
+                     ixlock.txid.ToString(), hash.ToString(), nodeId);
+            continue;
+        }
+
+        ProcessInstantXLock(nodeId, hash, ixlock);
+
+        // See comment further on top. We pass a reconstructed recovered sig to the signing manager to avoid
+        // double-verification of the sig.
+        auto it = recSigs.find(hash);
+        if (it != recSigs.end()) {
+            auto& quorum = it->second.first;
+            auto& recSig = it->second.second;
+            if (!quorumSigningManager->HasRecoveredSigForId(llmqType, recSig.id)) {
+                recSig.UpdateHash();
+                LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, ixlock=%s: passing reconstructed recSig to signing mgr, peer=%d\n", __func__,
+                         ixlock.txid.ToString(), hash.ToString(), nodeId);
+                quorumSigningManager->PushReconstructedRecoveredSig(recSig, quorum);
+            }
+        }
+    }
+}
+
+void CInstantSendManager::ProcessInstantXLock(NodeId from, const uint256& hash, const CInstantXLock& ixlock)
+{
+    {
+        LOCK(cs_main);
+        g_connman->RemoveAskFor(hash);
+    }
+
+    CInstantXLockInfo ixlockInfo;
+    ixlockInfo.time = GetTimeMillis();
+    ixlockInfo.ixlock = ixlock;
+    ixlockInfo.ixlockHash = hash;
+
+    uint256 hashBlock;
+    // we ignore failure here as we must be able to propagate the lock even if we don't have the TX locally
+    if (GetTransaction(ixlock.txid, ixlockInfo.tx, Params().GetConsensus(), hashBlock)) {
+        if (!hashBlock.IsNull()) {
+            {
+                LOCK(cs_main);
+                ixlockInfo.pindexMined = mapBlockIndex.at(hashBlock);
+            }
+
+            // Let's see if the TX that was locked by this ixlock is already mined in a ChainLocked block. If yes,
+            // we can simply ignore the ixlock, as the ChainLock implies locking of all TXs in that chain
+            if (llmq::chainLocksHandler->HasChainLock(ixlockInfo.pindexMined->nHeight, ixlockInfo.pindexMined->GetBlockHash())) {
+                LogPrint("instantsend", "CInstantSendManager::%s -- txlock=%s, ixlock=%s: dropping ixlock as it already got a ChainLock in block %s, peer=%d\n", __func__,
+                         ixlock.txid.ToString(), hash.ToString(), hashBlock.ToString(), from);
+                return;
+            }
+        }
+    }
+
+    {
+        LOCK(cs);
+        auto e = finalInstantXLocks.emplace(hash, ixlockInfo);
+        if (!e.second) {
+            return;
+        }
+        auto ixlockInfoPtr = &e.first->second;
+
+        creatingInstantXLocks.erase(ixlockInfoPtr->ixlock.GetRequestId());
+        txToCreatingInstantXLocks.erase(ixlockInfoPtr->ixlock.txid);
+
+        LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, ixlock=%s: processsing ixlock, peer=%d\n", __func__,
+                 ixlock.txid.ToString(), hash.ToString(), from);
+
+        if (!txToInstantXLock.emplace(ixlock.txid, ixlockInfoPtr).second) {
+            LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, ixlock=%s: duplicate ixlock, other ixlock=%s, peer=%d\n", __func__,
+                    ixlock.txid.ToString(), hash.ToString(), txToInstantXLock[ixlock.txid]->ixlockHash.ToString(), from);
+            txToInstantXLock.erase(hash);
+            return;
+        }
+        for (size_t i = 0; i < ixlock.inputs.size(); i++) {
+            auto& in = ixlock.inputs[i];
+            if (!inputToInstantXLock.emplace(in, ixlockInfoPtr).second) {
+                LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, ixlock=%s: conflicting input in ixlock. input=%s, other ixlock=%s, peer=%d\n", __func__,
+                         ixlock.txid.ToString(), hash.ToString(), in.ToStringShort(), inputToInstantXLock[in]->ixlockHash.ToString(), from);
+                txToInstantXLock.erase(hash);
+                for (size_t j = 0; j < i; j++) {
+                    inputToInstantXLock.erase(ixlock.inputs[j]);
+                }
+                return;
+            }
+        }
+    }
+
+    CInv inv(MSG_IXLOCK, hash);
+    g_connman->RelayInv(inv);
+
+    RemoveMempoolConflictsForLock(hash, ixlock);
+    RetryLockMempoolTxs(ixlock.txid);
+
+    UpdateWalletTransaction(ixlock.txid);
+}
+
+void CInstantSendManager::UpdateWalletTransaction(const uint256& txid)
+{
+#ifdef ENABLE_WALLET
+    if (!pwalletMain) {
+        return;
+    }
+
+    if (pwalletMain->UpdatedTransaction(txid)) {
+        // bumping this to update UI
+        nCompleteTXLocks++;
+        // notify an external script once threshold is reached
+        std::string strCmd = GetArg("-instantsendnotify", "");
+        if (!strCmd.empty()) {
+            boost::replace_all(strCmd, "%s", txid.GetHex());
+            boost::thread t(runCommand, strCmd); // thread runs free
+        }
+    }
+#endif
+
+    LOCK(cs);
+    auto it = txToInstantXLock.find(txid);
+    if (it == txToInstantXLock.end()) {
+        return;
+    }
+    if (it->second->tx == nullptr) {
+        return;
+    }
+
+    GetMainSignals().NotifyTransactionLock(*it->second->tx);
+}
+
+void CInstantSendManager::SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int posInBlock)
+{
+    if (!IsNewInstantSendEnabled()) {
+        return;
+    }
+
+    {
+        LOCK(cs);
+        auto it = txToInstantXLock.find(tx.GetHash());
+        if (it == txToInstantXLock.end()) {
+            return;
+        }
+        auto ixlockInfo = it->second;
+        if (ixlockInfo->tx == nullptr) {
+            ixlockInfo->tx = MakeTransactionRef(tx);
+        }
+
+        if (posInBlock == CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK) {
+            UpdateIxLockMinedBlock(ixlockInfo, nullptr);
+            return;
+        }
+        UpdateIxLockMinedBlock(ixlockInfo, pindex);
+    }
+
+    if (IsLocked(tx.GetHash())) {
+        RetryLockMempoolTxs(tx.GetHash());
+    }
+}
+
+void CInstantSendManager::NotifyChainLock(const CBlockIndex* pindex)
+{
+    {
+        LOCK(cs);
+
+        // Let's find all ixlocks that correspond to TXs which are part of the freshly ChainLocked chain and then delete
+        // the ixlocks. We do this because the ChainLocks imply locking and thus it's not needed to further track
+        // or propagate the ixlocks
+        std::unordered_set<uint256> toDelete;
+        while (pindex && pindex != pindexLastChainLock) {
+            auto its = blockToInstantXLocks.equal_range(pindex->GetBlockHash());
+            while (its.first != its.second) {
+                auto ixlockInfo = its.first->second;
+                LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, ixlock=%s: removing ixlock as it got ChainLocked in block %s\n", __func__,
+                         ixlockInfo->ixlock.txid.ToString(), ixlockInfo->ixlockHash.ToString(), pindex->GetBlockHash().ToString());
+                toDelete.emplace(its.first->second->ixlockHash);
+                ++its.first;
+            }
+
+            pindex = pindex->pprev;
+        }
+
+        pindexLastChainLock = pindex;
+
+        for (auto& ixlockHash : toDelete) {
+            RemoveFinalIxLock(ixlockHash);
+        }
+    }
+
+    RetryLockMempoolTxs(uint256());
+}
+
+void CInstantSendManager::UpdateIxLockMinedBlock(llmq::CInstantXLockInfo* ixlockInfo, const CBlockIndex* pindex)
+{
+    AssertLockHeld(cs);
+    
+    if (ixlockInfo->pindexMined == pindex) {
+        return;
+    }
+    
+    if (ixlockInfo->pindexMined) {
+        auto its = blockToInstantXLocks.equal_range(ixlockInfo->pindexMined->GetBlockHash());
+        while (its.first != its.second) {
+            if (its.first->second == ixlockInfo) {
+                its.first = blockToInstantXLocks.erase(its.first);
+            } else {
+                ++its.first;
+            }
+        }
+    }
+    
+    if (pindex) {
+        blockToInstantXLocks.emplace(pindex->GetBlockHash(), ixlockInfo);
+    }
+    
+    ixlockInfo->pindexMined = pindex;
+}
+
+void CInstantSendManager::RemoveFinalIxLock(const uint256& hash)
+{
+    AssertLockHeld(cs);
+    
+    auto it = finalInstantXLocks.find(hash);
+    if (it == finalInstantXLocks.end()) {
+        return;
+    }
+    auto& ixlockInfo = it->second;
+
+    txToInstantXLock.erase(ixlockInfo.ixlock.txid);
+    for (auto& in : ixlockInfo.ixlock.inputs) {
+        auto inputRequestId = ::SerializeHash(std::make_pair(INPUTLOCK_REQUESTID_PREFIX, in));
+        inputVotes.erase(inputRequestId);
+        inputToInstantXLock.erase(in);
+    }
+    UpdateIxLockMinedBlock(&ixlockInfo, nullptr);
+}
+
+void CInstantSendManager::RemoveMempoolConflictsForLock(const uint256& hash, const CInstantXLock& ixlock)
+{
+    LOCK(mempool.cs);
+
+    std::unordered_map<uint256, CTransactionRef> toDelete;
+
+    for (auto& in : ixlock.inputs) {
+        auto it = mempool.mapNextTx.find(in);
+        if (it == mempool.mapNextTx.end()) {
+            continue;
+        }
+        if (it->second->GetHash() != ixlock.txid) {
+            toDelete.emplace(it->second->GetHash(), mempool.get(it->second->GetHash()));
+
+            LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, ixlock=%s: mempool TX %s with input %s conflicts with ixlock\n", __func__,
+                     ixlock.txid.ToString(), hash.ToString(), it->second->GetHash().ToString(), in.ToStringShort());
+        }
+    }
+
+    for (auto& p : toDelete) {
+        mempool.removeRecursive(*p.second, MemPoolRemovalReason::CONFLICT);
+    }
+}
+
+void CInstantSendManager::RetryLockMempoolTxs(const uint256& lockedParentTx)
+{
+    // Let's retry all mempool TXs which don't have an ixlock yet and where the parents got ChainLocked now
+
+    std::unordered_map<uint256, CTransactionRef> txs;
+
+    {
+        LOCK(mempool.cs);
+
+        if (lockedParentTx.IsNull()) {
+            txs.reserve(mempool.mapTx.size());
+            for (auto it = mempool.mapTx.begin(); it != mempool.mapTx.end(); ++it) {
+                txs.emplace(it->GetTx().GetHash(), it->GetSharedTx());
+            }
+        } else {
+            auto it = mempool.mapNextTx.lower_bound(COutPoint(lockedParentTx, 0));
+            while (it != mempool.mapNextTx.end() && it->first->hash == lockedParentTx) {
+                txs.emplace(it->second->GetHash(), mempool.get(it->second->GetHash()));
+                ++it;
+            }
+        }
+    }
+    for (auto& p : txs) {
+        auto& tx = p.second;
+        {
+            LOCK(cs);
+            if (txToCreatingInstantXLocks.count(tx->GetHash())) {
+                // we're already in the middle of locking this one
+                continue;
+            }
+            if (IsLocked(tx->GetHash())) {
+                continue;
+            }
+            if (IsConflicted(*tx)) {
+                // should not really happen as we have already filtered these out
+                continue;
+            }
+        }
+
+        // CheckCanLock is already called by ProcessTx, so we should avoid calling it twice. But we also shouldn't spam
+        // the logs when retrying TXs that are not ready yet.
+        if (LogAcceptCategory("instantsend")) {
+            if (!CheckCanLock(*tx, false, Params().GetConsensus())) {
+                continue;
+            }
+            LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s: retrying to lock\n", __func__,
+                     tx->GetHash().ToString());
+        }
+
+        ProcessTx(nullptr, *tx, *g_connman, Params().GetConsensus());
+    }
+}
+
+bool CInstantSendManager::AlreadyHave(const CInv& inv)
+{
+    if (!IsNewInstantSendEnabled()) {
+        return true;
+    }
+
+    LOCK(cs);
+    return finalInstantXLocks.count(inv.hash) != 0 || pendingInstantXLocks.count(inv.hash) != 0;
+}
+
+bool CInstantSendManager::GetInstantXLockByHash(const uint256& hash, llmq::CInstantXLock& ret)
+{
+    if (!IsNewInstantSendEnabled()) {
+        return false;
+    }
+
+    LOCK(cs);
+    auto it = finalInstantXLocks.find(hash);
+    if (it == finalInstantXLocks.end()) {
+        return false;
+    }
+    ret = it->second.ixlock;
+    return true;
+}
+
+bool CInstantSendManager::IsLocked(const uint256& txHash)
+{
+    if (!IsNewInstantSendEnabled()) {
+        return false;
+    }
+
+    LOCK(cs);
+    return txToInstantXLock.count(txHash) != 0;
+}
+
+bool CInstantSendManager::IsConflicted(const CTransaction& tx)
+{
+    LOCK(cs);
+    uint256 dummy;
+    return GetConflictingTx(tx, dummy);
+}
+
+bool CInstantSendManager::GetConflictingTx(const CTransaction& tx, uint256& retConflictTxHash)
+{
+    if (!IsNewInstantSendEnabled()) {
+        return false;
+    }
+
+    LOCK(cs);
+    for (const auto& in : tx.vin) {
+        auto it = inputToInstantXLock.find(in.prevout);
+        if (it == inputToInstantXLock.end()) {
+            continue;
+        }
+
+        if (it->second->ixlock.txid != tx.GetHash()) {
+            retConflictTxHash = it->second->ixlock.txid;
+            return true;
+        }
+    }
+    return false;
+}
+
+bool IsOldInstantSendEnabled()
+{
+    int spork2Value = sporkManager.GetSporkValue(SPORK_2_INSTANTSEND_ENABLED);
+    if (spork2Value == 0) {
+        return true;
+    }
+    return false;
+}
+
+bool IsNewInstantSendEnabled()
+{
+    int spork2Value = sporkManager.GetSporkValue(SPORK_2_INSTANTSEND_ENABLED);
+    if (spork2Value == 1) {
+        return true;
+    }
+    return false;
+}
+
+bool IsInstantSendEnabled()
+{
+    int spork2Value = sporkManager.GetSporkValue(SPORK_2_INSTANTSEND_ENABLED);
+    return spork2Value == 0 || spork2Value == 1;
+}
+
+}
diff --git a/src/llmq/quorums_instantsend.h b/src/llmq/quorums_instantsend.h
new file mode 100644
index 000000000000..36ce131d4093
--- /dev/null
+++ b/src/llmq/quorums_instantsend.h
@@ -0,0 +1,149 @@
+// Copyright (c) 2019 The Dash Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef DASH_QUORUMS_INSTANTX_H
+#define DASH_QUORUMS_INSTANTX_H
+
+#include "quorums_signing.h"
+
+#include "coins.h"
+#include "primitives/transaction.h"
+
+#include <unordered_map>
+#include <unordered_set>
+
+class CScheduler;
+
+namespace llmq
+{
+
+class CInstantXLock
+{
+public:
+    std::vector<COutPoint> inputs;
+    uint256 txid;
+    CBLSSignature sig;
+
+public:
+    ADD_SERIALIZE_METHODS
+
+    template<typename Stream, typename Operation>
+    inline void SerializationOp(Stream& s, Operation ser_action)
+    {
+        READWRITE(inputs);
+        READWRITE(txid);
+        READWRITE(sig);
+    }
+
+    uint256 GetRequestId() const;
+};
+
+class CInstantXLockInfo
+{
+public:
+    // might be nullptr when ixlock is received before the TX itself
+    CTransactionRef tx;
+    CInstantXLock ixlock;
+    // only valid when recovered sig was received
+    uint256 ixlockHash;
+    // time when it was created/received
+    int64_t time;
+
+    // might be null initially (when TX was not mined yet) and will later be filled by SyncTransaction
+    const CBlockIndex* pindexMined{nullptr};
+};
+
+class CInstantSendManager : public CRecoveredSigsListener
+{
+private:
+    CCriticalSection cs;
+    CScheduler* scheduler;
+
+    /**
+     * These are the votes/signatures we performed locally. It's indexed by the LLMQ requestId, which is
+     * hash(TXLOCK_REQUESTID_PREFIX, prevout). The map values are the txids we voted for. This map is used to
+     * avoid voting for the same input twice.
+     */
+    std::unordered_map<uint256, uint256, StaticSaltedHasher> inputVotes;
+
+    /**
+     * These are the ixlocks that are currently in the middle of being created. Entries are created when we observed
+     * recovered signatures for all inputs of a TX. At the same time, we initiate signing of our sigshare for the ixlock.
+     * When the recovered sig for the ixlock later arrives, we can finish the ixlock and propagate it.
+     */
+    std::unordered_map<uint256, CInstantXLockInfo, StaticSaltedHasher> creatingInstantXLocks;
+    // maps from txid to the in-progress ixlock
+    std::unordered_map<uint256, CInstantXLockInfo*, StaticSaltedHasher> txToCreatingInstantXLocks;
+
+    /**
+     * These are the final ixlocks, indexed by their own hash. The other maps are used to get from TXs, inputs and blocks
+     * to ixlocks.
+     */
+    std::unordered_map<uint256, CInstantXLockInfo, StaticSaltedHasher> finalInstantXLocks;
+    std::unordered_map<uint256, CInstantXLockInfo*, StaticSaltedHasher> txToInstantXLock;
+    std::unordered_map<COutPoint, CInstantXLockInfo*, SaltedOutpointHasher> inputToInstantXLock;
+    std::unordered_multimap<uint256, CInstantXLockInfo*, StaticSaltedHasher> blockToInstantXLocks;
+
+    const CBlockIndex* pindexLastChainLock{nullptr};
+
+    // Incoming and not verified yet
+    std::unordered_map<uint256, std::pair<NodeId, CInstantXLock>> pendingInstantXLocks;
+    bool hasScheduledProcessPending{false};
+
+public:
+    CInstantSendManager(CScheduler* _scheduler);
+    ~CInstantSendManager();
+
+    void RegisterAsRecoveredSigsListener();
+    void UnregisterAsRecoveredSigsListener();
+
+public:
+    bool ProcessTx(CNode* pfrom, const CTransaction& tx, CConnman& connman, const Consensus::Params& params);
+    bool CheckCanLock(const CTransaction& tx, bool printDebug, const Consensus::Params& params);
+    bool CheckCanLock(const COutPoint& outpoint, bool printDebug, const uint256* _txHash, CAmount* retValue, const Consensus::Params& params);
+    bool IsLocked(const uint256& txHash);
+    bool IsConflicted(const CTransaction& tx);
+    bool GetConflictingTx(const CTransaction& tx, uint256& retConflictTxHash);
+
+    virtual void HandleNewRecoveredSig(const CRecoveredSig& recoveredSig);
+    void HandleNewInputLockRecoveredSig(const CRecoveredSig& recoveredSig, const uint256& txid);
+    void HandleNewInstantXLockRecoveredSig(const CRecoveredSig& recoveredSig);
+
+    void TrySignInstantXLock(const CTransaction& tx);
+
+    void ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman);
+    void ProcessMessageInstantXLock(CNode* pfrom, const CInstantXLock& ixlock, CConnman& connman);
+    bool PreVerifyInstantXLock(NodeId nodeId, const CInstantXLock& ixlock, bool& retBan);
+    void ProcessPendingInstantXLocks();
+    void ProcessInstantXLock(NodeId from, const uint256& hash, const CInstantXLock& ixlock);
+    void UpdateWalletTransaction(const uint256& txid);
+
+    void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock);
+    void NotifyChainLock(const CBlockIndex* pindex);
+    void UpdateIxLockMinedBlock(CInstantXLockInfo* ixlockInfo, const CBlockIndex* pindex);
+    void RemoveFinalIxLock(const uint256& hash);
+
+    void RemoveMempoolConflictsForLock(const uint256& hash, const CInstantXLock& ixlock);
+    void RetryLockMempoolTxs(const uint256& lockedParentTx);
+
+    bool AlreadyHave(const CInv& inv);
+    bool GetInstantXLockByHash(const uint256& hash, CInstantXLock& ret);
+};
+
+extern CInstantSendManager* quorumInstantSendManager;
+
+// The meaning of spork 2 has changed in v0.14. Before that, spork 2 was simply time based and either enabled or not
+// After 0.14, spork 2 can have 3 states.
+// 0 = old system is active (0 is compatible with the value set on mainnet at time of deployment)
+// 1 = new system is active (old nodes will interpret this as the old system being enabled, but then won't get enough IX lock votes)
+// everything else = disabled
+// TODO When the new system is fully deployed and enabled, we can remove this special handling of the spork in a future version
+// and revert to the old behaviour.
+bool IsOldInstantSendEnabled();
+bool IsNewInstantSendEnabled();
+bool IsInstantSendEnabled();
+
+}
+
+#endif//DASH_QUORUMS_INSTANTX_H
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index b68c0bcf03c0..b53beb3d128f 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -50,6 +50,7 @@
 #include "llmq/quorums_debug.h"
 #include "llmq/quorums_dkgsessionmgr.h"
 #include "llmq/quorums_init.h"
+#include "llmq/quorums_instantsend.h"
 #include "llmq/quorums_signing.h"
 #include "llmq/quorums_signing_shares.h"
 
@@ -976,6 +977,8 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
         return llmq::quorumSigningManager->AlreadyHave(inv);
     case MSG_CLSIG:
         return llmq::chainLocksHandler->AlreadyHave(inv);
+    case MSG_IXLOCK:
+        return llmq::quorumInstantSendManager->AlreadyHave(inv);
     }
 
     // Don't know what it is, just say we already got one
@@ -1296,6 +1299,14 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
                     }
                 }
 
+                if (!push && (inv.type == MSG_IXLOCK)) {
+                    llmq::CInstantXLock o;
+                    if (llmq::quorumInstantSendManager->GetInstantXLockByHash(inv.hash, o)) {
+                        connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::IXLOCK, o));
+                        push = true;
+                    }
+                }
+
                 if (!push)
                     vNotFound.push_back(inv);
             }
@@ -1777,6 +1788,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
                             case MSG_CLSIG:
                                 doubleRequestDelay = 5 * 1000000;
                                 break;
+                            case MSG_IXLOCK:
+                                doubleRequestDelay = 5 * 1000000;
+                                break;
                         }
                         pfrom->AskFor(inv, doubleRequestDelay);
                     }
@@ -2943,6 +2957,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
             llmq::quorumSigSharesManager->ProcessMessage(pfrom, strCommand, vRecv, connman);
             llmq::quorumSigningManager->ProcessMessage(pfrom, strCommand, vRecv, connman);
             llmq::chainLocksHandler->ProcessMessage(pfrom, strCommand, vRecv, connman);
+            llmq::quorumInstantSendManager->ProcessMessage(pfrom, strCommand, vRecv, connman);
         }
         else
         {
diff --git a/src/protocol.cpp b/src/protocol.cpp
index 6df80cae74fa..e63688c0a00c 100644
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -71,6 +71,7 @@ const char *QGETSIGSHARES="qgetsigs";
 const char *QBSIGSHARES="qbsigs";
 const char *QSIGREC="qsigrec";
 const char *CLSIG="clsig";
+const char *IXLOCK="ixlock";
 };
 
 static const char* ppszTypeName[] =
@@ -107,6 +108,7 @@ static const char* ppszTypeName[] =
     NetMsgType::QDEBUGSTATUS,
     NetMsgType::QSIGREC,
     NetMsgType::CLSIG,
+    NetMsgType::IXLOCK,
 };
 
 /** All known message types. Keep this in the same order as the list of
@@ -172,6 +174,7 @@ const static std::string allNetMessageTypes[] = {
     NetMsgType::QBSIGSHARES,
     NetMsgType::QSIGREC,
     NetMsgType::CLSIG,
+    NetMsgType::IXLOCK,
 };
 const static std::vector<std::string> allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes));
 
diff --git a/src/protocol.h b/src/protocol.h
index b4d6eab3ecca..0376f86f26ff 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -277,6 +277,7 @@ extern const char *QGETSIGSHARES;
 extern const char *QBSIGSHARES;
 extern const char *QSIGREC;
 extern const char *CLSIG;
+extern const char *IXLOCK;
 };
 
 /* Get a vector of all valid message types (see above) */
@@ -379,6 +380,7 @@ enum GetDataMsg {
     MSG_QUORUM_DEBUG_STATUS = 27,
     MSG_QUORUM_RECOVERED_SIG = 28,
     MSG_CLSIG = 29,
+    MSG_IXLOCK = 30,
 };
 
 /** inv message data */

From 1959f3e4a59b8282f480669dbaf420b51c81df51 Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Thu, 28 Feb 2019 13:43:04 +0100
Subject: [PATCH 04/33] Handle incoming TXs by calling
 CInstantXManager::ProcessTx

This also includes handling of TXs that were previously orphanced
---
 src/net_processing.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index b53beb3d128f..cdd2b522e8af 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -2094,6 +2094,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
                 instantsend.Vote(tx.GetHash(), connman);
             }
 
+            if (nInvType != MSG_TXLOCK_REQUEST) {
+                llmq::quorumInstantSendManager->ProcessTx(pfrom, tx, connman, chainparams.GetConsensus());
+            }
+
             mempool.check(pcoinsTip);
             connman.RelayTransaction(tx);
             for (unsigned int i = 0; i < tx.vout.size(); i++) {
@@ -2138,6 +2142,8 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
                             vWorkQueue.emplace_back(orphanHash, i);
                         }
                         vEraseQueue.push_back(orphanHash);
+
+                        llmq::quorumInstantSendManager->ProcessTx(pfrom, orphanTx, connman, chainparams.GetConsensus());
                     }
                     else if (!fMissingInputs2)
                     {

From 5ff4db0a05b15c507e0c3891fbab157f337e70b3 Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Thu, 28 Feb 2019 13:44:53 +0100
Subject: [PATCH 05/33] Downgrade TXLOCKREQUEST to TX when new IX system is
 active

The new system does not require explicit lock requests, so we downgrade
TXLOCKREQUEST to TX and start propagating it instead of the original.
---
 src/net.cpp            |  9 +++++++--
 src/net_processing.cpp | 17 +++++++++++------
 2 files changed, 18 insertions(+), 8 deletions(-)

diff --git a/src/net.cpp b/src/net.cpp
index fd20a51ee090..b54147c02d68 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -28,6 +28,7 @@
 #include "instantx.h"
 #include "masternode-sync.h"
 #include "privatesend.h"
+#include "llmq/quorums_instantsend.h"
 
 #ifdef WIN32
 #include <string.h>
@@ -2786,8 +2787,12 @@ bool CConnman::DisconnectNode(NodeId id)
 void CConnman::RelayTransaction(const CTransaction& tx)
 {
     uint256 hash = tx.GetHash();
-    int nInv = static_cast<bool>(CPrivateSend::GetDSTX(hash)) ? MSG_DSTX :
-                (instantsend.HasTxLockRequest(hash) ? MSG_TXLOCK_REQUEST : MSG_TX);
+    int nInv = MSG_TX;
+    if (CPrivateSend::GetDSTX(hash)) {
+        nInv = MSG_DSTX;
+    } else if (llmq::IsOldInstantSendEnabled() && instantsend.HasTxLockRequest(hash)) {
+        nInv = MSG_TXLOCK_REQUEST;
+    }
     CInv inv(nInv, hash);
     LOCK(cs_vNodes);
     BOOST_FOREACH(CNode* pnode, vNodes)
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index cdd2b522e8af..1123e9c48e1e 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -2015,11 +2015,16 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
         if(strCommand == NetMsgType::TX) {
             vRecv >> ptx;
             txLockRequest = CTxLockRequest(ptx);
-            fCanAutoLock = CInstantSend::CanAutoLock() && txLockRequest.IsSimple();
+            fCanAutoLock = llmq::IsOldInstantSendEnabled() && CInstantSend::CanAutoLock() && txLockRequest.IsSimple();
         } else if(strCommand == NetMsgType::TXLOCKREQUEST) {
             vRecv >> txLockRequest;
             ptx = txLockRequest.tx;
             nInvType = MSG_TXLOCK_REQUEST;
+            if (llmq::IsNewInstantSendEnabled()) {
+                // the new system does not require explicit lock requests
+                // changing the inv type to MSG_TX also results in re-broadcasting the TX as normal TX
+                nInvType = MSG_TX;
+            }
         } else if (strCommand == NetMsgType::DSTX) {
             vRecv >> dstx;
             ptx = dstx.tx;
@@ -2035,7 +2040,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
         }
 
         // Process custom logic, no matter if tx will be accepted to mempool later or not
-        if (strCommand == NetMsgType::TXLOCKREQUEST || fCanAutoLock) {
+        if (nInvType == MSG_TXLOCK_REQUEST || fCanAutoLock) {
             if(!instantsend.ProcessTxLockRequest(txLockRequest, connman)) {
                 LogPrint("instantsend", "TXLOCKREQUEST -- failed %s\n", txLockRequest.GetHash().ToString());
                 // Should not really happen for "fCanAutoLock == true" but just in case:
@@ -2046,7 +2051,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
                 // Fallback for normal txes to process as usual
                 fCanAutoLock = false;
             }
-        } else if (strCommand == NetMsgType::DSTX) {
+        } else if (nInvType == MSG_DSTX) {
             uint256 hashTx = tx.GetHash();
             if(CPrivateSend::GetDSTX(hashTx)) {
                 LogPrint("privatesend", "DSTX -- Already have %s, skipping...\n", hashTx.ToString());
@@ -2083,11 +2088,11 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
 
         if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, ptx, true, &fMissingInputs)) {
             // Process custom txes, this changes AlreadyHave to "true"
-            if (strCommand == NetMsgType::DSTX) {
+            if (nInvType == MSG_DSTX) {
                 LogPrintf("DSTX -- Masternode transaction accepted, txid=%s, peer=%d\n",
                         tx.GetHash().ToString(), pfrom->id);
                 CPrivateSend::AddDSTX(dstx);
-            } else if (strCommand == NetMsgType::TXLOCKREQUEST || fCanAutoLock) {
+            } else if (nInvType == MSG_TXLOCK_REQUEST || fCanAutoLock) {
                 LogPrintf("TXLOCKREQUEST -- Transaction Lock Request accepted, txid=%s, peer=%d\n",
                         tx.GetHash().ToString(), pfrom->id);
                 instantsend.AcceptLockRequest(txLockRequest);
@@ -2208,7 +2213,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
                 }
             }
 
-            if (strCommand == NetMsgType::TXLOCKREQUEST && !AlreadyHave(inv)) {
+            if (nInvType == MSG_TXLOCK_REQUEST && !AlreadyHave(inv)) {
                 // i.e. AcceptToMemoryPool failed, probably because it's conflicting
                 // with existing normal tx or tx lock for another tx. For the same tx lock
                 // AlreadyHave would have return "true" already.

From 34a8b2997a6228f8b72ecb967120219b24cb50f3 Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Thu, 28 Feb 2019 13:46:36 +0100
Subject: [PATCH 06/33] Disable old IX code when the new system is active

---
 src/instantx.cpp | 26 ++++++++++++++++++++------
 1 file changed, 20 insertions(+), 6 deletions(-)

diff --git a/src/instantx.cpp b/src/instantx.cpp
index 9096a6707a09..87bbd1a5b6bf 100644
--- a/src/instantx.cpp
+++ b/src/instantx.cpp
@@ -25,6 +25,8 @@
 #include "wallet/wallet.h"
 #endif // ENABLE_WALLET
 
+#include "llmq/quorums_instantsend.h"
+
 #include <boost/algorithm/string/replace.hpp>
 #include <boost/thread.hpp>
 
@@ -56,7 +58,7 @@ const std::string CInstantSend::SERIALIZATION_VERSION_STRING = "CInstantSend-Ver
 void CInstantSend::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman)
 {
     if (fLiteMode) return; // disable all Dash specific functionality
-    if (!sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return;
+    if (!llmq::IsOldInstantSendEnabled()) return;
 
     // NOTE: NetMsgType::TXLOCKREQUEST is handled via ProcessMessage() in net_processing.cpp
 
@@ -224,7 +226,7 @@ void CInstantSend::Vote(const uint256& txHash, CConnman& connman)
 void CInstantSend::Vote(CTxLockCandidate& txLockCandidate, CConnman& connman)
 {
     if (!fMasternodeMode) return;
-    if (!sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return;
+    if (!llmq::IsOldInstantSendEnabled()) return;
 
     AssertLockHeld(cs_main);
     AssertLockHeld(cs_instantsend);
@@ -496,7 +498,7 @@ void CInstantSend::ProcessOrphanTxLockVotes()
 
 void CInstantSend::TryToFinalizeLockCandidate(const CTxLockCandidate& txLockCandidate)
 {
-    if (!sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return;
+    if (!llmq::IsOldInstantSendEnabled()) return;
 
     AssertLockHeld(cs_main);
     AssertLockHeld(cs_instantsend);
@@ -547,7 +549,7 @@ void CInstantSend::UpdateLockedTransaction(const CTxLockCandidate& txLockCandida
 
 void CInstantSend::LockTransactionInputs(const CTxLockCandidate& txLockCandidate)
 {
-    if (!sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return;
+    if (!llmq::IsOldInstantSendEnabled()) return;
 
     LOCK(cs_instantsend);
 
@@ -740,6 +742,10 @@ void CInstantSend::CheckAndRemove()
 
 bool CInstantSend::AlreadyHave(const uint256& hash)
 {
+    if (!llmq::IsOldInstantSendEnabled()) {
+        return true;
+    }
+
     LOCK(cs_instantsend);
     return mapLockRequestAccepted.count(hash) ||
             mapLockRequestRejected.count(hash) ||
@@ -766,6 +772,10 @@ bool CInstantSend::HasTxLockRequest(const uint256& txHash)
 
 bool CInstantSend::GetTxLockRequest(const uint256& txHash, CTxLockRequest& txLockRequestRet)
 {
+    if (!llmq::IsOldInstantSendEnabled()) {
+        return false;
+    }
+
     LOCK(cs_instantsend);
 
     std::map<uint256, CTxLockCandidate>::iterator it = mapTxLockCandidates.find(txHash);
@@ -777,6 +787,10 @@ bool CInstantSend::GetTxLockRequest(const uint256& txHash, CTxLockRequest& txLoc
 
 bool CInstantSend::GetTxLockVote(const uint256& hash, CTxLockVote& txLockVoteRet)
 {
+    if (!llmq::IsOldInstantSendEnabled()) {
+        return false;
+    }
+
     LOCK(cs_instantsend);
 
     std::map<uint256, CTxLockVote>::iterator it = mapTxLockVotes.find(hash);
@@ -828,7 +842,7 @@ int CInstantSend::GetTransactionLockSignatures(const uint256& txHash)
 {
     if (!fEnableInstantSend) return -1;
     if (GetfLargeWorkForkFound() || GetfLargeWorkInvalidChainFound()) return -2;
-    if (!sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return -3;
+    if (!llmq::IsOldInstantSendEnabled()) return -3;
 
     LOCK(cs_instantsend);
 
@@ -932,7 +946,7 @@ void CInstantSend::DoMaintenance()
 
 bool CInstantSend::CanAutoLock()
 {
-    if (!isAutoLockBip9Active) {
+    if (!isAutoLockBip9Active || !llmq::IsOldInstantSendEnabled()) {
         return false;
     }
     if (!sporkManager.IsSporkActive(SPORK_16_INSTANTSEND_AUTOLOCKS)) {

From 3a6cc2cc17e15f5007aab08bdbe399448cccf532 Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Thu, 28 Feb 2019 13:47:18 +0100
Subject: [PATCH 07/33] Show "verified via LLMQ based InstantSend" in GUI TX
 status

---
 src/qt/transactiondesc.cpp | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index f6069f24cf21..7f3385dfd825 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -21,6 +21,8 @@
 
 #include "instantx.h"
 
+#include "llmq/quorums_instantsend.h"
+
 #include <stdint.h>
 #include <string>
 
@@ -52,6 +54,11 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx)
             strTxStatus = tr("%1 confirmations").arg(nDepth);
         }
 
+        if (llmq::quorumInstantSendManager->IsLocked(wtx.GetHash())) {
+            strTxStatus += tr(" (verified via LLMQ based InstantSend)");
+            return strTxStatus;
+        }
+
         if(!instantsend.HasTxLockRequest(wtx.GetHash())) return strTxStatus; // regular tx
 
         int nSignatures = instantsend.GetTransactionLockSignatures(wtx.GetHash());

From 1d2d370cd09f039718a4e97c6bba70551e2cf387 Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Thu, 28 Feb 2019 13:59:57 +0100
Subject: [PATCH 08/33] Whenever we check for locked TXs, also check for the
 new system having a lock

---
 src/governance-object.cpp  | 4 +++-
 src/rpc/blockchain.cpp     | 3 ++-
 src/rpc/rawtransaction.cpp | 4 +++-
 src/txmempool.cpp          | 4 +++-
 src/wallet/rpcwallet.cpp   | 4 +++-
 src/wallet/wallet.cpp      | 4 +++-
 6 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/src/governance-object.cpp b/src/governance-object.cpp
index 902138fd40a2..e8c370ade333 100644
--- a/src/governance-object.cpp
+++ b/src/governance-object.cpp
@@ -16,6 +16,8 @@
 #include "util.h"
 #include "validation.h"
 
+#include "llmq/quorums_instantsend.h"
+
 #include <string>
 #include <univalue.h>
 
@@ -618,7 +620,7 @@ bool CGovernanceObject::IsCollateralValid(std::string& strError, bool& fMissingC
     }
 
     if ((nConfirmationsIn < GOVERNANCE_FEE_CONFIRMATIONS) &&
-        (!instantsend.IsLockedInstantSendTransaction(nCollateralHash))) {
+        (!instantsend.IsLockedInstantSendTransaction(nCollateralHash) || llmq::quorumInstantSendManager->IsLocked(nCollateralHash))) {
         strError = strprintf("Collateral requires at least %d confirmations to be relayed throughout the network (it has only %d)", GOVERNANCE_FEE_CONFIRMATIONS, nConfirmationsIn);
         if (nConfirmationsIn >= GOVERNANCE_MIN_RELAY_FEE_CONFIRMATIONS) {
             fMissingConfirmations = true;
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 7aaf3bcd2b47..de5bd13ceba0 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -28,6 +28,7 @@
 #include "evo/cbtx.h"
 
 #include "llmq/quorums_chainlocks.h"
+#include "llmq/quorums_instantsend.h"
 
 #include <stdint.h>
 
@@ -409,7 +410,7 @@ void entryToJSON(UniValue &info, const CTxMemPoolEntry &e)
 
     info.push_back(Pair("depends", depends));
     info.push_back(Pair("instantsend", instantsend.HasTxLockRequest(tx.GetHash())));
-    info.push_back(Pair("instantlock", instantsend.IsLockedInstantSendTransaction(tx.GetHash())));
+    info.push_back(Pair("instantlock", instantsend.IsLockedInstantSendTransaction(tx.GetHash()) || llmq::quorumInstantSendManager->IsLocked(tx.GetHash())));
 }
 
 UniValue mempoolToJSON(bool fVerbose = false)
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 4a8bdb078dd1..8ece8720fc62 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -36,6 +36,7 @@
 
 #include "llmq/quorums_chainlocks.h"
 #include "llmq/quorums_commitment.h"
+#include "llmq/quorums_instantsend.h"
 
 #include <stdint.h>
 
@@ -199,7 +200,8 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
         }
     }
     bool fLocked = instantsend.IsLockedInstantSendTransaction(txid);
-    entry.push_back(Pair("instantlock", fLocked));
+    bool fLLMQLocked = llmq::quorumInstantSendManager->IsLocked(txid);
+    entry.push_back(Pair("instantlock", fLocked || fLLMQLocked));
     entry.push_back(Pair("chainlock", chainLock));
 }
 
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 295f31cbba54..9575ba99cab7 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -24,6 +24,8 @@
 #include "evo/specialtx.h"
 #include "evo/providertx.h"
 
+#include "llmq/quorums_instantsend.h"
+
 CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
                                  int64_t _nTime, double _entryPriority, unsigned int _entryHeight,
                                  CAmount _inChainInputValue,
@@ -1497,7 +1499,7 @@ int CTxMemPool::Expire(int64_t time) {
     setEntries toremove;
     while (it != mapTx.get<entry_time>().end() && it->GetTime() < time) {
         // locked txes do not expire until mined and have sufficient confirmations
-        if (instantsend.IsLockedInstantSendTransaction(it->GetTx().GetHash())) {
+        if (instantsend.IsLockedInstantSendTransaction(it->GetTx().GetHash()) || llmq::quorumInstantSendManager->IsLocked(it->GetTx().GetHash())) {
             it++;
             continue;
         }
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 530f7d725761..7846f4d5b153 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -23,6 +23,7 @@
 #include "privatesend-client.h"
 
 #include "llmq/quorums_chainlocks.h"
+#include "llmq/quorums_instantsend.h"
 
 #include <stdint.h>
 
@@ -65,12 +66,13 @@ void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry)
     AssertLockHeld(cs_main); // for mapBlockIndex
     int confirms = wtx.GetDepthInMainChain();
     bool fLocked = instantsend.IsLockedInstantSendTransaction(wtx.GetHash());
+    bool fLLMQLocked = llmq::quorumInstantSendManager->IsLocked(wtx.GetHash());
     bool chainlock = false;
     if (confirms > 0) {
         chainlock = llmq::chainLocksHandler->HasChainLock(mapBlockIndex[wtx.hashBlock]->nHeight, wtx.hashBlock);
     }
     entry.push_back(Pair("confirmations", confirms));
-    entry.push_back(Pair("instantlock", fLocked));
+    entry.push_back(Pair("instantlock", fLocked || fLLMQLocked));
     entry.push_back(Pair("chainlock", chainlock));
     if (wtx.IsCoinBase())
         entry.push_back(Pair("generated", true));
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 8c34403b3541..7a8fde9ff16d 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -36,6 +36,8 @@
 
 #include "evo/providertx.h"
 
+#include "llmq/quorums_instantsend.h"
+
 #include <assert.h>
 
 #include <boost/algorithm/string/replace.hpp>
@@ -5426,7 +5428,7 @@ int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const
 
 bool CMerkleTx::IsLockedByInstantSend() const
 {
-    return instantsend.IsLockedInstantSendTransaction(GetHash());
+    return instantsend.IsLockedInstantSendTransaction(GetHash()) || llmq::quorumInstantSendManager->IsLocked(GetHash());
 }
 
 int CMerkleTx::GetBlocksToMaturity() const

From 68cfdc9325401a2c667674e56ba4b50364a31698 Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Thu, 28 Feb 2019 14:01:11 +0100
Subject: [PATCH 09/33] Also call ProcessTx from sendrawtransaction and
 RelayWalletTransaction

---
 src/rpc/rawtransaction.cpp | 1 +
 src/wallet/wallet.cpp      | 3 +++
 2 files changed, 4 insertions(+)

diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 8ece8720fc62..afdece3aabbb 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -1020,6 +1020,7 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
                 throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason());
             }
         }
+        llmq::quorumInstantSendManager->ProcessTx(nullptr, *tx, *g_connman, Params().GetConsensus());
     } else if (fHaveChain) {
         throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain");
     }
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 7a8fde9ff16d..ea1cc14f2007 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1921,6 +1921,9 @@ bool CWalletTx::RelayWalletTransaction(CConnman* connman, const std::string& str
                     instantsend.RejectLockRequest((CTxLockRequest)*this);
                 }
             }
+
+            llmq::quorumInstantSendManager->ProcessTx(nullptr, *this->tx, *connman, Params().GetConsensus());
+
             if (connman) {
                 connman->RelayTransaction((CTransaction)*this);
                 return true;

From dc97835ec6a1d5a0adc35078d8a615867e26b406 Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Thu, 28 Feb 2019 14:03:19 +0100
Subject: [PATCH 10/33] Disable explicit lock requests when the new IX system
 is active

---
 src/qt/walletmodel.cpp   |  8 +++++++-
 src/wallet/rpcwallet.cpp | 14 ++++++++++++--
 src/wallet/wallet.cpp    |  6 ++++++
 3 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 15f13a25b2d0..487bcca3caec 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -26,6 +26,7 @@
 #include "instantx.h"
 #include "spork.h"
 #include "privatesend-client.h"
+#include "llmq/quorums_instantsend.h"
 
 #include <stdint.h>
 
@@ -389,7 +390,12 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
 
         CReserveKey *keyChange = transaction.getPossibleKeyChange();
         CValidationState state;
-        if(!wallet->CommitTransaction(*newTx, *keyChange, g_connman.get(), state, recipients[0].fUseInstantSend ? NetMsgType::TXLOCKREQUEST : NetMsgType::TX))
+        // the new IX system does not require explicit IX messages
+        std::string strCommand = NetMsgType::TX;
+        if (recipients[0].fUseInstantSend && llmq::IsOldInstantSendEnabled()) {
+            strCommand = NetMsgType::TXLOCKREQUEST;
+        }
+        if(!wallet->CommitTransaction(*newTx, *keyChange, g_connman.get(), state, strCommand))
             return SendCoinsReturn(TransactionCommitFailed, QString::fromStdString(state.GetRejectReason()));
 
         CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 7846f4d5b153..9074d78c13c3 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -386,7 +386,12 @@ static void SendMoney(CWallet * const pwallet, const CTxDestination &address, CA
         throw JSONRPCError(RPC_WALLET_ERROR, strError);
     }
     CValidationState state;
-    if (!pwallet->CommitTransaction(wtxNew, reservekey, g_connman.get(), state, fUseInstantSend ? NetMsgType::TXLOCKREQUEST : NetMsgType::TX)) {
+    // the new IX system does not require explicit IX messages
+    std::string strCommand = NetMsgType::TX;
+    if (fUseInstantSend && llmq::IsOldInstantSendEnabled()) {
+        strCommand = NetMsgType::TXLOCKREQUEST;
+    }
+    if (!pwallet->CommitTransaction(wtxNew, reservekey, g_connman.get(), state, strCommand)) {
         strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason());
         throw JSONRPCError(RPC_WALLET_ERROR, strError);
     }
@@ -1136,7 +1141,12 @@ UniValue sendmany(const JSONRPCRequest& request)
     if (!fCreated)
         throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
     CValidationState state;
-    if (!pwallet->CommitTransaction(wtx, keyChange, g_connman.get(), state, fUseInstantSend ? NetMsgType::TXLOCKREQUEST : NetMsgType::TX)) {
+    // the new IX system does not require explicit IX messages
+    std::string strCommand = NetMsgType::TX;
+    if (fUseInstantSend && llmq::IsOldInstantSendEnabled()) {
+        strCommand = NetMsgType::TXLOCKREQUEST;
+    }
+    if (!pwallet->CommitTransaction(wtx, keyChange, g_connman.get(), state, strCommand)) {
         strFailReason = strprintf("Transaction commit failed:: %s", state.GetRejectReason());
         throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
     }
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index ea1cc14f2007..22138bde0f49 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -3345,6 +3345,12 @@ bool CWallet::ConvertList(std::vector<CTxIn> vecTxIn, std::vector<CAmount>& vecA
 bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet,
                                 int& nChangePosInOut, std::string& strFailReason, const CCoinControl* coinControl, bool sign, AvailableCoinsType nCoinType, bool fUseInstantSend, int nExtraPayloadSize)
 {
+    if (!llmq::IsOldInstantSendEnabled()) {
+        // The new system does not require special handling for InstantSend as this is all done in CInstantXManager.
+        // There is also no need for an extra fee anymore.
+        fUseInstantSend = false;
+    }
+
     CAmount nFeePay = fUseInstantSend ? CTxLockRequest().GetMinFee(true) : 0;
 
     CAmount nValue = 0;

From 7945192ff66e95fdd6c4035a17587f3a5e62a368 Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Thu, 28 Feb 2019 14:04:11 +0100
Subject: [PATCH 11/33] Enforce IX locks from the new system

---
 src/validation.cpp | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/src/validation.cpp b/src/validation.cpp
index e57409bcf6fa..5142d8e616a3 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -46,6 +46,7 @@
 #include "evo/deterministicmns.h"
 #include "evo/cbtx.h"
 
+#include "llmq/quorums_instantsend.h"
 #include "llmq/quorums_chainlocks.h"
 
 #include <atomic>
@@ -692,6 +693,18 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
                             REJECT_INVALID, "tx-txlock-conflict");
     }
 
+    uint256 txConflictHash;
+    if (llmq::quorumInstantSendManager->GetConflictingTx(tx, txConflictHash)) {
+        CTransactionRef txConflict;
+        uint256 hashBlock;
+        if (GetTransaction(txConflictHash, txConflict, Params().GetConsensus(), hashBlock)) {
+            GetMainSignals().NotifyInstantSendDoubleSpendAttempt(tx, *txConflict);
+        }
+        return state.DoS(10, error("AcceptToMemoryPool : Transaction %s conflicts with locked TX %s",
+                                   hash.ToString(), txConflictHash.ToString()),
+                         REJECT_INVALID, "tx-txlock-conflict");
+    }
+
     // Check for conflicts with in-memory transactions
     {
     LOCK(pool.cs); // protect pool.mapNextTx
@@ -3278,6 +3291,15 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
                                      strprintf("transaction %s conflicts with transaction lock %s", tx->GetHash().ToString(), hashLocked.ToString()));
                 }
             }
+            uint256 txConflict;
+            if (llmq::quorumInstantSendManager->GetConflictingTx(*tx, txConflict)) {
+                // The node which relayed this will have to switch later,
+                // relaying instantsend data won't help it.
+                LOCK(cs_main);
+                mapRejectedBlocks.insert(std::make_pair(block.GetHash(), GetTime()));
+                return state.DoS(100, false, REJECT_INVALID, "conflict-tx-lock", false,
+                                 strprintf("transaction %s conflicts with transaction lock %s", tx->GetHash().ToString(), txConflict.ToString()));
+            }
         }
     } else {
         LogPrintf("CheckBlock(DASH): spork is off, skipping transaction locking checks\n");

From bd7edc8ae99646a27dcf2e0bae9bcb16239ff871 Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Thu, 28 Feb 2019 15:04:27 +0100
Subject: [PATCH 12/33] Track txids of new blocks and first-seen time of TXs in
 CChainLocksHandler

---
 src/dsnotificationinterface.cpp |  6 +++
 src/dsnotificationinterface.h   |  1 +
 src/llmq/quorums_chainlocks.cpp | 82 ++++++++++++++++++++++++++++++++-
 src/llmq/quorums_chainlocks.h   |  7 +++
 4 files changed, 95 insertions(+), 1 deletion(-)

diff --git a/src/dsnotificationinterface.cpp b/src/dsnotificationinterface.cpp
index 954de50d0bae..418f1f9e2879 100644
--- a/src/dsnotificationinterface.cpp
+++ b/src/dsnotificationinterface.cpp
@@ -71,9 +71,15 @@ void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, con
     llmq::quorumDKGSessionManager->UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload);
 }
 
+void CDSNotificationInterface::NewPoWValidBlock(const CBlockIndex* pindex, const std::shared_ptr<const CBlock>& block)
+{
+    llmq::chainLocksHandler->NewPoWValidBlock(pindex, block);
+}
+
 void CDSNotificationInterface::SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock)
 {
     llmq::quorumInstantSendManager->SyncTransaction(tx, pindex, posInBlock);
+    llmq::chainLocksHandler->SyncTransaction(tx, pindex, posInBlock);
     instantsend.SyncTransaction(tx, pindex, posInBlock);
     CPrivateSend::SyncTransaction(tx, pindex, posInBlock);
 }
diff --git a/src/dsnotificationinterface.h b/src/dsnotificationinterface.h
index f4e97d7ed5c6..60c1e5bd3611 100644
--- a/src/dsnotificationinterface.h
+++ b/src/dsnotificationinterface.h
@@ -21,6 +21,7 @@ class CDSNotificationInterface : public CValidationInterface
     void AcceptedBlockHeader(const CBlockIndex *pindexNew) override;
     void NotifyHeaderTip(const CBlockIndex *pindexNew, bool fInitialDownload) override;
     void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override;
+    void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& block) override;
     void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock) override;
     void NotifyMasternodeListChanged(const CDeterministicMNList& newList) override;
     void NotifyChainLock(const CBlockIndex* pindex);
diff --git a/src/llmq/quorums_chainlocks.cpp b/src/llmq/quorums_chainlocks.cpp
index 5a51f98e9ca7..67c9ddbeffee 100644
--- a/src/llmq/quorums_chainlocks.cpp
+++ b/src/llmq/quorums_chainlocks.cpp
@@ -11,6 +11,7 @@
 #include "net_processing.h"
 #include "scheduler.h"
 #include "spork.h"
+#include "txmempool.h"
 #include "validation.h"
 
 namespace llmq
@@ -244,6 +245,51 @@ void CChainLocksHandler::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBl
     quorumSigningManager->AsyncSignIfMember(Params().GetConsensus().llmqChainLocks, requestId, msgHash);
 }
 
+void CChainLocksHandler::NewPoWValidBlock(const CBlockIndex* pindex, const std::shared_ptr<const CBlock>& block)
+{
+    LOCK(cs);
+    if (blockTxs.count(pindex->GetBlockHash())) {
+        // should actually not happen (blocks are only written once to disk and this is when NewPoWValidBlock is called)
+        // but be extra safe here in case this behaviour changes.
+        return;
+    }
+
+    // We listen for NewPoWValidBlock so that we can collect all TX ids of all included TXs of newly received blocks
+    // We need this information later when we try to sign a new tip, so that we can determine if all included TXs are
+    // safe.
+
+    auto txs = std::make_shared<std::unordered_set<uint256, StaticSaltedHasher>>();
+    for (const auto& tx : block->vtx) {
+        if (tx->nVersion == 3) {
+            if (tx->nType == TRANSACTION_COINBASE ||
+                tx->nType == TRANSACTION_QUORUM_COMMITMENT) {
+                continue;
+            }
+        }
+        txs->emplace(tx->GetHash());
+    }
+    blockTxs[pindex->GetBlockHash()] = txs;
+
+    int64_t curTime = GetAdjustedTime();
+    for (auto& tx : block->vtx) {
+        txFirstSeenTime.emplace(tx->GetHash(), curTime);
+    }
+}
+
+void CChainLocksHandler::SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int posInBlock)
+{
+    if (tx.nVersion == 3) {
+        if (tx.nType == TRANSACTION_COINBASE ||
+            tx.nType == TRANSACTION_QUORUM_COMMITMENT) {
+            return;
+        }
+    }
+
+    LOCK(cs);
+    int64_t curTime = GetAdjustedTime();
+    txFirstSeenTime.emplace(tx.GetHash(), curTime);
+}
+
 // WARNING: cs_main and cs should not be held!
 void CChainLocksHandler::EnforceBestChainLock()
 {
@@ -420,7 +466,9 @@ void CChainLocksHandler::Cleanup()
         }
     }
 
-    LOCK2(cs_main, cs);
+    // need mempool.cs due to GetTransaction calls
+    LOCK2(cs_main, mempool.cs);
+    LOCK(cs);
 
     for (auto it = seenChainLocks.begin(); it != seenChainLocks.end(); ) {
         if (GetTimeMillis() - it->second >= CLEANUP_SEEN_TIMEOUT) {
@@ -430,6 +478,38 @@ void CChainLocksHandler::Cleanup()
         }
     }
 
+    for (auto it = blockTxs.begin(); it != blockTxs.end(); ) {
+        auto pindex = mapBlockIndex.at(it->first);
+        if (InternalHasChainLock(pindex->nHeight, pindex->GetBlockHash())) {
+            for (auto& txid : *it->second) {
+                txFirstSeenTime.erase(txid);
+            }
+            it = blockTxs.erase(it);
+        } else if (InternalHasConflictingChainLock(pindex->nHeight, pindex->GetBlockHash())) {
+            it = blockTxs.erase(it);
+        } else {
+            ++it;
+        }
+    }
+    for (auto it = txFirstSeenTime.begin(); it != txFirstSeenTime.end(); ) {
+        CTransactionRef tx;
+        uint256 hashBlock;
+        if (!GetTransaction(it->first, tx, Params().GetConsensus(), hashBlock)) {
+            // tx has vanished, probably due to conflicts
+            it = txFirstSeenTime.erase(it);
+        } else if (!hashBlock.IsNull()) {
+            auto pindex = mapBlockIndex.at(hashBlock);
+            if (chainActive.Tip()->GetAncestor(pindex->nHeight) == pindex && chainActive.Height() - pindex->nHeight >= 6) {
+                // tx got confirmed >= 6 times, so we can stop keeping track of it
+                it = txFirstSeenTime.erase(it);
+            } else {
+                ++it;
+            }
+        } else {
+            ++it;
+        }
+    }
+
     lastCleanupTime = GetTimeMillis();
 }
 
diff --git a/src/llmq/quorums_chainlocks.h b/src/llmq/quorums_chainlocks.h
index b7cd441e040a..410f2bf80bcb 100644
--- a/src/llmq/quorums_chainlocks.h
+++ b/src/llmq/quorums_chainlocks.h
@@ -12,6 +12,7 @@
 #include "chainparams.h"
 
 #include <atomic>
+#include <unordered_set>
 
 class CBlockIndex;
 class CScheduler;
@@ -61,6 +62,10 @@ class CChainLocksHandler : public CRecoveredSigsListener
     uint256 lastSignedRequestId;
     uint256 lastSignedMsgHash;
 
+    // We keep track of txids from recently received blocks so that we can check if all TXs got ixlocked
+    std::unordered_map<uint256, std::shared_ptr<std::unordered_set<uint256, StaticSaltedHasher>>> blockTxs;
+    std::unordered_map<uint256, int64_t> txFirstSeenTime;
+
     std::map<uint256, int64_t> seenChainLocks;
 
     int64_t lastCleanupTime{0};
@@ -79,6 +84,8 @@ class CChainLocksHandler : public CRecoveredSigsListener
     void ProcessNewChainLock(NodeId from, const CChainLockSig& clsig, const uint256& hash);
     void AcceptedBlockHeader(const CBlockIndex* pindexNew);
     void UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork);
+    void NewPoWValidBlock(const CBlockIndex* pindex, const std::shared_ptr<const CBlock>& block);
+    void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock);
     void EnforceBestChainLock();
     virtual void HandleNewRecoveredSig(const CRecoveredSig& recoveredSig);
 

From 0a5e8eb862025d609164405f38f65a6e95e91557 Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Thu, 28 Feb 2019 15:02:20 +0100
Subject: [PATCH 13/33] Move ChainLock signing into TrySignChainTip and call it
 periodically

Later commits will introduce checks for "safe TXs" which might abort the
signing on first try, but succeed a few seconds later, so we periodically
retry to sign the tip.
---
 src/llmq/quorums_chainlocks.cpp | 52 ++++++++++++++++++++++++---------
 src/llmq/quorums_chainlocks.h   |  6 ++--
 src/llmq/quorums_init.cpp       |  4 +--
 3 files changed, 45 insertions(+), 17 deletions(-)

diff --git a/src/llmq/quorums_chainlocks.cpp b/src/llmq/quorums_chainlocks.cpp
index 67c9ddbeffee..eed337bfd861 100644
--- a/src/llmq/quorums_chainlocks.cpp
+++ b/src/llmq/quorums_chainlocks.cpp
@@ -35,12 +35,16 @@ CChainLocksHandler::~CChainLocksHandler()
 {
 }
 
-void CChainLocksHandler::RegisterAsRecoveredSigsListener()
+void CChainLocksHandler::Start()
 {
     quorumSigningManager->RegisterRecoveredSigsListener(this);
+    scheduler->scheduleEvery([&]() {
+        // regularely retry signing the current chaintip as it might have failed before due to missing ixlocks
+        TrySignChainTip();
+    }, 5000);
 }
 
-void CChainLocksHandler::UnregisterAsRecoveredSigsListener()
+void CChainLocksHandler::Stop()
 {
     quorumSigningManager->UnregisterRecoveredSigsListener(this);
 }
@@ -184,30 +188,52 @@ void CChainLocksHandler::AcceptedBlockHeader(const CBlockIndex* pindexNew)
 
 void CChainLocksHandler::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork)
 {
+    // don't call TrySignChainTip directly but instead let the scheduler call it. This way we ensure that cs_main is
+    // never locked and TrySignChainTip is not called twice in parallel
+    LOCK(cs);
+    if (tryLockChainTipScheduled) {
+        return;
+    }
+    tryLockChainTipScheduled = true;
+    scheduler->scheduleFromNow([&]() {
+        TrySignChainTip();
+        LOCK(cs);
+        tryLockChainTipScheduled = false;
+    }, 0);
+}
+
+void CChainLocksHandler::TrySignChainTip()
+{
+    Cleanup();
+
+    const CBlockIndex* pindex;
+    {
+        LOCK(cs_main);
+        pindex = chainActive.Tip();
+    }
+
     if (!fMasternodeMode) {
         return;
     }
-    if (!pindexNew->pprev) {
+    if (!pindex->pprev) {
         return;
     }
     if (!sporkManager.IsSporkActive(SPORK_19_CHAINLOCKS_ENABLED)) {
         return;
     }
 
-    Cleanup();
-
     // DIP8 defines a process called "Signing attempts" which should run before the CLSIG is finalized
     // To simplify the initial implementation, we skip this process and directly try to create a CLSIG
     // This will fail when multiple blocks compete, but we accept this for the initial implementation.
     // Later, we'll add the multiple attempts process.
 
-    uint256 requestId = ::SerializeHash(std::make_pair(CLSIG_REQUESTID_PREFIX, pindexNew->nHeight));
-    uint256 msgHash = pindexNew->GetBlockHash();
+    uint256 requestId = ::SerializeHash(std::make_pair(CLSIG_REQUESTID_PREFIX, pindex->nHeight));
+    uint256 msgHash = pindex->GetBlockHash();
 
     {
         LOCK(cs);
 
-        if (bestChainLockBlockIndex == pindexNew) {
+        if (bestChainLockBlockIndex == pindex) {
             // we first got the CLSIG, then the header, and then the block was connected.
             // In this case there is no need to continue here.
             // However, NotifyChainLock might not have been called yet, so call it now if needed
@@ -218,26 +244,26 @@ void CChainLocksHandler::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBl
             return;
         }
 
-        if (InternalHasConflictingChainLock(pindexNew->nHeight, pindexNew->GetBlockHash())) {
+        if (InternalHasConflictingChainLock(pindex->nHeight, pindex->GetBlockHash())) {
             if (!inEnforceBestChainLock) {
                 // we accepted this block when there was no lock yet, but now a conflicting lock appeared. Invalidate it.
                 LogPrintf("CChainLocksHandler::%s -- conflicting lock after block was accepted, invalidating now\n",
                           __func__);
-                ScheduleInvalidateBlock(pindexNew);
+                ScheduleInvalidateBlock(pindex);
             }
             return;
         }
 
-        if (bestChainLock.nHeight >= pindexNew->nHeight) {
+        if (bestChainLock.nHeight >= pindex->nHeight) {
             // already got the same CLSIG or a better one
             return;
         }
 
-        if (pindexNew->nHeight == lastSignedHeight) {
+        if (pindex->nHeight == lastSignedHeight) {
             // already signed this one
             return;
         }
-        lastSignedHeight = pindexNew->nHeight;
+        lastSignedHeight = pindex->nHeight;
         lastSignedRequestId = requestId;
         lastSignedMsgHash = msgHash;
     }
diff --git a/src/llmq/quorums_chainlocks.h b/src/llmq/quorums_chainlocks.h
index 410f2bf80bcb..93ed32404f88 100644
--- a/src/llmq/quorums_chainlocks.h
+++ b/src/llmq/quorums_chainlocks.h
@@ -49,6 +49,7 @@ class CChainLocksHandler : public CRecoveredSigsListener
 private:
     CScheduler* scheduler;
     CCriticalSection cs;
+    bool tryLockChainTipScheduled{false};
     std::atomic<bool> inEnforceBestChainLock{false};
 
     uint256 bestChainLockHash;
@@ -74,8 +75,8 @@ class CChainLocksHandler : public CRecoveredSigsListener
     CChainLocksHandler(CScheduler* _scheduler);
     ~CChainLocksHandler();
 
-    void RegisterAsRecoveredSigsListener();
-    void UnregisterAsRecoveredSigsListener();
+    void Start();
+    void Stop();
 
     bool AlreadyHave(const CInv& inv);
     bool GetChainLockByHash(const uint256& hash, CChainLockSig& ret);
@@ -86,6 +87,7 @@ class CChainLocksHandler : public CRecoveredSigsListener
     void UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork);
     void NewPoWValidBlock(const CBlockIndex* pindex, const std::shared_ptr<const CBlock>& block);
     void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock);
+    void TrySignChainTip();
     void EnforceBestChainLock();
     virtual void HandleNewRecoveredSig(const CRecoveredSig& recoveredSig);
 
diff --git a/src/llmq/quorums_init.cpp b/src/llmq/quorums_init.cpp
index a2620683b6fc..578d9783e4bb 100644
--- a/src/llmq/quorums_init.cpp
+++ b/src/llmq/quorums_init.cpp
@@ -66,7 +66,7 @@ void StartLLMQSystem()
         quorumSigSharesManager->StartWorkerThread();
     }
     if (chainLocksHandler) {
-        chainLocksHandler->RegisterAsRecoveredSigsListener();
+        chainLocksHandler->Start();
     }
     if (quorumInstantSendManager) {
         quorumInstantSendManager->RegisterAsRecoveredSigsListener();
@@ -79,7 +79,7 @@ void StopLLMQSystem()
         quorumInstantSendManager->UnregisterAsRecoveredSigsListener();
     }
     if (chainLocksHandler) {
-        chainLocksHandler->UnregisterAsRecoveredSigsListener();
+        chainLocksHandler->Stop();
     }
     if (quorumSigSharesManager) {
         quorumSigSharesManager->StopWorkerThread();

From 96291e7a0f9a363b8d55aa613898af0615b1d0e5 Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Fri, 1 Mar 2019 06:45:11 +0100
Subject: [PATCH 14/33] Cheaper/Faster bailout from TrySignChainTip when
 already signed before

---
 src/llmq/quorums_chainlocks.cpp | 26 +++++++++++++++++---------
 1 file changed, 17 insertions(+), 9 deletions(-)

diff --git a/src/llmq/quorums_chainlocks.cpp b/src/llmq/quorums_chainlocks.cpp
index eed337bfd861..eb016ed04579 100644
--- a/src/llmq/quorums_chainlocks.cpp
+++ b/src/llmq/quorums_chainlocks.cpp
@@ -227,9 +227,6 @@ void CChainLocksHandler::TrySignChainTip()
     // This will fail when multiple blocks compete, but we accept this for the initial implementation.
     // Later, we'll add the multiple attempts process.
 
-    uint256 requestId = ::SerializeHash(std::make_pair(CLSIG_REQUESTID_PREFIX, pindex->nHeight));
-    uint256 msgHash = pindex->GetBlockHash();
-
     {
         LOCK(cs);
 
@@ -244,6 +241,16 @@ void CChainLocksHandler::TrySignChainTip()
             return;
         }
 
+        if (pindex->nHeight == lastSignedHeight) {
+            // already signed this one
+            return;
+        }
+
+        if (bestChainLock.nHeight >= pindex->nHeight) {
+            // already got the same CLSIG or a better one
+            return;
+        }
+
         if (InternalHasConflictingChainLock(pindex->nHeight, pindex->GetBlockHash())) {
             if (!inEnforceBestChainLock) {
                 // we accepted this block when there was no lock yet, but now a conflicting lock appeared. Invalidate it.
@@ -253,14 +260,15 @@ void CChainLocksHandler::TrySignChainTip()
             }
             return;
         }
+    }
 
-        if (bestChainLock.nHeight >= pindex->nHeight) {
-            // already got the same CLSIG or a better one
-            return;
-        }
+    uint256 requestId = ::SerializeHash(std::make_pair(CLSIG_REQUESTID_PREFIX, pindex->nHeight));
+    uint256 msgHash = pindex->GetBlockHash();
 
-        if (pindex->nHeight == lastSignedHeight) {
-            // already signed this one
+    {
+        LOCK(cs);
+        if (bestChainLock.nHeight >= pindex->nHeight) {
+            // might have happened while we didn't hold cs
             return;
         }
         lastSignedHeight = pindex->nHeight;

From 2a7a5c6338c3b528a1627faf72a3d39c64a3adb6 Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Fri, 1 Mar 2019 06:46:31 +0100
Subject: [PATCH 15/33] Only sign ChainLocks when all included TXs are "safe"

Safe means that the TX is either ixlocked or known since at least 10
minutes.

Also change miner code to only include safe TXs in block templates.
---
 src/llmq/quorums_chainlocks.cpp | 80 +++++++++++++++++++++++++++++++++
 src/llmq/quorums_chainlocks.h   |  5 +++
 src/miner.cpp                   | 10 +++++
 3 files changed, 95 insertions(+)

diff --git a/src/llmq/quorums_chainlocks.cpp b/src/llmq/quorums_chainlocks.cpp
index eb016ed04579..4d26115caedc 100644
--- a/src/llmq/quorums_chainlocks.cpp
+++ b/src/llmq/quorums_chainlocks.cpp
@@ -4,6 +4,7 @@
 
 #include "quorums.h"
 #include "quorums_chainlocks.h"
+#include "quorums_instantsend.h"
 #include "quorums_signing.h"
 #include "quorums_utils.h"
 
@@ -262,6 +263,61 @@ void CChainLocksHandler::TrySignChainTip()
         }
     }
 
+    LogPrintf("CChainLocksHandler::%s -- trying to sign %s, height=%d\n", __func__, pindex->GetBlockHash().ToString(), pindex->nHeight);
+
+    // When the new IX system is activated, we only try to ChainLock blocks which include safe transactions. A TX is
+    // considered safe when it is ixlocked or at least known since 10 minutes (from mempool or block). These checks are
+    // performed for the tip (which we try to sign) and the previous 5 blocks. If a ChainLocked block is found on the
+    // way down, we consider all TXs to be safe.
+    if (IsNewInstantSendEnabled() && sporkManager.IsSporkActive(SPORK_3_INSTANTSEND_BLOCK_FILTERING)) {
+        auto pindexWalk = pindex;
+        while (pindexWalk) {
+            if (pindex->nHeight - pindexWalk->nHeight > 5) {
+                // no need to check further down, 6 confs is safe to assume that TXs below this height won't be
+                // ixlocked anymore if they aren't already
+                LogPrintf("CChainLocksHandler::%s -- tip and previous 5 blocks all safe\n", __func__);
+                break;
+            }
+            if (HasChainLock(pindexWalk->nHeight, pindexWalk->GetBlockHash())) {
+                // we don't care about ixlocks for TXs that are ChainLocked already
+                LogPrintf("CChainLocksHandler::%s -- chainlock at height %d \n", __func__, pindexWalk->nHeight);
+                break;
+            }
+
+            decltype(blockTxs.begin()->second) txids;
+            {
+                LOCK(cs);
+                auto it = blockTxs.find(pindexWalk->GetBlockHash());
+                if (it == blockTxs.end()) {
+                    // this should actually not happen as NewPoWValidBlock should have been called before
+                    LogPrintf("CChainLocksHandler::%s -- blockTxs for %s not found\n", __func__,
+                              pindexWalk->GetBlockHash().ToString());
+                    return;
+                }
+                txids = it->second;
+            }
+
+            for (auto& txid : *txids) {
+                int64_t txAge = 0;
+                {
+                    LOCK(cs);
+                    auto it = txFirstSeenTime.find(txid);
+                    if (it != txFirstSeenTime.end()) {
+                        txAge = GetAdjustedTime() - it->second;
+                    }
+                }
+
+                if (txAge < WAIT_FOR_IXLOCK_TIMEOUT && !quorumInstantSendManager->IsLocked(txid)) {
+                    LogPrintf("CChainLocksHandler::%s -- not signing block %s due to TX %s not being ixlocked and not old enough. age=%d\n", __func__,
+                              pindexWalk->GetBlockHash().ToString(), txid.ToString(), txAge);
+                    return;
+                }
+            }
+
+            pindexWalk = pindexWalk->pprev;
+        }
+    }
+
     uint256 requestId = ::SerializeHash(std::make_pair(CLSIG_REQUESTID_PREFIX, pindex->nHeight));
     uint256 msgHash = pindex->GetBlockHash();
 
@@ -324,6 +380,30 @@ void CChainLocksHandler::SyncTransaction(const CTransaction& tx, const CBlockInd
     txFirstSeenTime.emplace(tx.GetHash(), curTime);
 }
 
+bool CChainLocksHandler::IsTxSafeForMining(const uint256& txid)
+{
+    if (!sporkManager.IsSporkActive(SPORK_19_CHAINLOCKS_ENABLED) || !sporkManager.IsSporkActive(SPORK_3_INSTANTSEND_BLOCK_FILTERING)) {
+        return true;
+    }
+    if (!IsNewInstantSendEnabled()) {
+        return true;
+    }
+
+    int64_t txAge = 0;
+    {
+        LOCK(cs);
+        auto it = txFirstSeenTime.find(txid);
+        if (it != txFirstSeenTime.end()) {
+            txAge = GetAdjustedTime() - it->second;
+        }
+    }
+
+    if (txAge < WAIT_FOR_IXLOCK_TIMEOUT && !quorumInstantSendManager->IsLocked(txid)) {
+        return false;
+    }
+    return true;
+}
+
 // WARNING: cs_main and cs should not be held!
 void CChainLocksHandler::EnforceBestChainLock()
 {
diff --git a/src/llmq/quorums_chainlocks.h b/src/llmq/quorums_chainlocks.h
index 93ed32404f88..05fbd9a691ad 100644
--- a/src/llmq/quorums_chainlocks.h
+++ b/src/llmq/quorums_chainlocks.h
@@ -46,6 +46,9 @@ class CChainLocksHandler : public CRecoveredSigsListener
     static const int64_t CLEANUP_INTERVAL = 1000 * 30;
     static const int64_t CLEANUP_SEEN_TIMEOUT = 24 * 60 * 60 * 1000;
 
+    // how long to wait for ixlocks until we consider a block with non-ixlocked TXs to be safe to sign
+    static const int64_t WAIT_FOR_IXLOCK_TIMEOUT = 10 * 60;
+
 private:
     CScheduler* scheduler;
     CCriticalSection cs;
@@ -94,6 +97,8 @@ class CChainLocksHandler : public CRecoveredSigsListener
     bool HasChainLock(int nHeight, const uint256& blockHash);
     bool HasConflictingChainLock(int nHeight, const uint256& blockHash);
 
+    bool IsTxSafeForMining(const uint256& txid);
+
 private:
     // these require locks to be held already
     bool InternalHasChainLock(int nHeight, const uint256& blockHash);
diff --git a/src/miner.cpp b/src/miner.cpp
index c07061f329ca..1e2e8ba2c459 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -34,6 +34,7 @@
 #include "evo/deterministicmns.h"
 
 #include "llmq/quorums_blockprocessor.h"
+#include "llmq/quorums_chainlocks.h"
 
 #include <algorithm>
 #include <boost/thread.hpp>
@@ -453,6 +454,11 @@ void BlockAssembler::addPackageTxs(int &nPackagesSelected, int &nDescendantsUpda
             continue;
         }
 
+        if (!llmq::chainLocksHandler->IsTxSafeForMining(mi->GetTx().GetHash())) {
+            ++mi;
+            continue;
+        }
+
         // Now that mi is not stale, determine which transaction to evaluate:
         // the next entry from mapTx, or the best from mapModifiedTx?
         bool fUsingModified = false;
@@ -601,6 +607,10 @@ void BlockAssembler::addPriorityTxs()
             continue;
         }
 
+        if (!llmq::chainLocksHandler->IsTxSafeForMining(iter->GetTx().GetHash())) {
+            continue;
+        }
+
         // If this tx fits in the block add it, otherwise keep looping
         if (TestForBlock(iter)) {
             AddToBlock(iter);

From baf8b81c4a78b19995bd2942de9a5d88bb25dd8d Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Fri, 1 Mar 2019 15:27:51 +0100
Subject: [PATCH 16/33] Fix no-wallet build

---
 src/llmq/quorums_instantsend.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/llmq/quorums_instantsend.cpp b/src/llmq/quorums_instantsend.cpp
index 737c22b29a10..db8490675268 100644
--- a/src/llmq/quorums_instantsend.cpp
+++ b/src/llmq/quorums_instantsend.cpp
@@ -15,7 +15,10 @@
 #include "scheduler.h"
 #include "spork.h"
 #include "validation.h"
+
+#ifdef ENABLE_WALLET
 #include "wallet/wallet.h"
+#endif
 
 // needed for nCompleteTXLocks
 #include "instantx.h"

From f44f09ca04f3415189b6d3bd0a5f17412b251687 Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Fri, 1 Mar 2019 15:29:49 +0100
Subject: [PATCH 17/33] Fix crash in BlockAssembler::addPackageTxs

---
 src/miner.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/miner.cpp b/src/miner.cpp
index 1e2e8ba2c459..64fd4fc93f53 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -454,7 +454,8 @@ void BlockAssembler::addPackageTxs(int &nPackagesSelected, int &nDescendantsUpda
             continue;
         }
 
-        if (!llmq::chainLocksHandler->IsTxSafeForMining(mi->GetTx().GetHash())) {
+        if (mi != mempool.mapTx.get<ancestor_score>().end() &&
+                !llmq::chainLocksHandler->IsTxSafeForMining(mi->GetTx().GetHash())) {
             ++mi;
             continue;
         }

From 5b8344e8f85ae6335d594465ba5b8fbe9a73ee96 Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Fri, 1 Mar 2019 16:10:24 +0100
Subject: [PATCH 18/33] Use scheduleFromNow instead of schedule+boost::chrono

---
 src/llmq/quorums_instantsend.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/llmq/quorums_instantsend.cpp b/src/llmq/quorums_instantsend.cpp
index db8490675268..21eaf63f2266 100644
--- a/src/llmq/quorums_instantsend.cpp
+++ b/src/llmq/quorums_instantsend.cpp
@@ -382,9 +382,9 @@ void CInstantSendManager::ProcessMessageInstantXLock(CNode* pfrom, const llmq::C
 
     if (!hasScheduledProcessPending) {
         hasScheduledProcessPending = true;
-        scheduler->schedule([&] {
+        scheduler->scheduleFromNow([&] {
             ProcessPendingInstantXLocks();
-        }, boost::chrono::system_clock::now() + boost::chrono::milliseconds(100));
+        }, 100);
     }
 }
 

From e2f99f4ae1f9ff888ba748ca7b806946520f2567 Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Fri, 1 Mar 2019 16:13:47 +0100
Subject: [PATCH 19/33] Set llmqForInstantSend for mainnet and testnet

---
 src/chainparams.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 314584f7d788..8d850068c2c1 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -308,6 +308,7 @@ class CMainParams : public CChainParams {
         consensus.llmqs[Consensus::LLMQ_400_60] = llmq400_60;
         consensus.llmqs[Consensus::LLMQ_400_85] = llmq400_85;
         consensus.llmqChainLocks = Consensus::LLMQ_400_60;
+        consensus.llmqForInstantSend = Consensus::LLMQ_50_60;
 
         fMiningRequiresPeers = true;
         fDefaultConsistencyChecks = false;
@@ -476,6 +477,7 @@ class CTestNetParams : public CChainParams {
         consensus.llmqs[Consensus::LLMQ_400_60] = llmq400_60;
         consensus.llmqs[Consensus::LLMQ_400_85] = llmq400_85;
         consensus.llmqChainLocks = Consensus::LLMQ_50_60;
+        consensus.llmqForInstantSend = Consensus::LLMQ_50_60;
 
         fMiningRequiresPeers = true;
         fDefaultConsistencyChecks = false;

From 9e9081110bd7b2390c27247f32379b58501143de Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Mon, 4 Mar 2019 07:57:51 +0100
Subject: [PATCH 20/33] Add override keywork to
 CDSNotificationInterface::NotifyChainLock

---
 src/dsnotificationinterface.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/dsnotificationinterface.h b/src/dsnotificationinterface.h
index 60c1e5bd3611..c345551153e3 100644
--- a/src/dsnotificationinterface.h
+++ b/src/dsnotificationinterface.h
@@ -24,7 +24,7 @@ class CDSNotificationInterface : public CValidationInterface
     void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& block) override;
     void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock) override;
     void NotifyMasternodeListChanged(const CDeterministicMNList& newList) override;
-    void NotifyChainLock(const CBlockIndex* pindex);
+    void NotifyChainLock(const CBlockIndex* pindex) override;
 
 private:
     CConnman& connman;

From 280690792ab450fc73c458fe7f1a3fd928ded1d8 Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Mon, 4 Mar 2019 07:58:12 +0100
Subject: [PATCH 21/33] Combine loops in CChainLocksHandler::NewPoWValidBlock

---
 src/llmq/quorums_chainlocks.cpp | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/src/llmq/quorums_chainlocks.cpp b/src/llmq/quorums_chainlocks.cpp
index 4d26115caedc..fa3d40837734 100644
--- a/src/llmq/quorums_chainlocks.cpp
+++ b/src/llmq/quorums_chainlocks.cpp
@@ -344,6 +344,8 @@ void CChainLocksHandler::NewPoWValidBlock(const CBlockIndex* pindex, const std::
         return;
     }
 
+    int64_t curTime = GetAdjustedTime();
+
     // We listen for NewPoWValidBlock so that we can collect all TX ids of all included TXs of newly received blocks
     // We need this information later when we try to sign a new tip, so that we can determine if all included TXs are
     // safe.
@@ -357,13 +359,9 @@ void CChainLocksHandler::NewPoWValidBlock(const CBlockIndex* pindex, const std::
             }
         }
         txs->emplace(tx->GetHash());
-    }
-    blockTxs[pindex->GetBlockHash()] = txs;
-
-    int64_t curTime = GetAdjustedTime();
-    for (auto& tx : block->vtx) {
         txFirstSeenTime.emplace(tx->GetHash(), curTime);
     }
+    blockTxs[pindex->GetBlockHash()] = txs;
 }
 
 void CChainLocksHandler::SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int posInBlock)

From f5dcb00acf69efed63af8b033fa46cfe6535bb6c Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Mon, 4 Mar 2019 08:04:18 +0100
Subject: [PATCH 22/33] Introduce spork SPORK_20_INSTANTSEND_LLMQ_BASED to
 switch between new/old system

---
 src/llmq/quorums_instantsend.cpp | 15 +++------------
 src/llmq/quorums_instantsend.h   | 12 +++++-------
 src/spork.cpp                    |  3 +++
 src/spork.h                      |  3 ++-
 4 files changed, 13 insertions(+), 20 deletions(-)

diff --git a/src/llmq/quorums_instantsend.cpp b/src/llmq/quorums_instantsend.cpp
index 21eaf63f2266..3eb7c550f366 100644
--- a/src/llmq/quorums_instantsend.cpp
+++ b/src/llmq/quorums_instantsend.cpp
@@ -861,26 +861,17 @@ bool CInstantSendManager::GetConflictingTx(const CTransaction& tx, uint256& retC
 
 bool IsOldInstantSendEnabled()
 {
-    int spork2Value = sporkManager.GetSporkValue(SPORK_2_INSTANTSEND_ENABLED);
-    if (spork2Value == 0) {
-        return true;
-    }
-    return false;
+    return sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED) && !sporkManager.IsSporkActive(SPORK_20_INSTANTSEND_LLMQ_BASED);
 }
 
 bool IsNewInstantSendEnabled()
 {
-    int spork2Value = sporkManager.GetSporkValue(SPORK_2_INSTANTSEND_ENABLED);
-    if (spork2Value == 1) {
-        return true;
-    }
-    return false;
+    return sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED) && sporkManager.IsSporkActive(SPORK_20_INSTANTSEND_LLMQ_BASED);
 }
 
 bool IsInstantSendEnabled()
 {
-    int spork2Value = sporkManager.GetSporkValue(SPORK_2_INSTANTSEND_ENABLED);
-    return spork2Value == 0 || spork2Value == 1;
+    return sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED);
 }
 
 }
diff --git a/src/llmq/quorums_instantsend.h b/src/llmq/quorums_instantsend.h
index 36ce131d4093..66a6a1cad377 100644
--- a/src/llmq/quorums_instantsend.h
+++ b/src/llmq/quorums_instantsend.h
@@ -133,13 +133,11 @@ class CInstantSendManager : public CRecoveredSigsListener
 
 extern CInstantSendManager* quorumInstantSendManager;
 
-// The meaning of spork 2 has changed in v0.14. Before that, spork 2 was simply time based and either enabled or not
-// After 0.14, spork 2 can have 3 states.
-// 0 = old system is active (0 is compatible with the value set on mainnet at time of deployment)
-// 1 = new system is active (old nodes will interpret this as the old system being enabled, but then won't get enough IX lock votes)
-// everything else = disabled
-// TODO When the new system is fully deployed and enabled, we can remove this special handling of the spork in a future version
-// and revert to the old behaviour.
+// This involves 2 sporks: SPORK_2_INSTANTSEND_ENABLED and SPORK_20_INSTANTSEND_LLMQ_BASED
+// SPORK_2_INSTANTSEND_ENABLED generally enables/disables InstantSend and SPORK_20_INSTANTSEND_LLMQ_BASED switches
+// between the old and the new (LLMQ based) system
+// TODO When the new system is fully deployed and enabled, we can remove this special handling in a future version
+// and revert to only using SPORK_2_INSTANTSEND_ENABLED.
 bool IsOldInstantSendEnabled();
 bool IsNewInstantSendEnabled();
 bool IsInstantSendEnabled();
diff --git a/src/spork.cpp b/src/spork.cpp
index fe4fbf2967f1..b12d04bcfe5a 100644
--- a/src/spork.cpp
+++ b/src/spork.cpp
@@ -29,6 +29,7 @@ std::map<int, int64_t> mapSporkDefaults = {
     {SPORK_17_QUORUM_DKG_ENABLED,            4070908800ULL}, // OFF
     {SPORK_18_QUORUM_DEBUG_ENABLED,          4070908800ULL}, // OFF
     {SPORK_19_CHAINLOCKS_ENABLED,            4070908800ULL}, // OFF
+    {SPORK_20_INSTANTSEND_LLMQ_BASED,        4070908800ULL}, // OFF
 };
 
 bool CSporkManager::SporkValueIsActive(int nSporkID, int64_t &nActiveValueRet) const
@@ -291,6 +292,7 @@ int CSporkManager::GetSporkIDByName(const std::string& strName)
     if (strName == "SPORK_17_QUORUM_DKG_ENABLED")               return SPORK_17_QUORUM_DKG_ENABLED;
     if (strName == "SPORK_18_QUORUM_DEBUG_ENABLED")             return SPORK_18_QUORUM_DEBUG_ENABLED;
     if (strName == "SPORK_19_CHAINLOCKS_ENABLED")               return SPORK_19_CHAINLOCKS_ENABLED;
+    if (strName == "SPORK_20_INSTANTSEND_LLMQ_BASED")           return SPORK_20_INSTANTSEND_LLMQ_BASED;
 
     LogPrint("spork", "CSporkManager::GetSporkIDByName -- Unknown Spork name '%s'\n", strName);
     return -1;
@@ -310,6 +312,7 @@ std::string CSporkManager::GetSporkNameByID(int nSporkID)
         case SPORK_17_QUORUM_DKG_ENABLED:               return "SPORK_17_QUORUM_DKG_ENABLED";
         case SPORK_18_QUORUM_DEBUG_ENABLED:             return "SPORK_18_QUORUM_DEBUG_ENABLED";
         case SPORK_19_CHAINLOCKS_ENABLED:               return "SPORK_19_CHAINLOCKS_ENABLED";
+        case SPORK_20_INSTANTSEND_LLMQ_BASED:           return "SPORK_20_INSTANTSEND_LLMQ_BASED";
         default:
             LogPrint("spork", "CSporkManager::GetSporkNameByID -- Unknown Spork ID %d\n", nSporkID);
             return "Unknown";
diff --git a/src/spork.h b/src/spork.h
index 55d9d6230556..e02ac30623a9 100644
--- a/src/spork.h
+++ b/src/spork.h
@@ -28,9 +28,10 @@ static const int SPORK_16_INSTANTSEND_AUTOLOCKS                         = 10015;
 static const int SPORK_17_QUORUM_DKG_ENABLED                            = 10016;
 static const int SPORK_18_QUORUM_DEBUG_ENABLED                          = 10017;
 static const int SPORK_19_CHAINLOCKS_ENABLED                            = 10018;
+static const int SPORK_20_INSTANTSEND_LLMQ_BASED                        = 10019;
 
 static const int SPORK_START                                            = SPORK_2_INSTANTSEND_ENABLED;
-static const int SPORK_END                                              = SPORK_19_CHAINLOCKS_ENABLED;
+static const int SPORK_END                                              = SPORK_20_INSTANTSEND_LLMQ_BASED;
 
 extern std::map<int, int64_t> mapSporkDefaults;
 extern CSporkManager sporkManager;

From 2299ee2836017c53d94590c8bfc5cd334cf9647e Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Mon, 4 Mar 2019 10:58:47 +0100
Subject: [PATCH 23/33] Rename IXLOCK to ISLOCK and InstantX to InstantSend

---
 src/llmq/quorums_chainlocks.cpp  |   4 +-
 src/llmq/quorums_chainlocks.h    |   2 +-
 src/llmq/quorums_instantsend.cpp | 302 +++++++++++++++----------------
 src/llmq/quorums_instantsend.h   |  62 +++----
 src/llmq/quorums_signing.cpp     |   2 +-
 src/net_processing.cpp           |  12 +-
 src/protocol.cpp                 |   6 +-
 src/protocol.h                   |   4 +-
 src/wallet/wallet.cpp            |   2 +-
 9 files changed, 198 insertions(+), 198 deletions(-)

diff --git a/src/llmq/quorums_chainlocks.cpp b/src/llmq/quorums_chainlocks.cpp
index fa3d40837734..a077224c9e3e 100644
--- a/src/llmq/quorums_chainlocks.cpp
+++ b/src/llmq/quorums_chainlocks.cpp
@@ -307,7 +307,7 @@ void CChainLocksHandler::TrySignChainTip()
                     }
                 }
 
-                if (txAge < WAIT_FOR_IXLOCK_TIMEOUT && !quorumInstantSendManager->IsLocked(txid)) {
+                if (txAge < WAIT_FOR_ISLOCK_TIMEOUT && !quorumInstantSendManager->IsLocked(txid)) {
                     LogPrintf("CChainLocksHandler::%s -- not signing block %s due to TX %s not being ixlocked and not old enough. age=%d\n", __func__,
                               pindexWalk->GetBlockHash().ToString(), txid.ToString(), txAge);
                     return;
@@ -396,7 +396,7 @@ bool CChainLocksHandler::IsTxSafeForMining(const uint256& txid)
         }
     }
 
-    if (txAge < WAIT_FOR_IXLOCK_TIMEOUT && !quorumInstantSendManager->IsLocked(txid)) {
+    if (txAge < WAIT_FOR_ISLOCK_TIMEOUT && !quorumInstantSendManager->IsLocked(txid)) {
         return false;
     }
     return true;
diff --git a/src/llmq/quorums_chainlocks.h b/src/llmq/quorums_chainlocks.h
index 05fbd9a691ad..722621fc654f 100644
--- a/src/llmq/quorums_chainlocks.h
+++ b/src/llmq/quorums_chainlocks.h
@@ -47,7 +47,7 @@ class CChainLocksHandler : public CRecoveredSigsListener
     static const int64_t CLEANUP_SEEN_TIMEOUT = 24 * 60 * 60 * 1000;
 
     // how long to wait for ixlocks until we consider a block with non-ixlocked TXs to be safe to sign
-    static const int64_t WAIT_FOR_IXLOCK_TIMEOUT = 10 * 60;
+    static const int64_t WAIT_FOR_ISLOCK_TIMEOUT = 10 * 60;
 
 private:
     CScheduler* scheduler;
diff --git a/src/llmq/quorums_instantsend.cpp b/src/llmq/quorums_instantsend.cpp
index 3eb7c550f366..6104f5b3281a 100644
--- a/src/llmq/quorums_instantsend.cpp
+++ b/src/llmq/quorums_instantsend.cpp
@@ -29,14 +29,14 @@ namespace llmq
 {
 
 static const std::string INPUTLOCK_REQUESTID_PREFIX = "inlock";
-static const std::string IXLOCK_REQUESTID_PREFIX = "ixlock";
+static const std::string ISLOCK_REQUESTID_PREFIX = "islock";
 
 CInstantSendManager* quorumInstantSendManager;
 
-uint256 CInstantXLock::GetRequestId() const
+uint256 CInstantSendLock::GetRequestId() const
 {
     CHashWriter hw(SER_GETHASH, 0);
-    hw << IXLOCK_REQUESTID_PREFIX;
+    hw << ISLOCK_REQUESTID_PREFIX;
     hw << inputs;
     return hw.GetHash();
 }
@@ -102,7 +102,7 @@ bool CInstantSendManager::ProcessTx(CNode* pfrom, const CTransaction& tx, CConnm
             auto it = inputVotes.find(ids[i]);
             if (it != inputVotes.end()) {
                 if (it->second != tx.GetHash()) {
-                    LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s: input %s is conflicting with ixlock %s\n", __func__,
+                    LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s: input %s is conflicting with islock %s\n", __func__,
                             tx.GetHash().ToString(), tx.vin[i].prevout.ToStringShort(), it->second.ToString());
                     return false;
                 }
@@ -129,8 +129,8 @@ bool CInstantSendManager::ProcessTx(CNode* pfrom, const CTransaction& tx, CConnm
     }
 
     // We might have received all input locks before we got the corresponding TX. In this case, we have to sign the
-    // ixlock now instead of waiting for the input locks.
-    TrySignInstantXLock(tx);
+    // islock now instead of waiting for the input locks.
+    TrySignInstantSendLock(tx);
 
     return true;
 }
@@ -237,21 +237,21 @@ void CInstantSendManager::HandleNewRecoveredSig(const CRecoveredSig& recoveredSi
     auto& params = Params().GetConsensus().llmqs.at(llmqType);
 
     uint256 txid;
-    bool isInstantXLock = false;
+    bool isInstantSendLock = false;
     {
         LOCK(cs);
         auto it = inputVotes.find(recoveredSig.id);
         if (it != inputVotes.end()) {
             txid = it->second;
         }
-        if (creatingInstantXLocks.count(recoveredSig.id)) {
-            isInstantXLock = true;
+        if (creatingInstantSendLocks.count(recoveredSig.id)) {
+            isInstantSendLock = true;
         }
     }
     if (!txid.IsNull()) {
         HandleNewInputLockRecoveredSig(recoveredSig, txid);
-    } else if (isInstantXLock) {
-        HandleNewInstantXLockRecoveredSig(recoveredSig);
+    } else if (isInstantSendLock) {
+        HandleNewInstantSendLockRecoveredSig(recoveredSig);
     }
 }
 
@@ -276,10 +276,10 @@ void CInstantSendManager::HandleNewInputLockRecoveredSig(const CRecoveredSig& re
         }
     }
 
-    TrySignInstantXLock(*tx);
+    TrySignInstantSendLock(*tx);
 }
 
-void CInstantSendManager::TrySignInstantXLock(const CTransaction& tx)
+void CInstantSendManager::TrySignInstantSendLock(const CTransaction& tx)
 {
     auto llmqType = Params().GetConsensus().llmqForInstantSend;
 
@@ -290,17 +290,17 @@ void CInstantSendManager::TrySignInstantXLock(const CTransaction& tx)
         }
     }
 
-    LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s: got all recovered sigs, creating CInstantXLock\n", __func__,
+    LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s: got all recovered sigs, creating CInstantSendLock\n", __func__,
             tx.GetHash().ToString());
 
-    CInstantXLockInfo ixlockInfo;
-    ixlockInfo.time = GetTimeMillis();
-    ixlockInfo.ixlock.txid = tx.GetHash();
+    CInstantSendLockInfo islockInfo;
+    islockInfo.time = GetTimeMillis();
+    islockInfo.islock.txid = tx.GetHash();
     for (auto& in : tx.vin) {
-        ixlockInfo.ixlock.inputs.emplace_back(in.prevout);
+        islockInfo.islock.inputs.emplace_back(in.prevout);
     }
 
-    auto id = ixlockInfo.ixlock.GetRequestId();
+    auto id = islockInfo.islock.GetRequestId();
 
     if (quorumSigningManager->HasRecoveredSigForId(llmqType, id)) {
         return;
@@ -308,40 +308,40 @@ void CInstantSendManager::TrySignInstantXLock(const CTransaction& tx)
 
     {
         LOCK(cs);
-        auto e = creatingInstantXLocks.emplace(id, ixlockInfo);
+        auto e = creatingInstantSendLocks.emplace(id, islockInfo);
         if (!e.second) {
             return;
         }
-        txToCreatingInstantXLocks.emplace(tx.GetHash(), &e.first->second);
+        txToCreatingInstantSendLocks.emplace(tx.GetHash(), &e.first->second);
     }
 
     quorumSigningManager->AsyncSignIfMember(llmqType, id, tx.GetHash());
 }
 
-void CInstantSendManager::HandleNewInstantXLockRecoveredSig(const llmq::CRecoveredSig& recoveredSig)
+void CInstantSendManager::HandleNewInstantSendLockRecoveredSig(const llmq::CRecoveredSig& recoveredSig)
 {
-    CInstantXLockInfo ixlockInfo;
+    CInstantSendLockInfo islockInfo;
 
     {
         LOCK(cs);
-        auto it = creatingInstantXLocks.find(recoveredSig.id);
-        if (it == creatingInstantXLocks.end()) {
+        auto it = creatingInstantSendLocks.find(recoveredSig.id);
+        if (it == creatingInstantSendLocks.end()) {
             return;
         }
 
-        ixlockInfo = std::move(it->second);
-        creatingInstantXLocks.erase(it);
-        txToCreatingInstantXLocks.erase(ixlockInfo.ixlock.txid);
+        islockInfo = std::move(it->second);
+        creatingInstantSendLocks.erase(it);
+        txToCreatingInstantSendLocks.erase(islockInfo.islock.txid);
     }
 
-    if (ixlockInfo.ixlock.txid != recoveredSig.msgHash) {
-        LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s: ixlock conflicts with %s, dropping own version", __func__,
-                ixlockInfo.ixlock.txid.ToString(), recoveredSig.msgHash.ToString());
+    if (islockInfo.islock.txid != recoveredSig.msgHash) {
+        LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s: islock conflicts with %s, dropping own version", __func__,
+                islockInfo.islock.txid.ToString(), recoveredSig.msgHash.ToString());
         return;
     }
 
-    ixlockInfo.ixlock.sig = recoveredSig.sig;
-    ProcessInstantXLock(-1, ::SerializeHash(ixlockInfo.ixlock), ixlockInfo.ixlock);
+    islockInfo.islock.sig = recoveredSig.sig;
+    ProcessInstantSendLock(-1, ::SerializeHash(islockInfo.islock), islockInfo.islock);
 }
 
 void CInstantSendManager::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman)
@@ -350,17 +350,17 @@ void CInstantSendManager::ProcessMessage(CNode* pfrom, const std::string& strCom
         return;
     }
 
-    if (strCommand == NetMsgType::IXLOCK) {
-        CInstantXLock ixlock;
-        vRecv >> ixlock;
-        ProcessMessageInstantXLock(pfrom, ixlock, connman);
+    if (strCommand == NetMsgType::ISLOCK) {
+        CInstantSendLock islock;
+        vRecv >> islock;
+        ProcessMessageInstantSendLock(pfrom, islock, connman);
     }
 }
 
-void CInstantSendManager::ProcessMessageInstantXLock(CNode* pfrom, const llmq::CInstantXLock& ixlock, CConnman& connman)
+void CInstantSendManager::ProcessMessageInstantSendLock(CNode* pfrom, const llmq::CInstantSendLock& islock, CConnman& connman)
 {
     bool ban = false;
-    if (!PreVerifyInstantXLock(pfrom->id, ixlock, ban)) {
+    if (!PreVerifyInstantSendLock(pfrom->id, islock, ban)) {
         if (ban) {
             LOCK(cs_main);
             Misbehaving(pfrom->id, 100);
@@ -368,37 +368,37 @@ void CInstantSendManager::ProcessMessageInstantXLock(CNode* pfrom, const llmq::C
         return;
     }
 
-    auto hash = ::SerializeHash(ixlock);
+    auto hash = ::SerializeHash(islock);
 
     LOCK(cs);
-    if (pendingInstantXLocks.count(hash) || finalInstantXLocks.count(hash)) {
+    if (pendingInstantSendLocks.count(hash) || finalInstantSendLocks.count(hash)) {
         return;
     }
 
-    LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, ixlock=%s: received ixlock, peer=%d\n", __func__,
-            ixlock.txid.ToString(), hash.ToString(), pfrom->id);
+    LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, islock=%s: received islock, peer=%d\n", __func__,
+            islock.txid.ToString(), hash.ToString(), pfrom->id);
 
-    pendingInstantXLocks.emplace(hash, std::make_pair(pfrom->id, std::move(ixlock)));
+    pendingInstantSendLocks.emplace(hash, std::make_pair(pfrom->id, std::move(islock)));
 
     if (!hasScheduledProcessPending) {
         hasScheduledProcessPending = true;
         scheduler->scheduleFromNow([&] {
-            ProcessPendingInstantXLocks();
+            ProcessPendingInstantSendLocks();
         }, 100);
     }
 }
 
-bool CInstantSendManager::PreVerifyInstantXLock(NodeId nodeId, const llmq::CInstantXLock& ixlock, bool& retBan)
+bool CInstantSendManager::PreVerifyInstantSendLock(NodeId nodeId, const llmq::CInstantSendLock& islock, bool& retBan)
 {
     retBan = false;
 
-    if (ixlock.txid.IsNull() || !ixlock.sig.IsValid() || ixlock.inputs.empty()) {
+    if (islock.txid.IsNull() || !islock.sig.IsValid() || islock.inputs.empty()) {
         retBan = true;
         return false;
     }
 
     std::set<COutPoint> dups;
-    for (auto& o : ixlock.inputs) {
+    for (auto& o : islock.inputs) {
         if (!dups.emplace(o).second) {
             retBan = true;
             return false;
@@ -408,16 +408,16 @@ bool CInstantSendManager::PreVerifyInstantXLock(NodeId nodeId, const llmq::CInst
     return true;
 }
 
-void CInstantSendManager::ProcessPendingInstantXLocks()
+void CInstantSendManager::ProcessPendingInstantSendLocks()
 {
     auto llmqType = Params().GetConsensus().llmqForInstantSend;
 
-    decltype(pendingInstantXLocks) pend;
+    decltype(pendingInstantSendLocks) pend;
 
     {
         LOCK(cs);
         hasScheduledProcessPending = false;
-        pend = std::move(pendingInstantXLocks);
+        pend = std::move(pendingInstantSendLocks);
     }
 
     if (!IsNewInstantSendEnabled()) {
@@ -436,12 +436,12 @@ void CInstantSendManager::ProcessPendingInstantXLocks()
     for (const auto& p : pend) {
         auto& hash = p.first;
         auto nodeId = p.second.first;
-        auto& ixlock = p.second.second;
+        auto& islock = p.second.second;
 
-        auto id = ixlock.GetRequestId();
+        auto id = islock.GetRequestId();
 
-        // no need to verify an IXLOCK if we already have verified the recovered sig that belongs to it
-        if (quorumSigningManager->HasRecoveredSig(llmqType, id, ixlock.txid)) {
+        // no need to verify an ISLOCK if we already have verified the recovered sig that belongs to it
+        if (quorumSigningManager->HasRecoveredSig(llmqType, id, islock.txid)) {
             continue;
         }
 
@@ -450,10 +450,10 @@ void CInstantSendManager::ProcessPendingInstantXLocks()
             // should not happen, but if one fails to select, all others will also fail to select
             return;
         }
-        uint256 signHash = CLLMQUtils::BuildSignHash(llmqType, quorum->quorumHash, id, ixlock.txid);
-        batchVerifier.PushMessage(nodeId, hash, signHash, ixlock.sig, quorum->quorumPublicKey);
+        uint256 signHash = CLLMQUtils::BuildSignHash(llmqType, quorum->quorumHash, id, islock.txid);
+        batchVerifier.PushMessage(nodeId, hash, signHash, islock.sig, quorum->quorumPublicKey);
 
-        // We can reconstruct the CRecoveredSig objects from the ixlock and pass it to the signing manager, which
+        // We can reconstruct the CRecoveredSig objects from the islock and pass it to the signing manager, which
         // avoids unnecessary double-verification of the signature. We however only do this when verification here
         // turns out to be good (which is checked further down)
         if (!quorumSigningManager->HasRecoveredSigForId(llmqType, id)) {
@@ -461,8 +461,8 @@ void CInstantSendManager::ProcessPendingInstantXLocks()
             recSig.llmqType = llmqType;
             recSig.quorumHash = quorum->quorumHash;
             recSig.id = id;
-            recSig.msgHash = ixlock.txid;
-            recSig.sig = ixlock.sig;
+            recSig.msgHash = islock.txid;
+            recSig.sig = islock.sig;
             recSigs.emplace(std::piecewise_construct,
                     std::forward_as_tuple(hash),
                     std::forward_as_tuple(std::move(quorum), std::move(recSig)));
@@ -482,15 +482,15 @@ void CInstantSendManager::ProcessPendingInstantXLocks()
     for (const auto& p : pend) {
         auto& hash = p.first;
         auto nodeId = p.second.first;
-        auto& ixlock = p.second.second;
+        auto& islock = p.second.second;
 
         if (batchVerifier.badMessages.count(hash)) {
-            LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, ixlock=%s: invalid sig in ixlock, peer=%d\n", __func__,
-                     ixlock.txid.ToString(), hash.ToString(), nodeId);
+            LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, islock=%s: invalid sig in islock, peer=%d\n", __func__,
+                     islock.txid.ToString(), hash.ToString(), nodeId);
             continue;
         }
 
-        ProcessInstantXLock(nodeId, hash, ixlock);
+        ProcessInstantSendLock(nodeId, hash, islock);
 
         // See comment further on top. We pass a reconstructed recovered sig to the signing manager to avoid
         // double-verification of the sig.
@@ -500,40 +500,40 @@ void CInstantSendManager::ProcessPendingInstantXLocks()
             auto& recSig = it->second.second;
             if (!quorumSigningManager->HasRecoveredSigForId(llmqType, recSig.id)) {
                 recSig.UpdateHash();
-                LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, ixlock=%s: passing reconstructed recSig to signing mgr, peer=%d\n", __func__,
-                         ixlock.txid.ToString(), hash.ToString(), nodeId);
+                LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, islock=%s: passing reconstructed recSig to signing mgr, peer=%d\n", __func__,
+                         islock.txid.ToString(), hash.ToString(), nodeId);
                 quorumSigningManager->PushReconstructedRecoveredSig(recSig, quorum);
             }
         }
     }
 }
 
-void CInstantSendManager::ProcessInstantXLock(NodeId from, const uint256& hash, const CInstantXLock& ixlock)
+void CInstantSendManager::ProcessInstantSendLock(NodeId from, const uint256& hash, const CInstantSendLock& islock)
 {
     {
         LOCK(cs_main);
         g_connman->RemoveAskFor(hash);
     }
 
-    CInstantXLockInfo ixlockInfo;
-    ixlockInfo.time = GetTimeMillis();
-    ixlockInfo.ixlock = ixlock;
-    ixlockInfo.ixlockHash = hash;
+    CInstantSendLockInfo islockInfo;
+    islockInfo.time = GetTimeMillis();
+    islockInfo.islock = islock;
+    islockInfo.islockHash = hash;
 
     uint256 hashBlock;
     // we ignore failure here as we must be able to propagate the lock even if we don't have the TX locally
-    if (GetTransaction(ixlock.txid, ixlockInfo.tx, Params().GetConsensus(), hashBlock)) {
+    if (GetTransaction(islock.txid, islockInfo.tx, Params().GetConsensus(), hashBlock)) {
         if (!hashBlock.IsNull()) {
             {
                 LOCK(cs_main);
-                ixlockInfo.pindexMined = mapBlockIndex.at(hashBlock);
+                islockInfo.pindexMined = mapBlockIndex.at(hashBlock);
             }
 
-            // Let's see if the TX that was locked by this ixlock is already mined in a ChainLocked block. If yes,
-            // we can simply ignore the ixlock, as the ChainLock implies locking of all TXs in that chain
-            if (llmq::chainLocksHandler->HasChainLock(ixlockInfo.pindexMined->nHeight, ixlockInfo.pindexMined->GetBlockHash())) {
-                LogPrint("instantsend", "CInstantSendManager::%s -- txlock=%s, ixlock=%s: dropping ixlock as it already got a ChainLock in block %s, peer=%d\n", __func__,
-                         ixlock.txid.ToString(), hash.ToString(), hashBlock.ToString(), from);
+            // Let's see if the TX that was locked by this islock is already mined in a ChainLocked block. If yes,
+            // we can simply ignore the islock, as the ChainLock implies locking of all TXs in that chain
+            if (llmq::chainLocksHandler->HasChainLock(islockInfo.pindexMined->nHeight, islockInfo.pindexMined->GetBlockHash())) {
+                LogPrint("instantsend", "CInstantSendManager::%s -- txlock=%s, islock=%s: dropping islock as it already got a ChainLock in block %s, peer=%d\n", __func__,
+                         islock.txid.ToString(), hash.ToString(), hashBlock.ToString(), from);
                 return;
             }
         }
@@ -541,45 +541,45 @@ void CInstantSendManager::ProcessInstantXLock(NodeId from, const uint256& hash,
 
     {
         LOCK(cs);
-        auto e = finalInstantXLocks.emplace(hash, ixlockInfo);
+        auto e = finalInstantSendLocks.emplace(hash, islockInfo);
         if (!e.second) {
             return;
         }
-        auto ixlockInfoPtr = &e.first->second;
+        auto islockInfoPtr = &e.first->second;
 
-        creatingInstantXLocks.erase(ixlockInfoPtr->ixlock.GetRequestId());
-        txToCreatingInstantXLocks.erase(ixlockInfoPtr->ixlock.txid);
+        creatingInstantSendLocks.erase(islockInfoPtr->islock.GetRequestId());
+        txToCreatingInstantSendLocks.erase(islockInfoPtr->islock.txid);
 
-        LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, ixlock=%s: processsing ixlock, peer=%d\n", __func__,
-                 ixlock.txid.ToString(), hash.ToString(), from);
+        LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, islock=%s: processsing islock, peer=%d\n", __func__,
+                 islock.txid.ToString(), hash.ToString(), from);
 
-        if (!txToInstantXLock.emplace(ixlock.txid, ixlockInfoPtr).second) {
-            LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, ixlock=%s: duplicate ixlock, other ixlock=%s, peer=%d\n", __func__,
-                    ixlock.txid.ToString(), hash.ToString(), txToInstantXLock[ixlock.txid]->ixlockHash.ToString(), from);
-            txToInstantXLock.erase(hash);
+        if (!txToInstantSendLock.emplace(islock.txid, islockInfoPtr).second) {
+            LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, islock=%s: duplicate islock, other islock=%s, peer=%d\n", __func__,
+                    islock.txid.ToString(), hash.ToString(), txToInstantSendLock[islock.txid]->islockHash.ToString(), from);
+            txToInstantSendLock.erase(hash);
             return;
         }
-        for (size_t i = 0; i < ixlock.inputs.size(); i++) {
-            auto& in = ixlock.inputs[i];
-            if (!inputToInstantXLock.emplace(in, ixlockInfoPtr).second) {
-                LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, ixlock=%s: conflicting input in ixlock. input=%s, other ixlock=%s, peer=%d\n", __func__,
-                         ixlock.txid.ToString(), hash.ToString(), in.ToStringShort(), inputToInstantXLock[in]->ixlockHash.ToString(), from);
-                txToInstantXLock.erase(hash);
+        for (size_t i = 0; i < islock.inputs.size(); i++) {
+            auto& in = islock.inputs[i];
+            if (!inputToInstantSendLock.emplace(in, islockInfoPtr).second) {
+                LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, islock=%s: conflicting input in islock. input=%s, other islock=%s, peer=%d\n", __func__,
+                         islock.txid.ToString(), hash.ToString(), in.ToStringShort(), inputToInstantSendLock[in]->islockHash.ToString(), from);
+                txToInstantSendLock.erase(hash);
                 for (size_t j = 0; j < i; j++) {
-                    inputToInstantXLock.erase(ixlock.inputs[j]);
+                    inputToInstantSendLock.erase(islock.inputs[j]);
                 }
                 return;
             }
         }
     }
 
-    CInv inv(MSG_IXLOCK, hash);
+    CInv inv(MSG_ISLOCK, hash);
     g_connman->RelayInv(inv);
 
-    RemoveMempoolConflictsForLock(hash, ixlock);
-    RetryLockMempoolTxs(ixlock.txid);
+    RemoveMempoolConflictsForLock(hash, islock);
+    RetryLockMempoolTxs(islock.txid);
 
-    UpdateWalletTransaction(ixlock.txid);
+    UpdateWalletTransaction(islock.txid);
 }
 
 void CInstantSendManager::UpdateWalletTransaction(const uint256& txid)
@@ -602,8 +602,8 @@ void CInstantSendManager::UpdateWalletTransaction(const uint256& txid)
 #endif
 
     LOCK(cs);
-    auto it = txToInstantXLock.find(txid);
-    if (it == txToInstantXLock.end()) {
+    auto it = txToInstantSendLock.find(txid);
+    if (it == txToInstantSendLock.end()) {
         return;
     }
     if (it->second->tx == nullptr) {
@@ -621,20 +621,20 @@ void CInstantSendManager::SyncTransaction(const CTransaction& tx, const CBlockIn
 
     {
         LOCK(cs);
-        auto it = txToInstantXLock.find(tx.GetHash());
-        if (it == txToInstantXLock.end()) {
+        auto it = txToInstantSendLock.find(tx.GetHash());
+        if (it == txToInstantSendLock.end()) {
             return;
         }
-        auto ixlockInfo = it->second;
-        if (ixlockInfo->tx == nullptr) {
-            ixlockInfo->tx = MakeTransactionRef(tx);
+        auto islockInfo = it->second;
+        if (islockInfo->tx == nullptr) {
+            islockInfo->tx = MakeTransactionRef(tx);
         }
 
         if (posInBlock == CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK) {
-            UpdateIxLockMinedBlock(ixlockInfo, nullptr);
+            UpdateISLockMinedBlock(islockInfo, nullptr);
             return;
         }
-        UpdateIxLockMinedBlock(ixlockInfo, pindex);
+        UpdateISLockMinedBlock(islockInfo, pindex);
     }
 
     if (IsLocked(tx.GetHash())) {
@@ -647,17 +647,17 @@ void CInstantSendManager::NotifyChainLock(const CBlockIndex* pindex)
     {
         LOCK(cs);
 
-        // Let's find all ixlocks that correspond to TXs which are part of the freshly ChainLocked chain and then delete
-        // the ixlocks. We do this because the ChainLocks imply locking and thus it's not needed to further track
-        // or propagate the ixlocks
+        // Let's find all islocks that correspond to TXs which are part of the freshly ChainLocked chain and then delete
+        // the islocks. We do this because the ChainLocks imply locking and thus it's not needed to further track
+        // or propagate the islocks
         std::unordered_set<uint256> toDelete;
         while (pindex && pindex != pindexLastChainLock) {
-            auto its = blockToInstantXLocks.equal_range(pindex->GetBlockHash());
+            auto its = blockToInstantSendLocks.equal_range(pindex->GetBlockHash());
             while (its.first != its.second) {
-                auto ixlockInfo = its.first->second;
-                LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, ixlock=%s: removing ixlock as it got ChainLocked in block %s\n", __func__,
-                         ixlockInfo->ixlock.txid.ToString(), ixlockInfo->ixlockHash.ToString(), pindex->GetBlockHash().ToString());
-                toDelete.emplace(its.first->second->ixlockHash);
+                auto islockInfo = its.first->second;
+                LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, islock=%s: removing islock as it got ChainLocked in block %s\n", __func__,
+                         islockInfo->islock.txid.ToString(), islockInfo->islockHash.ToString(), pindex->GetBlockHash().ToString());
+                toDelete.emplace(its.first->second->islockHash);
                 ++its.first;
             }
 
@@ -666,27 +666,27 @@ void CInstantSendManager::NotifyChainLock(const CBlockIndex* pindex)
 
         pindexLastChainLock = pindex;
 
-        for (auto& ixlockHash : toDelete) {
-            RemoveFinalIxLock(ixlockHash);
+        for (auto& islockHash : toDelete) {
+            RemoveFinalISLock(islockHash);
         }
     }
 
     RetryLockMempoolTxs(uint256());
 }
 
-void CInstantSendManager::UpdateIxLockMinedBlock(llmq::CInstantXLockInfo* ixlockInfo, const CBlockIndex* pindex)
+void CInstantSendManager::UpdateISLockMinedBlock(llmq::CInstantSendLockInfo* islockInfo, const CBlockIndex* pindex)
 {
     AssertLockHeld(cs);
     
-    if (ixlockInfo->pindexMined == pindex) {
+    if (islockInfo->pindexMined == pindex) {
         return;
     }
     
-    if (ixlockInfo->pindexMined) {
-        auto its = blockToInstantXLocks.equal_range(ixlockInfo->pindexMined->GetBlockHash());
+    if (islockInfo->pindexMined) {
+        auto its = blockToInstantSendLocks.equal_range(islockInfo->pindexMined->GetBlockHash());
         while (its.first != its.second) {
-            if (its.first->second == ixlockInfo) {
-                its.first = blockToInstantXLocks.erase(its.first);
+            if (its.first->second == islockInfo) {
+                its.first = blockToInstantSendLocks.erase(its.first);
             } else {
                 ++its.first;
             }
@@ -694,47 +694,47 @@ void CInstantSendManager::UpdateIxLockMinedBlock(llmq::CInstantXLockInfo* ixlock
     }
     
     if (pindex) {
-        blockToInstantXLocks.emplace(pindex->GetBlockHash(), ixlockInfo);
+        blockToInstantSendLocks.emplace(pindex->GetBlockHash(), islockInfo);
     }
     
-    ixlockInfo->pindexMined = pindex;
+    islockInfo->pindexMined = pindex;
 }
 
-void CInstantSendManager::RemoveFinalIxLock(const uint256& hash)
+void CInstantSendManager::RemoveFinalISLock(const uint256& hash)
 {
     AssertLockHeld(cs);
     
-    auto it = finalInstantXLocks.find(hash);
-    if (it == finalInstantXLocks.end()) {
+    auto it = finalInstantSendLocks.find(hash);
+    if (it == finalInstantSendLocks.end()) {
         return;
     }
-    auto& ixlockInfo = it->second;
+    auto& islockInfo = it->second;
 
-    txToInstantXLock.erase(ixlockInfo.ixlock.txid);
-    for (auto& in : ixlockInfo.ixlock.inputs) {
+    txToInstantSendLock.erase(islockInfo.islock.txid);
+    for (auto& in : islockInfo.islock.inputs) {
         auto inputRequestId = ::SerializeHash(std::make_pair(INPUTLOCK_REQUESTID_PREFIX, in));
         inputVotes.erase(inputRequestId);
-        inputToInstantXLock.erase(in);
+        inputToInstantSendLock.erase(in);
     }
-    UpdateIxLockMinedBlock(&ixlockInfo, nullptr);
+    UpdateISLockMinedBlock(&islockInfo, nullptr);
 }
 
-void CInstantSendManager::RemoveMempoolConflictsForLock(const uint256& hash, const CInstantXLock& ixlock)
+void CInstantSendManager::RemoveMempoolConflictsForLock(const uint256& hash, const CInstantSendLock& islock)
 {
     LOCK(mempool.cs);
 
     std::unordered_map<uint256, CTransactionRef> toDelete;
 
-    for (auto& in : ixlock.inputs) {
+    for (auto& in : islock.inputs) {
         auto it = mempool.mapNextTx.find(in);
         if (it == mempool.mapNextTx.end()) {
             continue;
         }
-        if (it->second->GetHash() != ixlock.txid) {
+        if (it->second->GetHash() != islock.txid) {
             toDelete.emplace(it->second->GetHash(), mempool.get(it->second->GetHash()));
 
-            LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, ixlock=%s: mempool TX %s with input %s conflicts with ixlock\n", __func__,
-                     ixlock.txid.ToString(), hash.ToString(), it->second->GetHash().ToString(), in.ToStringShort());
+            LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, islock=%s: mempool TX %s with input %s conflicts with islock\n", __func__,
+                     islock.txid.ToString(), hash.ToString(), it->second->GetHash().ToString(), in.ToStringShort());
         }
     }
 
@@ -745,7 +745,7 @@ void CInstantSendManager::RemoveMempoolConflictsForLock(const uint256& hash, con
 
 void CInstantSendManager::RetryLockMempoolTxs(const uint256& lockedParentTx)
 {
-    // Let's retry all mempool TXs which don't have an ixlock yet and where the parents got ChainLocked now
+    // Let's retry all mempool TXs which don't have an islock yet and where the parents got ChainLocked now
 
     std::unordered_map<uint256, CTransactionRef> txs;
 
@@ -769,7 +769,7 @@ void CInstantSendManager::RetryLockMempoolTxs(const uint256& lockedParentTx)
         auto& tx = p.second;
         {
             LOCK(cs);
-            if (txToCreatingInstantXLocks.count(tx->GetHash())) {
+            if (txToCreatingInstantSendLocks.count(tx->GetHash())) {
                 // we're already in the middle of locking this one
                 continue;
             }
@@ -803,21 +803,21 @@ bool CInstantSendManager::AlreadyHave(const CInv& inv)
     }
 
     LOCK(cs);
-    return finalInstantXLocks.count(inv.hash) != 0 || pendingInstantXLocks.count(inv.hash) != 0;
+    return finalInstantSendLocks.count(inv.hash) != 0 || pendingInstantSendLocks.count(inv.hash) != 0;
 }
 
-bool CInstantSendManager::GetInstantXLockByHash(const uint256& hash, llmq::CInstantXLock& ret)
+bool CInstantSendManager::GetInstantSendLockByHash(const uint256& hash, llmq::CInstantSendLock& ret)
 {
     if (!IsNewInstantSendEnabled()) {
         return false;
     }
 
     LOCK(cs);
-    auto it = finalInstantXLocks.find(hash);
-    if (it == finalInstantXLocks.end()) {
+    auto it = finalInstantSendLocks.find(hash);
+    if (it == finalInstantSendLocks.end()) {
         return false;
     }
-    ret = it->second.ixlock;
+    ret = it->second.islock;
     return true;
 }
 
@@ -828,7 +828,7 @@ bool CInstantSendManager::IsLocked(const uint256& txHash)
     }
 
     LOCK(cs);
-    return txToInstantXLock.count(txHash) != 0;
+    return txToInstantSendLock.count(txHash) != 0;
 }
 
 bool CInstantSendManager::IsConflicted(const CTransaction& tx)
@@ -846,13 +846,13 @@ bool CInstantSendManager::GetConflictingTx(const CTransaction& tx, uint256& retC
 
     LOCK(cs);
     for (const auto& in : tx.vin) {
-        auto it = inputToInstantXLock.find(in.prevout);
-        if (it == inputToInstantXLock.end()) {
+        auto it = inputToInstantSendLock.find(in.prevout);
+        if (it == inputToInstantSendLock.end()) {
             continue;
         }
 
-        if (it->second->ixlock.txid != tx.GetHash()) {
-            retConflictTxHash = it->second->ixlock.txid;
+        if (it->second->islock.txid != tx.GetHash()) {
+            retConflictTxHash = it->second->islock.txid;
             return true;
         }
     }
diff --git a/src/llmq/quorums_instantsend.h b/src/llmq/quorums_instantsend.h
index 66a6a1cad377..3d114970efe0 100644
--- a/src/llmq/quorums_instantsend.h
+++ b/src/llmq/quorums_instantsend.h
@@ -2,8 +2,8 @@
 // Distributed under the MIT software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
-#ifndef DASH_QUORUMS_INSTANTX_H
-#define DASH_QUORUMS_INSTANTX_H
+#ifndef DASH_QUORUMS_INSTANTSEND_H
+#define DASH_QUORUMS_INSTANTSEND_H
 
 #include "quorums_signing.h"
 
@@ -18,7 +18,7 @@ class CScheduler;
 namespace llmq
 {
 
-class CInstantXLock
+class CInstantSendLock
 {
 public:
     std::vector<COutPoint> inputs;
@@ -39,14 +39,14 @@ class CInstantXLock
     uint256 GetRequestId() const;
 };
 
-class CInstantXLockInfo
+class CInstantSendLockInfo
 {
 public:
-    // might be nullptr when ixlock is received before the TX itself
+    // might be nullptr when islock is received before the TX itself
     CTransactionRef tx;
-    CInstantXLock ixlock;
+    CInstantSendLock islock;
     // only valid when recovered sig was received
-    uint256 ixlockHash;
+    uint256 islockHash;
     // time when it was created/received
     int64_t time;
 
@@ -68,27 +68,27 @@ class CInstantSendManager : public CRecoveredSigsListener
     std::unordered_map<uint256, uint256, StaticSaltedHasher> inputVotes;
 
     /**
-     * These are the ixlocks that are currently in the middle of being created. Entries are created when we observed
-     * recovered signatures for all inputs of a TX. At the same time, we initiate signing of our sigshare for the ixlock.
-     * When the recovered sig for the ixlock later arrives, we can finish the ixlock and propagate it.
+     * These are the islocks that are currently in the middle of being created. Entries are created when we observed
+     * recovered signatures for all inputs of a TX. At the same time, we initiate signing of our sigshare for the islock.
+     * When the recovered sig for the islock later arrives, we can finish the islock and propagate it.
      */
-    std::unordered_map<uint256, CInstantXLockInfo, StaticSaltedHasher> creatingInstantXLocks;
-    // maps from txid to the in-progress ixlock
-    std::unordered_map<uint256, CInstantXLockInfo*, StaticSaltedHasher> txToCreatingInstantXLocks;
+    std::unordered_map<uint256, CInstantSendLockInfo, StaticSaltedHasher> creatingInstantSendLocks;
+    // maps from txid to the in-progress islock
+    std::unordered_map<uint256, CInstantSendLockInfo*, StaticSaltedHasher> txToCreatingInstantSendLocks;
 
     /**
-     * These are the final ixlocks, indexed by their own hash. The other maps are used to get from TXs, inputs and blocks
-     * to ixlocks.
+     * These are the final islocks, indexed by their own hash. The other maps are used to get from TXs, inputs and blocks
+     * to islocks.
      */
-    std::unordered_map<uint256, CInstantXLockInfo, StaticSaltedHasher> finalInstantXLocks;
-    std::unordered_map<uint256, CInstantXLockInfo*, StaticSaltedHasher> txToInstantXLock;
-    std::unordered_map<COutPoint, CInstantXLockInfo*, SaltedOutpointHasher> inputToInstantXLock;
-    std::unordered_multimap<uint256, CInstantXLockInfo*, StaticSaltedHasher> blockToInstantXLocks;
+    std::unordered_map<uint256, CInstantSendLockInfo, StaticSaltedHasher> finalInstantSendLocks;
+    std::unordered_map<uint256, CInstantSendLockInfo*, StaticSaltedHasher> txToInstantSendLock;
+    std::unordered_map<COutPoint, CInstantSendLockInfo*, SaltedOutpointHasher> inputToInstantSendLock;
+    std::unordered_multimap<uint256, CInstantSendLockInfo*, StaticSaltedHasher> blockToInstantSendLocks;
 
     const CBlockIndex* pindexLastChainLock{nullptr};
 
     // Incoming and not verified yet
-    std::unordered_map<uint256, std::pair<NodeId, CInstantXLock>> pendingInstantXLocks;
+    std::unordered_map<uint256, std::pair<NodeId, CInstantSendLock>> pendingInstantSendLocks;
     bool hasScheduledProcessPending{false};
 
 public:
@@ -108,27 +108,27 @@ class CInstantSendManager : public CRecoveredSigsListener
 
     virtual void HandleNewRecoveredSig(const CRecoveredSig& recoveredSig);
     void HandleNewInputLockRecoveredSig(const CRecoveredSig& recoveredSig, const uint256& txid);
-    void HandleNewInstantXLockRecoveredSig(const CRecoveredSig& recoveredSig);
+    void HandleNewInstantSendLockRecoveredSig(const CRecoveredSig& recoveredSig);
 
-    void TrySignInstantXLock(const CTransaction& tx);
+    void TrySignInstantSendLock(const CTransaction& tx);
 
     void ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman);
-    void ProcessMessageInstantXLock(CNode* pfrom, const CInstantXLock& ixlock, CConnman& connman);
-    bool PreVerifyInstantXLock(NodeId nodeId, const CInstantXLock& ixlock, bool& retBan);
-    void ProcessPendingInstantXLocks();
-    void ProcessInstantXLock(NodeId from, const uint256& hash, const CInstantXLock& ixlock);
+    void ProcessMessageInstantSendLock(CNode* pfrom, const CInstantSendLock& islock, CConnman& connman);
+    bool PreVerifyInstantSendLock(NodeId nodeId, const CInstantSendLock& islock, bool& retBan);
+    void ProcessPendingInstantSendLocks();
+    void ProcessInstantSendLock(NodeId from, const uint256& hash, const CInstantSendLock& islock);
     void UpdateWalletTransaction(const uint256& txid);
 
     void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock);
     void NotifyChainLock(const CBlockIndex* pindex);
-    void UpdateIxLockMinedBlock(CInstantXLockInfo* ixlockInfo, const CBlockIndex* pindex);
-    void RemoveFinalIxLock(const uint256& hash);
+    void UpdateISLockMinedBlock(CInstantSendLockInfo* islockInfo, const CBlockIndex* pindex);
+    void RemoveFinalISLock(const uint256& hash);
 
-    void RemoveMempoolConflictsForLock(const uint256& hash, const CInstantXLock& ixlock);
+    void RemoveMempoolConflictsForLock(const uint256& hash, const CInstantSendLock& islock);
     void RetryLockMempoolTxs(const uint256& lockedParentTx);
 
     bool AlreadyHave(const CInv& inv);
-    bool GetInstantXLockByHash(const uint256& hash, CInstantXLock& ret);
+    bool GetInstantSendLockByHash(const uint256& hash, CInstantSendLock& ret);
 };
 
 extern CInstantSendManager* quorumInstantSendManager;
@@ -144,4 +144,4 @@ bool IsInstantSendEnabled();
 
 }
 
-#endif//DASH_QUORUMS_INSTANTX_H
+#endif//DASH_QUORUMS_INSTANTSEND_H
diff --git a/src/llmq/quorums_signing.cpp b/src/llmq/quorums_signing.cpp
index fc394533c49c..8941c4504a4d 100644
--- a/src/llmq/quorums_signing.cpp
+++ b/src/llmq/quorums_signing.cpp
@@ -543,7 +543,7 @@ void CSigningManager::ProcessRecoveredSig(NodeId nodeId, const CRecoveredSig& re
                 } else {
                     // Looks like we're trying to process a recSig that is already known. This might happen if the same
                     // recSig comes in through regular QRECSIG messages and at the same time through some other message
-                    // which allowed to reconstruct a recSig (e.g. IXLOCK). In this case, just bail out.
+                    // which allowed to reconstruct a recSig (e.g. ISLOCK). In this case, just bail out.
                 }
                 return;
             } else {
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 1123e9c48e1e..8df501141116 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -977,7 +977,7 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
         return llmq::quorumSigningManager->AlreadyHave(inv);
     case MSG_CLSIG:
         return llmq::chainLocksHandler->AlreadyHave(inv);
-    case MSG_IXLOCK:
+    case MSG_ISLOCK:
         return llmq::quorumInstantSendManager->AlreadyHave(inv);
     }
 
@@ -1299,10 +1299,10 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
                     }
                 }
 
-                if (!push && (inv.type == MSG_IXLOCK)) {
-                    llmq::CInstantXLock o;
-                    if (llmq::quorumInstantSendManager->GetInstantXLockByHash(inv.hash, o)) {
-                        connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::IXLOCK, o));
+                if (!push && (inv.type == MSG_ISLOCK)) {
+                    llmq::CInstantSendLock o;
+                    if (llmq::quorumInstantSendManager->GetInstantSendLockByHash(inv.hash, o)) {
+                        connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::ISLOCK, o));
                         push = true;
                     }
                 }
@@ -1788,7 +1788,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
                             case MSG_CLSIG:
                                 doubleRequestDelay = 5 * 1000000;
                                 break;
-                            case MSG_IXLOCK:
+                            case MSG_ISLOCK:
                                 doubleRequestDelay = 5 * 1000000;
                                 break;
                         }
diff --git a/src/protocol.cpp b/src/protocol.cpp
index e63688c0a00c..48407d640ef9 100644
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -71,7 +71,7 @@ const char *QGETSIGSHARES="qgetsigs";
 const char *QBSIGSHARES="qbsigs";
 const char *QSIGREC="qsigrec";
 const char *CLSIG="clsig";
-const char *IXLOCK="ixlock";
+const char *ISLOCK="islock";
 };
 
 static const char* ppszTypeName[] =
@@ -108,7 +108,7 @@ static const char* ppszTypeName[] =
     NetMsgType::QDEBUGSTATUS,
     NetMsgType::QSIGREC,
     NetMsgType::CLSIG,
-    NetMsgType::IXLOCK,
+    NetMsgType::ISLOCK,
 };
 
 /** All known message types. Keep this in the same order as the list of
@@ -174,7 +174,7 @@ const static std::string allNetMessageTypes[] = {
     NetMsgType::QBSIGSHARES,
     NetMsgType::QSIGREC,
     NetMsgType::CLSIG,
-    NetMsgType::IXLOCK,
+    NetMsgType::ISLOCK,
 };
 const static std::vector<std::string> allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes));
 
diff --git a/src/protocol.h b/src/protocol.h
index 0376f86f26ff..c2027b3e336e 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -277,7 +277,7 @@ extern const char *QGETSIGSHARES;
 extern const char *QBSIGSHARES;
 extern const char *QSIGREC;
 extern const char *CLSIG;
-extern const char *IXLOCK;
+extern const char *ISLOCK;
 };
 
 /* Get a vector of all valid message types (see above) */
@@ -380,7 +380,7 @@ enum GetDataMsg {
     MSG_QUORUM_DEBUG_STATUS = 27,
     MSG_QUORUM_RECOVERED_SIG = 28,
     MSG_CLSIG = 29,
-    MSG_IXLOCK = 30,
+    MSG_ISLOCK = 30,
 };
 
 /** inv message data */
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 22138bde0f49..b91a8f707102 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -3346,7 +3346,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
                                 int& nChangePosInOut, std::string& strFailReason, const CCoinControl* coinControl, bool sign, AvailableCoinsType nCoinType, bool fUseInstantSend, int nExtraPayloadSize)
 {
     if (!llmq::IsOldInstantSendEnabled()) {
-        // The new system does not require special handling for InstantSend as this is all done in CInstantXManager.
+        // The new system does not require special handling for InstantSend as this is all done in CInstantSendManager.
         // There is also no need for an extra fee anymore.
         fUseInstantSend = false;
     }

From 55152cb3135a84a917968ef04fe280828ce50ade Mon Sep 17 00:00:00 2001
From: UdjinM6 <UdjinM6@users.noreply.github.com>
Date: Sun, 3 Mar 2019 11:48:52 +0300
Subject: [PATCH 24/33] Move IS block filtering into ConnectBlock

---
 src/validation.cpp | 76 ++++++++++++++++++++++------------------------
 1 file changed, 37 insertions(+), 39 deletions(-)

diff --git a/src/validation.cpp b/src/validation.cpp
index 5142d8e616a3..ea042a42ddbf 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -2209,13 +2209,49 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
     LogPrint("bench", "    - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs]\n", nInputs - 1, 0.001 * (nTime4 - nTime2), nInputs <= 1 ? 0 : 0.001 * (nTime4 - nTime2) / (nInputs-1), nTimeVerify * 0.000001);
 
 
-    // DASH : MODIFIED TO CHECK MASTERNODE PAYMENTS AND SUPERBLOCKS
+    // DASH
 
     // It's possible that we simply don't have enough data and this could fail
     // (i.e. block itself could be a correct one and we need to store it),
     // that's why this is in ConnectBlock. Could be the other way around however -
     // the peer who sent us this block is missing some data and wasn't able
     // to recognize that block is actually invalid.
+
+    // DASH : CHECK TRANSACTIONS FOR INSTANTSEND
+
+    if (sporkManager.IsSporkActive(SPORK_3_INSTANTSEND_BLOCK_FILTERING)) {
+        // Require other nodes to comply, send them some data in case they are missing it.
+        for (const auto& tx : block.vtx) {
+            // skip txes that have no inputs
+            if (tx->vin.empty()) continue;
+            // LOOK FOR TRANSACTION LOCK IN OUR MAP OF OUTPOINTS
+            for (const auto& txin : tx->vin) {
+                uint256 hashLocked;
+                if (instantsend.GetLockedOutPointTxHash(txin.prevout, hashLocked) && hashLocked != tx->GetHash()) {
+                    // The node which relayed this should switch to correct chain.
+                    // TODO: relay instantsend data/proof.
+                    LOCK(cs_main);
+                    mapRejectedBlocks.insert(std::make_pair(block.GetHash(), GetTime()));
+                    return state.DoS(10, error("ConnectBlock(DASH): transaction %s conflicts with transaction lock %s", tx->GetHash().ToString(), hashLocked.ToString()),
+                                     REJECT_INVALID, "conflict-tx-lock");
+                }
+            }
+            uint256 txConflict;
+            if (llmq::quorumInstantSendManager->GetConflictingTx(*tx, txConflict)) {
+                // The node which relayed this should switch to correct chain.
+                // TODO: relay instantsend data/proof.
+                LOCK(cs_main);
+                mapRejectedBlocks.insert(std::make_pair(block.GetHash(), GetTime()));
+                return state.DoS(10, error("ConnectBlock(DASH): transaction %s conflicts with transaction lock %s", tx->GetHash().ToString(), txConflict.ToString()),
+                                 REJECT_INVALID, "conflict-tx-lock");
+            }
+        }
+    } else {
+        LogPrintf("ConnectBlock(DASH): spork is off, skipping transaction locking checks\n");
+    }
+
+    // DASH : MODIFIED TO CHECK MASTERNODE PAYMENTS AND SUPERBLOCKS
+
     // TODO: resync data (both ways?) and try to reprocess this block later.
     CAmount blockReward = nFees + GetBlockSubsidy(pindex->pprev->nBits, pindex->pprev->nHeight, chainparams.GetConsensus());
     std::string strError = "";
@@ -3269,44 +3305,6 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
         if (block.vtx[i]->IsCoinBase())
             return state.DoS(100, false, REJECT_INVALID, "bad-cb-multiple", false, "more than one coinbase");
 
-
-    // DASH : CHECK TRANSACTIONS FOR INSTANTSEND
-
-    if(sporkManager.IsSporkActive(SPORK_3_INSTANTSEND_BLOCK_FILTERING)) {
-        // We should never accept block which conflicts with completed transaction lock,
-        // that's why this is in CheckBlock unlike coinbase payee/amount.
-        // Require other nodes to comply, send them some data in case they are missing it.
-        for(const auto& tx : block.vtx) {
-            // skip coinbase, it has no inputs
-            if (tx->IsCoinBase()) continue;
-            // LOOK FOR TRANSACTION LOCK IN OUR MAP OF OUTPOINTS
-            for (const auto& txin : tx->vin) {
-                uint256 hashLocked;
-                if(instantsend.GetLockedOutPointTxHash(txin.prevout, hashLocked) && hashLocked != tx->GetHash()) {
-                    // The node which relayed this will have to switch later,
-                    // relaying instantsend data won't help it.
-                    LOCK(cs_main);
-                    mapRejectedBlocks.insert(std::make_pair(block.GetHash(), GetTime()));
-                    return state.DoS(100, false, REJECT_INVALID, "conflict-tx-lock", false, 
-                                     strprintf("transaction %s conflicts with transaction lock %s", tx->GetHash().ToString(), hashLocked.ToString()));
-                }
-            }
-            uint256 txConflict;
-            if (llmq::quorumInstantSendManager->GetConflictingTx(*tx, txConflict)) {
-                // The node which relayed this will have to switch later,
-                // relaying instantsend data won't help it.
-                LOCK(cs_main);
-                mapRejectedBlocks.insert(std::make_pair(block.GetHash(), GetTime()));
-                return state.DoS(100, false, REJECT_INVALID, "conflict-tx-lock", false,
-                                 strprintf("transaction %s conflicts with transaction lock %s", tx->GetHash().ToString(), txConflict.ToString()));
-            }
-        }
-    } else {
-        LogPrintf("CheckBlock(DASH): spork is off, skipping transaction locking checks\n");
-    }
-
-    // END DASH
-
     // Check transactions
     for (const auto& tx : block.vtx)
         if (!CheckTransaction(*tx, state))

From a8da11ac5dee4d7bc5b85c7a242bacad9d391d41 Mon Sep 17 00:00:00 2001
From: UdjinM6 <UdjinM6@users.noreply.github.com>
Date: Sun, 3 Mar 2019 10:31:23 +0300
Subject: [PATCH 25/33] update p2p-instantsend.py to test both "old" and "new"
 InstantSend

---
 qa/rpc-tests/p2p-instantsend.py | 21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/qa/rpc-tests/p2p-instantsend.py b/qa/rpc-tests/p2p-instantsend.py
index 9a1b15b6b05c..45ecfd3b2abf 100755
--- a/qa/rpc-tests/p2p-instantsend.py
+++ b/qa/rpc-tests/p2p-instantsend.py
@@ -21,6 +21,20 @@ def __init__(self):
         self.sender_idx = self.num_nodes - 3
 
     def run_test(self):
+        self.nodes[0].spork("SPORK_17_QUORUM_DKG_ENABLED", 0)
+        self.wait_for_sporks_same()
+        self.mine_quorum()
+
+        print("Test old InstantSend")
+        self.test_doublespend()
+
+        self.nodes[0].spork("SPORK_2_INSTANTSEND_ENABLED", 1)
+        self.wait_for_sporks_same()
+
+        print("Test new InstantSend")
+        self.test_doublespend()
+
+    def test_doublespend(self):
         # feed the sender with some balance
         sender_addr = self.nodes[self.sender_idx].getnewaddress()
         self.nodes[0].sendtoaddress(sender_addr, 1)
@@ -73,7 +87,12 @@ def run_test(self):
             assert (res['hash'] != wrong_block)
             # wait for long time only for first node
             timeout = 1
-
+        # mine more blocks
+        # TODO: mine these blocks on an isolated node
+        set_mocktime(get_mocktime() + 1)
+        set_node_times(self.nodes, get_mocktime())
+        self.nodes[0].generate(2)
+        self.sync_all()
 
 if __name__ == '__main__':
     InstantSendTest().main()

From 843b6d7a952f3602746e434af68f36bf7c94501f Mon Sep 17 00:00:00 2001
From: UdjinM6 <UdjinM6@users.noreply.github.com>
Date: Sun, 3 Mar 2019 13:30:34 +0300
Subject: [PATCH 26/33] update p2p-autoinstantsend.py to test both "old" and
 "new" InstantSend

---
 qa/rpc-tests/p2p-autoinstantsend.py | 42 ++++++++++++++++++++---------
 1 file changed, 29 insertions(+), 13 deletions(-)

diff --git a/qa/rpc-tests/p2p-autoinstantsend.py b/qa/rpc-tests/p2p-autoinstantsend.py
index b19eb07a4fe8..31f97ce50f6c 100755
--- a/qa/rpc-tests/p2p-autoinstantsend.py
+++ b/qa/rpc-tests/p2p-autoinstantsend.py
@@ -89,13 +89,14 @@ def set_autoix_spork_state(self, state):
         self.nodes[0].spork('SPORK_16_INSTANTSEND_AUTOLOCKS', value)
 
     # sends regular IX with high fee and may inputs (not-simple transaction)
-    def send_regular_IX(self):
+    def send_regular_IX(self, check_fee = True):
         receiver_addr = self.nodes[self.receiver_idx].getnewaddress()
         txid = self.nodes[0].instantsendtoaddress(receiver_addr, 1.0)
-        MIN_FEE = satoshi_round(-0.0001)
-        fee = self.nodes[0].gettransaction(txid)['fee']
-        expected_fee = MIN_FEE * len(self.nodes[0].getrawtransaction(txid, True)['vin'])
-        assert_equal(fee, expected_fee)
+        if (check_fee):
+            MIN_FEE = satoshi_round(-0.0001)
+            fee = self.nodes[0].gettransaction(txid)['fee']
+            expected_fee = MIN_FEE * len(self.nodes[0].getrawtransaction(txid, True)['vin'])
+            assert_equal(fee, expected_fee)
         return self.wait_for_instantlock(txid, self.nodes[0])
 
     # sends simple trx, it should become IX if autolocks are allowed
@@ -115,6 +116,21 @@ def send_complex_tx(self):
     def run_test(self):
         # make sure masternodes are synced
         sync_masternodes(self.nodes)
+
+        self.nodes[0].spork("SPORK_17_QUORUM_DKG_ENABLED", 0)
+        self.wait_for_sporks_same()
+        self.mine_quorum()
+
+        print("Test old InstantSend")
+        self.test_auto();
+
+        self.nodes[0].spork("SPORK_2_INSTANTSEND_ENABLED", 1)
+        self.wait_for_sporks_same()
+
+        print("Test new InstantSend")
+        self.test_auto(True);
+
+    def test_auto(self, new_is = False):
         # feed the sender with some balance
         sender_addr = self.nodes[self.sender_idx].getnewaddress()
         self.nodes[0].sendtoaddress(sender_addr, 1)
@@ -126,9 +142,9 @@ def run_test(self):
             
         assert(not self.get_autoix_spork_state())
 
-        assert(self.send_regular_IX())
-        assert(not self.send_simple_tx())
-        assert(not self.send_complex_tx())
+        assert(self.send_regular_IX(not new_is))
+        assert(self.send_simple_tx() if new_is else not self.send_simple_tx())
+        assert(self.send_complex_tx() if new_is else not self.send_complex_tx())
 
         self.activate_autoix_bip9()
         self.set_autoix_spork_state(True)
@@ -136,16 +152,16 @@ def run_test(self):
         assert(self.get_autoix_bip9_status() == 'active')
         assert(self.get_autoix_spork_state())
 
-        assert(self.send_regular_IX())
+        assert(self.send_regular_IX(not new_is))
         assert(self.send_simple_tx())
-        assert(not self.send_complex_tx())
+        assert(self.send_complex_tx() if new_is else not self.send_complex_tx())
 
         self.set_autoix_spork_state(False)
         assert(not self.get_autoix_spork_state())
 
-        assert(self.send_regular_IX())
-        assert(not self.send_simple_tx())
-        assert(not self.send_complex_tx())
+        assert(self.send_regular_IX(not new_is))
+        assert(self.send_simple_tx() if new_is else not self.send_simple_tx())
+        assert(self.send_complex_tx() if new_is else not self.send_complex_tx())
 
 
 if __name__ == '__main__':

From 41a71fe4430078b81d1b7e2fc01d71470bd7f07f Mon Sep 17 00:00:00 2001
From: UdjinM6 <UdjinM6@users.noreply.github.com>
Date: Sun, 3 Mar 2019 20:26:06 +0300
Subject: [PATCH 27/33] update autoix-mempool.py to test both "old" and "new"
 InstantSend (and fix CheckCanLock to respect mempool limits)

---
 qa/rpc-tests/autoix-mempool.py   | 19 +++++++++++++++++--
 src/instantx.h                   |  5 +++--
 src/llmq/quorums_instantsend.cpp |  4 ++++
 3 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/qa/rpc-tests/autoix-mempool.py b/qa/rpc-tests/autoix-mempool.py
index fdd3a57f3d29..f79fe772a70f 100755
--- a/qa/rpc-tests/autoix-mempool.py
+++ b/qa/rpc-tests/autoix-mempool.py
@@ -122,6 +122,21 @@ def fill_mempool(self):
     def run_test(self):
         # make sure masternodes are synced
         sync_masternodes(self.nodes)
+
+        self.nodes[0].spork("SPORK_17_QUORUM_DKG_ENABLED", 0)
+        self.wait_for_sporks_same()
+        self.mine_quorum()
+
+        print("Test old InstantSend")
+        self.test_auto();
+
+        self.nodes[0].spork("SPORK_2_INSTANTSEND_ENABLED", 1)
+        self.wait_for_sporks_same()
+
+        print("Test new InstantSend")
+        self.test_auto(True);
+
+    def test_auto(self, new_is = False):
         self.activate_autoix_bip9()
         self.set_autoix_spork_state(True)
 
@@ -151,8 +166,8 @@ def run_test(self):
 
         # autoIX is not working now
         assert(not self.send_simple_tx(sender, receiver))
-        # regular IX is still working
-        assert(self.send_regular_IX(sender, receiver))
+        # regular IX is still working for old IS but not for new one
+        assert(not self.send_regular_IX(sender, receiver) if new_is else self.send_regular_IX(sender, receiver))
 
         # generate one block to clean up mempool and retry auto and regular IX
         # generate 2 more blocks to have enough confirmations for IX
diff --git a/src/instantx.h b/src/instantx.h
index c8c0aa585429..aac54e2d1acf 100644
--- a/src/instantx.h
+++ b/src/instantx.h
@@ -45,11 +45,12 @@ extern int nCompleteTXLocks;
  */
 class CInstantSend
 {
-private:
-    static const std::string SERIALIZATION_VERSION_STRING;
+public:
     /// Automatic locks of "simple" transactions are only allowed
     /// when mempool usage is lower than this threshold
     static const double AUTO_IX_MEMPOOL_THRESHOLD;
+private:
+    static const std::string SERIALIZATION_VERSION_STRING;
 
     // Keep track of current block height
     int nCachedBlockHeight;
diff --git a/src/llmq/quorums_instantsend.cpp b/src/llmq/quorums_instantsend.cpp
index 6104f5b3281a..7f0a322ac66c 100644
--- a/src/llmq/quorums_instantsend.cpp
+++ b/src/llmq/quorums_instantsend.cpp
@@ -137,6 +137,10 @@ bool CInstantSendManager::ProcessTx(CNode* pfrom, const CTransaction& tx, CConnm
 
 bool CInstantSendManager::CheckCanLock(const CTransaction& tx, bool printDebug, const Consensus::Params& params)
 {
+    if (sporkManager.IsSporkActive(SPORK_16_INSTANTSEND_AUTOLOCKS) && (mempool.UsedMemoryShare() > CInstantSend::AUTO_IX_MEMPOOL_THRESHOLD)) {
+        return false;
+    }
+
     int nInstantSendConfirmationsRequired = params.nInstantSendConfirmationsRequired;
 
     uint256 txHash = tx.GetHash();

From 3e60d2d2dbadb30ffe0e1ca22a1ec41ad0a76a75 Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Mon, 4 Mar 2019 13:33:34 +0100
Subject: [PATCH 28/33] Adjust LLMQ based InstantSend tests for new spork20

---
 qa/rpc-tests/autoix-mempool.py      | 2 +-
 qa/rpc-tests/p2p-autoinstantsend.py | 2 +-
 qa/rpc-tests/p2p-instantsend.py     | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/qa/rpc-tests/autoix-mempool.py b/qa/rpc-tests/autoix-mempool.py
index f79fe772a70f..a8be9a321fc1 100755
--- a/qa/rpc-tests/autoix-mempool.py
+++ b/qa/rpc-tests/autoix-mempool.py
@@ -130,7 +130,7 @@ def run_test(self):
         print("Test old InstantSend")
         self.test_auto();
 
-        self.nodes[0].spork("SPORK_2_INSTANTSEND_ENABLED", 1)
+        self.nodes[0].spork("SPORK_20_INSTANTSEND_LLMQ_BASED", 0)
         self.wait_for_sporks_same()
 
         print("Test new InstantSend")
diff --git a/qa/rpc-tests/p2p-autoinstantsend.py b/qa/rpc-tests/p2p-autoinstantsend.py
index 31f97ce50f6c..bf930b921aab 100755
--- a/qa/rpc-tests/p2p-autoinstantsend.py
+++ b/qa/rpc-tests/p2p-autoinstantsend.py
@@ -124,7 +124,7 @@ def run_test(self):
         print("Test old InstantSend")
         self.test_auto();
 
-        self.nodes[0].spork("SPORK_2_INSTANTSEND_ENABLED", 1)
+        self.nodes[0].spork("SPORK_20_INSTANTSEND_LLMQ_BASED", 0)
         self.wait_for_sporks_same()
 
         print("Test new InstantSend")
diff --git a/qa/rpc-tests/p2p-instantsend.py b/qa/rpc-tests/p2p-instantsend.py
index 45ecfd3b2abf..96539191963b 100755
--- a/qa/rpc-tests/p2p-instantsend.py
+++ b/qa/rpc-tests/p2p-instantsend.py
@@ -28,7 +28,7 @@ def run_test(self):
         print("Test old InstantSend")
         self.test_doublespend()
 
-        self.nodes[0].spork("SPORK_2_INSTANTSEND_ENABLED", 1)
+        self.nodes[0].spork("SPORK_20_INSTANTSEND_LLMQ_BASED", 0)
         self.wait_for_sporks_same()
 
         print("Test new InstantSend")

From f8f867a6bb72182602bcc629426d666243fbb42c Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Mon, 4 Mar 2019 13:34:06 +0100
Subject: [PATCH 29/33] Sync blocks after generating in mine_quorum()

---
 qa/rpc-tests/test_framework/test_framework.py | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/qa/rpc-tests/test_framework/test_framework.py b/qa/rpc-tests/test_framework/test_framework.py
index b6f354f1387b..e0d5fbdd8494 100755
--- a/qa/rpc-tests/test_framework/test_framework.py
+++ b/qa/rpc-tests/test_framework/test_framework.py
@@ -498,36 +498,44 @@ def mine_quorum(self, expected_valid_count=10):
             set_mocktime(get_mocktime() + 1)
             set_node_times(self.nodes, get_mocktime())
             self.nodes[0].generate(skip_count)
+        sync_blocks(self.nodes)
 
         # Make sure all reached phase 1 (init)
         self.wait_for_quorum_phase(1, None, 0)
+        # Give nodes some time to connect to neighbors
+        sleep(2)
         set_mocktime(get_mocktime() + 1)
         set_node_times(self.nodes, get_mocktime())
         self.nodes[0].generate(2)
+        sync_blocks(self.nodes)
 
         # Make sure all reached phase 2 (contribute) and received all contributions
         self.wait_for_quorum_phase(2, "receivedContributions", expected_valid_count)
         set_mocktime(get_mocktime() + 1)
         set_node_times(self.nodes, get_mocktime())
         self.nodes[0].generate(2)
+        sync_blocks(self.nodes)
 
         # Make sure all reached phase 3 (complain) and received all complaints
         self.wait_for_quorum_phase(3, "receivedComplaints" if expected_valid_count != 10 else None, expected_valid_count)
         set_mocktime(get_mocktime() + 1)
         set_node_times(self.nodes, get_mocktime())
         self.nodes[0].generate(2)
+        sync_blocks(self.nodes)
 
         # Make sure all reached phase 4 (justify)
         self.wait_for_quorum_phase(4, None, 0)
         set_mocktime(get_mocktime() + 1)
         set_node_times(self.nodes, get_mocktime())
         self.nodes[0].generate(2)
+        sync_blocks(self.nodes)
 
         # Make sure all reached phase 5 (commit)
         self.wait_for_quorum_phase(5, "receivedPrematureCommitments", expected_valid_count)
         set_mocktime(get_mocktime() + 1)
         set_node_times(self.nodes, get_mocktime())
         self.nodes[0].generate(2)
+        sync_blocks(self.nodes)
 
         # Make sure all reached phase 6 (mining)
         self.wait_for_quorum_phase(6, None, 0)
@@ -544,6 +552,7 @@ def mine_quorum(self, expected_valid_count=10):
             set_mocktime(get_mocktime() + 1)
             set_node_times(self.nodes, get_mocktime())
             self.nodes[0].generate(1)
+            sync_blocks(self.nodes)
 
         sync_blocks(self.nodes)
 

From fae33e03ae5c112a0adceb9120b135211f8b9a3b Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Mon, 4 Mar 2019 15:15:38 +0100
Subject: [PATCH 30/33] Let ProcessPendingReconstructedRecoveredSigs return
 void instead of bool

Return value is unused and the method actually never returned something.
---
 src/llmq/quorums_signing.cpp | 2 +-
 src/llmq/quorums_signing.h   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/llmq/quorums_signing.cpp b/src/llmq/quorums_signing.cpp
index 8941c4504a4d..7c2f4e0ce561 100644
--- a/src/llmq/quorums_signing.cpp
+++ b/src/llmq/quorums_signing.cpp
@@ -440,7 +440,7 @@ void CSigningManager::CollectPendingRecoveredSigsToVerify(
     }
 }
 
-bool CSigningManager::ProcessPendingReconstructedRecoveredSigs()
+void CSigningManager::ProcessPendingReconstructedRecoveredSigs()
 {
     decltype(pendingReconstructedRecoveredSigs) l;
     {
diff --git a/src/llmq/quorums_signing.h b/src/llmq/quorums_signing.h
index 6a7325de6139..3c379e4bf307 100644
--- a/src/llmq/quorums_signing.h
+++ b/src/llmq/quorums_signing.h
@@ -154,7 +154,7 @@ class CSigningManager
     void CollectPendingRecoveredSigsToVerify(size_t maxUniqueSessions,
             std::unordered_map<NodeId, std::list<CRecoveredSig>>& retSigShares,
             std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher>& retQuorums);
-    bool ProcessPendingReconstructedRecoveredSigs();
+    void ProcessPendingReconstructedRecoveredSigs();
     bool ProcessPendingRecoveredSigs(CConnman& connman); // called from the worker thread of CSigSharesManager
     void ProcessRecoveredSig(NodeId nodeId, const CRecoveredSig& recoveredSig, const CQuorumCPtr& quorum, CConnman& connman);
     void Cleanup(); // called from the worker thread of CSigSharesManager

From 4d3365ddb2667391a506db9094e78c9dcd36555c Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Tue, 5 Mar 2019 17:35:18 +0100
Subject: [PATCH 31/33] Completely disable InstantSend while filling mempool in
 autoix-mempool.py

Otherwise we overload Travis and tests start to randomly fail.
---
 qa/rpc-tests/autoix-mempool.py | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/qa/rpc-tests/autoix-mempool.py b/qa/rpc-tests/autoix-mempool.py
index a8be9a321fc1..1d5e6ff90eed 100755
--- a/qa/rpc-tests/autoix-mempool.py
+++ b/qa/rpc-tests/autoix-mempool.py
@@ -161,8 +161,12 @@ def test_auto(self, new_is = False):
 
         # fill mempool with transactions
         self.set_autoix_spork_state(False)
+        self.nodes[0].spork("SPORK_2_INSTANTSEND_ENABLED", 4070908800)
+        self.wait_for_sporks_same()
         self.fill_mempool()
         self.set_autoix_spork_state(True)
+        self.nodes[0].spork("SPORK_2_INSTANTSEND_ENABLED", 0)
+        self.wait_for_sporks_same()
 
         # autoIX is not working now
         assert(not self.send_simple_tx(sender, receiver))

From 041a1c26d091e5623e510f0716872289e8e59510 Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Wed, 6 Mar 2019 07:58:27 +0100
Subject: [PATCH 32/33] Move safe TX checks into TestForBlock and
 TestPackageTransactions

Otherwise we'll miss checks for ancestors.
---
 src/miner.cpp | 20 +++++++++-----------
 1 file changed, 9 insertions(+), 11 deletions(-)

diff --git a/src/miner.cpp b/src/miner.cpp
index 64fd4fc93f53..c4c849069323 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -287,11 +287,15 @@ bool BlockAssembler::TestPackage(uint64_t packageSize, unsigned int packageSigOp
 
 // Perform transaction-level checks before adding to block:
 // - transaction finality (locktime)
+// - safe TXs in regard to ChainLocks
 bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& package)
 {
     BOOST_FOREACH (const CTxMemPool::txiter it, package) {
         if (!IsFinalTx(it->GetTx(), nHeight, nLockTimeCutoff))
             return false;
+        if (!llmq::chainLocksHandler->IsTxSafeForMining(it->GetTx().GetHash())) {
+            return false;
+        }
     }
     return true;
 }
@@ -333,6 +337,10 @@ bool BlockAssembler::TestForBlock(CTxMemPool::txiter iter)
     if (!IsFinalTx(iter->GetTx(), nHeight, nLockTimeCutoff))
         return false;
 
+    if (!llmq::chainLocksHandler->IsTxSafeForMining(iter->GetTx().GetHash())) {
+        return false;
+    }
+
     return true;
 }
 
@@ -454,12 +462,6 @@ void BlockAssembler::addPackageTxs(int &nPackagesSelected, int &nDescendantsUpda
             continue;
         }
 
-        if (mi != mempool.mapTx.get<ancestor_score>().end() &&
-                !llmq::chainLocksHandler->IsTxSafeForMining(mi->GetTx().GetHash())) {
-            ++mi;
-            continue;
-        }
-
         // Now that mi is not stale, determine which transaction to evaluate:
         // the next entry from mapTx, or the best from mapModifiedTx?
         bool fUsingModified = false;
@@ -530,7 +532,7 @@ void BlockAssembler::addPackageTxs(int &nPackagesSelected, int &nDescendantsUpda
         onlyUnconfirmed(ancestors);
         ancestors.insert(iter);
 
-        // Test if all tx's are Final
+        // Test if all tx's are Final and safe
         if (!TestPackageTransactions(ancestors)) {
             if (fUsingModified) {
                 mapModifiedTx.get<ancestor_score>().erase(modit);
@@ -608,10 +610,6 @@ void BlockAssembler::addPriorityTxs()
             continue;
         }
 
-        if (!llmq::chainLocksHandler->IsTxSafeForMining(iter->GetTx().GetHash())) {
-            continue;
-        }
-
         // If this tx fits in the block add it, otherwise keep looping
         if (TestForBlock(iter)) {
             AddToBlock(iter);

From 06fc655595711025355a27a0aff9b3edd9944e46 Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Thu, 7 Mar 2019 11:43:04 +0100
Subject: [PATCH 33/33] Actually remove from finalInstantSendLocks in
 CInstantSendManager::RemoveFinalISLock

---
 src/llmq/quorums_instantsend.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/llmq/quorums_instantsend.cpp b/src/llmq/quorums_instantsend.cpp
index 7f0a322ac66c..fec7033e23a5 100644
--- a/src/llmq/quorums_instantsend.cpp
+++ b/src/llmq/quorums_instantsend.cpp
@@ -721,6 +721,7 @@ void CInstantSendManager::RemoveFinalISLock(const uint256& hash)
         inputToInstantSendLock.erase(in);
     }
     UpdateISLockMinedBlock(&islockInfo, nullptr);
+    finalInstantSendLocks.erase(it);
 }
 
 void CInstantSendManager::RemoveMempoolConflictsForLock(const uint256& hash, const CInstantSendLock& islock)
