neobytes/src/llmq/quorums_instantsend.h

147 lines
5.5 KiB
C
Raw Normal View History

// 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_INSTANTSEND_H
#define DASH_QUORUMS_INSTANTSEND_H
#include "quorums_signing.h"
#include "coins.h"
2019-03-11 06:40:28 +01:00
#include "unordered_lru_cache.h"
#include "primitives/transaction.h"
#include <unordered_map>
#include <unordered_set>
class CScheduler;
namespace llmq
{
class CInstantSendLock
{
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;
};
2019-03-11 06:40:28 +01:00
typedef std::shared_ptr<CInstantSendLock> CInstantSendLockPtr;
class CInstantSendDb
{
2019-03-11 06:40:28 +01:00
private:
CDBWrapper& db;
unordered_lru_cache<uint256, CInstantSendLockPtr, StaticSaltedHasher, 10000> islockCache;
unordered_lru_cache<uint256, uint256, StaticSaltedHasher, 10000> txidCache;
unordered_lru_cache<COutPoint, uint256, SaltedOutpointHasher, 10000> outpointCache;
public:
2019-03-11 06:40:28 +01:00
CInstantSendDb(CDBWrapper& _db) : db(_db) {}
void WriteNewInstantSendLock(const uint256& hash, const CInstantSendLock& islock);
void RemoveInstantSendLock(const uint256& hash, CInstantSendLockPtr islock);
CInstantSendLockPtr GetInstantSendLockByHash(const uint256& hash);
CInstantSendLockPtr GetInstantSendLockByTxid(const uint256& txid);
CInstantSendLockPtr GetInstantSendLockByInput(const COutPoint& outpoint);
void WriteLastChainLockBlock(const uint256& hashBlock);
uint256 GetLastChainLockBlock();
};
class CInstantSendManager : public CRecoveredSigsListener
{
private:
CCriticalSection cs;
CScheduler* scheduler;
2019-03-11 06:40:28 +01:00
CInstantSendDb db;
/**
* Request ids of inputs that we signed. Used to determine if a recovered signature belongs to an
* in-progress input lock.
*/
std::unordered_set<uint256, StaticSaltedHasher> inputRequestIds;
/**
* 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.
*/
2019-03-11 06:40:28 +01:00
std::unordered_map<uint256, CInstantSendLock, StaticSaltedHasher> creatingInstantSendLocks;
// maps from txid to the in-progress islock
2019-03-11 06:40:28 +01:00
std::unordered_map<uint256, CInstantSendLock*, StaticSaltedHasher> txToCreatingInstantSendLocks;
// Incoming and not verified yet
std::unordered_map<uint256, std::pair<NodeId, CInstantSendLock>> pendingInstantSendLocks;
bool hasScheduledProcessPending{false};
public:
2019-03-11 06:40:28 +01:00
CInstantSendManager(CScheduler* _scheduler, CDBWrapper& _llmqDb);
~CInstantSendManager();
void RegisterAsRecoveredSigsListener();
void UnregisterAsRecoveredSigsListener();
public:
Multiple refactorings/fixes for LLMQ bases InstantSend and ChainLocks (#2779) * Remove unused parameters from CInstantSendManager::ProcessTx * Pass txHash in CheckCanLock by reference instead of pointer * Dont' allow locking of TXs without inputs * Remove unused local variable nInstantSendConfirmationsRequired * Don't subtract 1 from nInstantSendConfirmationsRequired This was necessary in the old system but is not necessary in the new system. It also prevented proper retroactive signing of chained TXs in regtest as it resulted in child TXs to return true immediately for CheckCanLock when it should actually have waited for the parent TX to become locked first. * Access chainActive.Height() while cs_main is locked * Properly read and write lastChainLockBlock "pindex" is NOT the chainlocked block after the while loop finishes. We must use the pindex (renamed to pindexChainLock now) given on method entry. Also, the GetLastChainLockBlock() result was not assigned to, lastChainLockBlock which resulted in the while loop to run unnecessarily long. * Generalize filtering in NewPoWValidBlock and SyncTransaction We're actually interested in all TXs that have inputs, so no need to explicitly check for tx types. * Use tx.IsCoinBase() instead of checking for index 0 * Handle cases where a TX is not received yet in wait_for_instantlock * Wait on all nodes for the locks Otherwise we end up with the sender having it locked but other nodes not yet, failing the test. * Fix LogPrintf call in CChainLocksHandler::DoInvalidateBlock
2019-03-19 08:38:16 +01:00
bool ProcessTx(const CTransaction& tx, const Consensus::Params& params);
bool CheckCanLock(const CTransaction& tx, bool printDebug, const Consensus::Params& params);
Multiple refactorings/fixes for LLMQ bases InstantSend and ChainLocks (#2779) * Remove unused parameters from CInstantSendManager::ProcessTx * Pass txHash in CheckCanLock by reference instead of pointer * Dont' allow locking of TXs without inputs * Remove unused local variable nInstantSendConfirmationsRequired * Don't subtract 1 from nInstantSendConfirmationsRequired This was necessary in the old system but is not necessary in the new system. It also prevented proper retroactive signing of chained TXs in regtest as it resulted in child TXs to return true immediately for CheckCanLock when it should actually have waited for the parent TX to become locked first. * Access chainActive.Height() while cs_main is locked * Properly read and write lastChainLockBlock "pindex" is NOT the chainlocked block after the while loop finishes. We must use the pindex (renamed to pindexChainLock now) given on method entry. Also, the GetLastChainLockBlock() result was not assigned to, lastChainLockBlock which resulted in the while loop to run unnecessarily long. * Generalize filtering in NewPoWValidBlock and SyncTransaction We're actually interested in all TXs that have inputs, so no need to explicitly check for tx types. * Use tx.IsCoinBase() instead of checking for index 0 * Handle cases where a TX is not received yet in wait_for_instantlock * Wait on all nodes for the locks Otherwise we end up with the sender having it locked but other nodes not yet, failing the test. * Fix LogPrintf call in CChainLocksHandler::DoInvalidateBlock
2019-03-19 08:38:16 +01:00
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 HandleNewInstantSendLockRecoveredSig(const CRecoveredSig& recoveredSig);
void TrySignInstantSendLock(const CTransaction& tx);
void ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman);
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);
2019-03-11 06:40:28 +01:00
void UpdateWalletTransaction(const uint256& txid, const CTransactionRef& tx);
void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock);
Multiple refactorings/fixes for LLMQ bases InstantSend and ChainLocks (#2779) * Remove unused parameters from CInstantSendManager::ProcessTx * Pass txHash in CheckCanLock by reference instead of pointer * Dont' allow locking of TXs without inputs * Remove unused local variable nInstantSendConfirmationsRequired * Don't subtract 1 from nInstantSendConfirmationsRequired This was necessary in the old system but is not necessary in the new system. It also prevented proper retroactive signing of chained TXs in regtest as it resulted in child TXs to return true immediately for CheckCanLock when it should actually have waited for the parent TX to become locked first. * Access chainActive.Height() while cs_main is locked * Properly read and write lastChainLockBlock "pindex" is NOT the chainlocked block after the while loop finishes. We must use the pindex (renamed to pindexChainLock now) given on method entry. Also, the GetLastChainLockBlock() result was not assigned to, lastChainLockBlock which resulted in the while loop to run unnecessarily long. * Generalize filtering in NewPoWValidBlock and SyncTransaction We're actually interested in all TXs that have inputs, so no need to explicitly check for tx types. * Use tx.IsCoinBase() instead of checking for index 0 * Handle cases where a TX is not received yet in wait_for_instantlock * Wait on all nodes for the locks Otherwise we end up with the sender having it locked but other nodes not yet, failing the test. * Fix LogPrintf call in CChainLocksHandler::DoInvalidateBlock
2019-03-19 08:38:16 +01:00
void NotifyChainLock(const CBlockIndex* pindexChainLock);
2019-03-11 06:40:28 +01:00
void RemoveFinalISLock(const uint256& hash, const CInstantSendLockPtr& islock);
void RemoveMempoolConflictsForLock(const uint256& hash, const CInstantSendLock& islock);
Implement retroactive IS locking of transactions first seen in blocks instead of mempool (#2770) * Don't rely on UTXO set in CheckCanLock The UTXO set only works for TXs in the mempool and won't work when we try to retroactively lock unlocked TXs from blocks. This is safe as ProcessTx is only called when a TX was accepted into the mempool or connected in a block, which means that all input checks were good. * Rename RetryLockMempoolTxs to RetryLockTxs and let it retry connected TXs * Instead of manually calling ProcessTx, let SyncTransaction handle all cases SyncTransaction is called from AcceptToMemoryPool and when transactions got connected in a block. So this is the time we want to run TXs through ProcessTx. This also enables retroactive signing of TXs that were unknown before a new block appeared. * Test retroactive signing and safe TXs in LLMQ ChainLocks tests * Also test for retroactive signing of chained TXs * Honor lockedParentTx when looking for TXs to retry signing * Stop scanning for TXs to retry after a depth of 6 * Generate 6 block to avoid retroactive signing overloading Travis * Avoid retroactive signing * Don't rely on NewPoWValidBlock and use SyncTransaction to build blockTxs NewPoWValidBlock is not guaranteed to be called when blocks come in fast. When a block is accepted in AcceptBlock, NewPoWValidBlock is only called when the new block is a successor of the currently active tip. This is not the case when after the first block a second block is accepted immediately as the first block is not connected yet. This might be a bug actually in the handling of NewPoWValidBlock, so we might need to check/fix this later, but currently I prefer to not touch that part. Instead, we now use SyncTransaction to gather TXs for blockTxs. This works because SyncTransaction is called for all transactions in a freshly connected block in one go. The call also happens before UpdatedBlockTip is called, so it's fine with the existing logic. * Use tx.IsCoinBase() instead of checking index 0 Also check for empty vin.
2019-03-19 11:55:51 +01:00
void RetryLockTxs(const uint256& lockedParentTx);
bool AlreadyHave(const CInv& inv);
bool GetInstantSendLockByHash(const uint256& hash, CInstantSendLock& ret);
};
extern CInstantSendManager* quorumInstantSendManager;
// 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();
}
#endif//DASH_QUORUMS_INSTANTSEND_H