InstantSend overhaul (#1288)
* Multi-quorum InstantSend, complete refactoring + cleanup for IS and partial protobump * more changes: - allow InstantSend tx to have 10 inputs max - store many unique tx hashes in mapVotedOutpoints - more checks in AcceptToMemoryPoolWorker (moved from ProcessMessage + CTxLockRequest(tx).IsValid() ) * More changes: - let multiple lock candidates compete for votes - fail to vote on the same outpoint twice early * More changes: - notify CInstantSend on UpdatedBlockTip -> remove cs_main from CheckAndRemove() - notify CInstantSend on SyncTransaction -> count expiration block starting from the block corresponding tx was confirmed instead of the block lock candidate/vote was created - fixed few locks * add comments about nConfirmedHeight * Fix "Block vs Lock" edge case * Fix "Block vs Lock" edge case, p2 * Fix issues: - fix logic for locking inputs and notifying - see UpdateLockedTransaction, TryToFinalizeLockCandidate - add missing hash inserting in ProcessTxLockVote - add nMaxBlocks param to ResolveConflicts to limit max depth allowed to disconnect blocks recursively - fix false positive mempool conflict - add missing mutex locks - fix fRequireUnspent logic in CTxLockRequest::IsValid
This commit is contained in:
parent
9c5db04953
commit
60409df822
@ -77,7 +77,6 @@ public:
|
|||||||
consensus.nMasternodePaymentsIncreaseBlock = 158000; // actual historical value
|
consensus.nMasternodePaymentsIncreaseBlock = 158000; // actual historical value
|
||||||
consensus.nMasternodePaymentsIncreasePeriod = 576*30; // 17280 - actual historical value
|
consensus.nMasternodePaymentsIncreasePeriod = 576*30; // 17280 - actual historical value
|
||||||
consensus.nInstantSendKeepLock = 24;
|
consensus.nInstantSendKeepLock = 24;
|
||||||
consensus.nInstantSendReprocessBlocks = 15;
|
|
||||||
consensus.nBudgetPaymentsStartBlock = 328008; // actual historical value
|
consensus.nBudgetPaymentsStartBlock = 328008; // actual historical value
|
||||||
consensus.nBudgetPaymentsCycleBlocks = 16616; // ~(60*24*30)/2.6, actual number of blocks per month is 200700 / 12 = 16725
|
consensus.nBudgetPaymentsCycleBlocks = 16616; // ~(60*24*30)/2.6, actual number of blocks per month is 200700 / 12 = 16725
|
||||||
consensus.nBudgetPaymentsWindowBlocks = 100;
|
consensus.nBudgetPaymentsWindowBlocks = 100;
|
||||||
@ -201,7 +200,6 @@ public:
|
|||||||
consensus.nMasternodePaymentsIncreaseBlock = 46000;
|
consensus.nMasternodePaymentsIncreaseBlock = 46000;
|
||||||
consensus.nMasternodePaymentsIncreasePeriod = 576;
|
consensus.nMasternodePaymentsIncreasePeriod = 576;
|
||||||
consensus.nInstantSendKeepLock = 6;
|
consensus.nInstantSendKeepLock = 6;
|
||||||
consensus.nInstantSendReprocessBlocks = 4;
|
|
||||||
consensus.nBudgetPaymentsStartBlock = 60000;
|
consensus.nBudgetPaymentsStartBlock = 60000;
|
||||||
consensus.nBudgetPaymentsCycleBlocks = 50;
|
consensus.nBudgetPaymentsCycleBlocks = 50;
|
||||||
consensus.nBudgetPaymentsWindowBlocks = 10;
|
consensus.nBudgetPaymentsWindowBlocks = 10;
|
||||||
@ -308,7 +306,6 @@ public:
|
|||||||
consensus.nMasternodePaymentsIncreaseBlock = 350;
|
consensus.nMasternodePaymentsIncreaseBlock = 350;
|
||||||
consensus.nMasternodePaymentsIncreasePeriod = 10;
|
consensus.nMasternodePaymentsIncreasePeriod = 10;
|
||||||
consensus.nInstantSendKeepLock = 6;
|
consensus.nInstantSendKeepLock = 6;
|
||||||
consensus.nInstantSendReprocessBlocks = 4;
|
|
||||||
consensus.nBudgetPaymentsStartBlock = 1000;
|
consensus.nBudgetPaymentsStartBlock = 1000;
|
||||||
consensus.nBudgetPaymentsCycleBlocks = 50;
|
consensus.nBudgetPaymentsCycleBlocks = 50;
|
||||||
consensus.nBudgetPaymentsWindowBlocks = 10;
|
consensus.nBudgetPaymentsWindowBlocks = 10;
|
||||||
|
@ -41,7 +41,6 @@ struct Params {
|
|||||||
int nMasternodePaymentsIncreaseBlock;
|
int nMasternodePaymentsIncreaseBlock;
|
||||||
int nMasternodePaymentsIncreasePeriod; // in blocks
|
int nMasternodePaymentsIncreasePeriod; // in blocks
|
||||||
int nInstantSendKeepLock; // in blocks
|
int nInstantSendKeepLock; // in blocks
|
||||||
int nInstantSendReprocessBlocks;
|
|
||||||
int nBudgetPaymentsStartBlock;
|
int nBudgetPaymentsStartBlock;
|
||||||
int nBudgetPaymentsCycleBlocks;
|
int nBudgetPaymentsCycleBlocks;
|
||||||
int nBudgetPaymentsWindowBlocks;
|
int nBudgetPaymentsWindowBlocks;
|
||||||
|
@ -2490,7 +2490,7 @@ void ThreadCheckDarkSendPool()
|
|||||||
mnodeman.ProcessMasternodeConnections();
|
mnodeman.ProcessMasternodeConnections();
|
||||||
mnodeman.CheckAndRemove();
|
mnodeman.CheckAndRemove();
|
||||||
mnpayments.CheckAndRemove();
|
mnpayments.CheckAndRemove();
|
||||||
CleanTxLockCandidates();
|
instantsend.CheckAndRemove();
|
||||||
}
|
}
|
||||||
|
|
||||||
darkSendPool.CheckTimeout();
|
darkSendPool.CheckTimeout();
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "dsnotificationinterface.h"
|
#include "dsnotificationinterface.h"
|
||||||
#include "darksend.h"
|
#include "darksend.h"
|
||||||
|
#include "instantx.h"
|
||||||
#include "governance.h"
|
#include "governance.h"
|
||||||
#include "masternodeman.h"
|
#include "masternodeman.h"
|
||||||
#include "masternode-payments.h"
|
#include "masternode-payments.h"
|
||||||
@ -21,7 +22,13 @@ void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindex)
|
|||||||
{
|
{
|
||||||
mnodeman.UpdatedBlockTip(pindex);
|
mnodeman.UpdatedBlockTip(pindex);
|
||||||
darkSendPool.UpdatedBlockTip(pindex);
|
darkSendPool.UpdatedBlockTip(pindex);
|
||||||
|
instantsend.UpdatedBlockTip(pindex);
|
||||||
mnpayments.UpdatedBlockTip(pindex);
|
mnpayments.UpdatedBlockTip(pindex);
|
||||||
governance.UpdatedBlockTip(pindex);
|
governance.UpdatedBlockTip(pindex);
|
||||||
masternodeSync.UpdatedBlockTip(pindex);
|
masternodeSync.UpdatedBlockTip(pindex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CDSNotificationInterface::SyncTransaction(const CTransaction &tx, const CBlock *pblock)
|
||||||
|
{
|
||||||
|
instantsend.SyncTransaction(tx, pblock);
|
||||||
|
}
|
@ -17,6 +17,7 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
// CValidationInterface
|
// CValidationInterface
|
||||||
void UpdatedBlockTip(const CBlockIndex *pindex);
|
void UpdatedBlockTip(const CBlockIndex *pindex);
|
||||||
|
void SyncTransaction(const CTransaction &tx, const CBlock *pblock);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
1263
src/instantx.cpp
1263
src/instantx.cpp
File diff suppressed because it is too large
Load Diff
229
src/instantx.h
229
src/instantx.h
@ -7,9 +7,13 @@
|
|||||||
#include "net.h"
|
#include "net.h"
|
||||||
#include "primitives/transaction.h"
|
#include "primitives/transaction.h"
|
||||||
|
|
||||||
class CTransaction;
|
|
||||||
class CTxLockVote;
|
class CTxLockVote;
|
||||||
|
class COutPointLock;
|
||||||
|
class CTxLockRequest;
|
||||||
class CTxLockCandidate;
|
class CTxLockCandidate;
|
||||||
|
class CInstantSend;
|
||||||
|
|
||||||
|
extern CInstantSend instantsend;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
At 15 signatures, 1/2 of the masternode network can be owned by
|
At 15 signatures, 1/2 of the masternode network can be owned by
|
||||||
@ -20,106 +24,225 @@ class CTxLockCandidate;
|
|||||||
### getting 5 of 10 signatures w/ 1000 nodes of 2900
|
### getting 5 of 10 signatures w/ 1000 nodes of 2900
|
||||||
(1000/2900.0)**5 = 0.004875397277841433
|
(1000/2900.0)**5 = 0.004875397277841433
|
||||||
*/
|
*/
|
||||||
static const int INSTANTSEND_SIGNATURES_REQUIRED = 6;
|
static const int INSTANTSEND_CONFIRMATIONS_REQUIRED = 6;
|
||||||
static const int INSTANTSEND_SIGNATURES_TOTAL = 10;
|
|
||||||
static const int DEFAULT_INSTANTSEND_DEPTH = 5;
|
static const int DEFAULT_INSTANTSEND_DEPTH = 5;
|
||||||
|
|
||||||
static const int MIN_INSTANTSEND_PROTO_VERSION = 70204;
|
static const int MIN_INSTANTSEND_PROTO_VERSION = 70205;
|
||||||
static const CAmount INSTANTSEND_MIN_FEE = 0.001 * COIN;
|
|
||||||
|
|
||||||
extern bool fEnableInstantSend;
|
extern bool fEnableInstantSend;
|
||||||
extern int nInstantSendDepth;
|
extern int nInstantSendDepth;
|
||||||
extern int nCompleteTXLocks;
|
extern int nCompleteTXLocks;
|
||||||
|
|
||||||
extern std::map<uint256, CTransaction> mapLockRequestAccepted;
|
class CInstantSend
|
||||||
extern std::map<uint256, CTransaction> mapLockRequestRejected;
|
{
|
||||||
extern std::map<uint256, CTxLockVote> mapTxLockVotes;
|
private:
|
||||||
extern std::map<COutPoint, uint256> mapLockedInputs;
|
static const int ORPHAN_VOTE_SECONDS = 60;
|
||||||
|
|
||||||
|
// Keep track of current block index
|
||||||
|
const CBlockIndex *pCurrentBlockIndex;
|
||||||
|
|
||||||
void ProcessMessageInstantSend(CNode* pfrom, std::string& strCommand, CDataStream& vRecv);
|
// maps for AlreadyHave
|
||||||
|
std::map<uint256, CTxLockRequest> mapLockRequestAccepted; // tx hash - tx
|
||||||
|
std::map<uint256, CTxLockRequest> mapLockRequestRejected; // tx hash - tx
|
||||||
|
std::map<uint256, CTxLockVote> mapTxLockVotes; // vote hash - vote
|
||||||
|
std::map<uint256, CTxLockVote> mapTxLockVotesOrphan; // vote hash - vote
|
||||||
|
|
||||||
bool IsInstantSendTxValid(const CTransaction& txCandidate);
|
std::map<uint256, CTxLockCandidate> mapTxLockCandidates; // tx hash - lock candidate
|
||||||
|
|
||||||
bool ProcessTxLockRequest(CNode* pfrom, const CTransaction &tx);
|
std::map<COutPoint, std::set<uint256> > mapVotedOutpoints; // utxo - tx hash set
|
||||||
|
std::map<COutPoint, uint256> mapLockedOutpoints; // utxo - tx hash
|
||||||
|
|
||||||
int64_t CreateTxLockCandidate(const CTransaction &tx);
|
//track masternodes who voted with no txreq (for DOS protection)
|
||||||
|
std::map<COutPoint, int64_t> mapMasternodeOrphanVotes; // mn outpoint - time
|
||||||
|
|
||||||
//check if we need to vote on this transaction
|
bool CreateTxLockCandidate(const CTxLockRequest& txLockRequest);
|
||||||
void CreateTxLockVote(const CTransaction& tx, int nBlockHeight);
|
void Vote(CTxLockCandidate& txLockCandidate);
|
||||||
|
|
||||||
//process consensus vote message
|
//process consensus vote message
|
||||||
bool ProcessTxLockVote(CNode *pnode, CTxLockVote& vote);
|
bool ProcessTxLockVote(CNode* pfrom, CTxLockVote& vote);
|
||||||
|
void ProcessOrphanTxLockVotes();
|
||||||
|
bool IsEnoughOrphanVotesForTx(const CTxLockRequest& txLockRequest);
|
||||||
|
bool IsEnoughOrphanVotesForTxAndOutPoint(const uint256& txHash, const COutPoint& outpoint);
|
||||||
|
int64_t GetAverageMasternodeOrphanVoteTime();
|
||||||
|
|
||||||
void ProcessOrphanTxLockVotes();
|
void TryToFinalizeLockCandidate(const CTxLockCandidate& txLockCandidate);
|
||||||
|
void LockTransactionInputs(const CTxLockCandidate& txLockCandidate);
|
||||||
|
//update UI and notify external script if any
|
||||||
|
void UpdateLockedTransaction(const CTxLockCandidate& txLockCandidate);
|
||||||
|
bool ResolveConflicts(const CTxLockCandidate& txLockCandidate, int nMaxBlocks);
|
||||||
|
|
||||||
//update UI and notify external script if any
|
bool IsInstantSendReadyToLock(const uint256 &txHash);
|
||||||
void UpdateLockedTransaction(const CTransaction& tx, bool fForceNotification = false);
|
|
||||||
|
|
||||||
void LockTransactionInputs(const CTransaction& tx);
|
public:
|
||||||
|
CCriticalSection cs_instantsend;
|
||||||
|
|
||||||
// if two conflicting locks are approved by the network, they will cancel out
|
void ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv);
|
||||||
bool FindConflictingLocks(const CTransaction& tx);
|
|
||||||
|
|
||||||
//try to resolve conflicting locks
|
bool ProcessTxLockRequest(const CTxLockRequest& txLockRequest);
|
||||||
void ResolveConflicts(const CTransaction& tx);
|
|
||||||
|
|
||||||
// keep transaction locks in memory for an hour
|
bool AlreadyHave(const uint256& hash);
|
||||||
void CleanTxLockCandidates();
|
|
||||||
|
|
||||||
// verify if transaction is currently locked
|
void AcceptLockRequest(const CTxLockRequest& txLockRequest);
|
||||||
bool IsLockedInstandSendTransaction(const uint256 &txHash);
|
void RejectLockRequest(const CTxLockRequest& txLockRequest);
|
||||||
|
bool HasTxLockRequest(const uint256& txHash);
|
||||||
|
bool GetTxLockRequest(const uint256& txHash, CTxLockRequest& txLockRequestRet);
|
||||||
|
|
||||||
// get the actual uber og accepted lock signatures
|
bool GetTxLockVote(const uint256& hash, CTxLockVote& txLockVoteRet);
|
||||||
int GetTransactionLockSignatures(const uint256 &txHash);
|
|
||||||
|
|
||||||
// verify if transaction lock timed out
|
bool GetLockedOutPointTxHash(const COutPoint& outpoint, uint256& hashRet);
|
||||||
bool IsTransactionLockTimedOut(const uint256 &txHash);
|
|
||||||
|
|
||||||
int64_t GetAverageMasternodeOrphanVoteTime();
|
// verify if transaction is currently locked
|
||||||
|
bool IsLockedInstantSendTransaction(const uint256& txHash);
|
||||||
|
// get the actual uber og accepted lock signatures
|
||||||
|
int GetTransactionLockSignatures(const uint256& txHash);
|
||||||
|
|
||||||
|
// remove expired entries from maps
|
||||||
|
void CheckAndRemove();
|
||||||
|
// verify if transaction lock timed out
|
||||||
|
bool IsTxLockRequestTimedOut(const uint256& txHash);
|
||||||
|
|
||||||
|
void Relay(const uint256& txHash);
|
||||||
|
|
||||||
|
void UpdatedBlockTip(const CBlockIndex *pindex);
|
||||||
|
void SyncTransaction(const CTransaction& tx, const CBlock* pblock);
|
||||||
|
};
|
||||||
|
|
||||||
|
class CTxLockRequest : public CTransaction
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
static const int TIMEOUT_SECONDS = 5 * 60;
|
||||||
|
static const CAmount MIN_FEE = 0.001 * COIN;
|
||||||
|
|
||||||
|
int64_t nTimeCreated;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const int MAX_INPUTS = 10;
|
||||||
|
|
||||||
|
CTxLockRequest() :
|
||||||
|
CTransaction(),
|
||||||
|
nTimeCreated(GetTime())
|
||||||
|
{}
|
||||||
|
CTxLockRequest(const CTransaction& tx) :
|
||||||
|
CTransaction(tx),
|
||||||
|
nTimeCreated(GetTime())
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool IsValid(bool fRequireUnspent = true) const;
|
||||||
|
CAmount GetMinFee() const;
|
||||||
|
int GetMaxSignatures() const;
|
||||||
|
bool IsTimedOut() const;
|
||||||
|
};
|
||||||
|
|
||||||
class CTxLockVote
|
class CTxLockVote
|
||||||
{
|
{
|
||||||
public:
|
private:
|
||||||
CTxIn vinMasternode;
|
|
||||||
uint256 txHash;
|
uint256 txHash;
|
||||||
int nBlockHeight;
|
COutPoint outpoint;
|
||||||
std::vector<unsigned char> vchMasterNodeSignature;
|
COutPoint outpointMasternode;
|
||||||
|
std::vector<unsigned char> vchMasternodeSignature;
|
||||||
// local memory only
|
// local memory only
|
||||||
int64_t nOrphanExpireTime;
|
int nConfirmedHeight; // when corresponding tx is 0-confirmed or conflicted, nConfirmedHeight is -1
|
||||||
|
int64_t nTimeCreated;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CTxLockVote() :
|
||||||
|
txHash(),
|
||||||
|
outpoint(),
|
||||||
|
outpointMasternode(),
|
||||||
|
vchMasternodeSignature(),
|
||||||
|
nConfirmedHeight(-1),
|
||||||
|
nTimeCreated(GetTime())
|
||||||
|
{}
|
||||||
|
|
||||||
|
CTxLockVote(const uint256& txHashIn, const COutPoint& outpointIn, const COutPoint& outpointMasternodeIn) :
|
||||||
|
txHash(txHashIn),
|
||||||
|
outpoint(outpointIn),
|
||||||
|
outpointMasternode(outpointMasternodeIn),
|
||||||
|
vchMasternodeSignature(),
|
||||||
|
nConfirmedHeight(-1),
|
||||||
|
nTimeCreated(GetTime())
|
||||||
|
{}
|
||||||
|
|
||||||
ADD_SERIALIZE_METHODS;
|
ADD_SERIALIZE_METHODS;
|
||||||
|
|
||||||
template <typename Stream, typename Operation>
|
template <typename Stream, typename Operation>
|
||||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||||
READWRITE(txHash);
|
READWRITE(txHash);
|
||||||
READWRITE(vinMasternode);
|
READWRITE(outpoint);
|
||||||
READWRITE(vchMasterNodeSignature);
|
READWRITE(outpointMasternode);
|
||||||
READWRITE(nBlockHeight);
|
READWRITE(vchMasternodeSignature);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256 GetHash() const;
|
uint256 GetHash() const;
|
||||||
|
|
||||||
|
uint256 GetTxHash() const { return txHash; }
|
||||||
|
COutPoint GetOutpoint() const { return outpoint; }
|
||||||
|
COutPoint GetMasternodeOutpoint() const { return outpointMasternode; }
|
||||||
|
int64_t GetTimeCreated() const { return nTimeCreated; }
|
||||||
|
|
||||||
|
bool IsValid(CNode* pnode) const;
|
||||||
|
void SetConfirmedHeight(int nConfirmedHeightIn) { nConfirmedHeight = nConfirmedHeightIn; }
|
||||||
|
bool IsExpired(int nHeight) const;
|
||||||
|
|
||||||
bool Sign();
|
bool Sign();
|
||||||
bool CheckSignature() const;
|
bool CheckSignature() const;
|
||||||
|
|
||||||
|
void Relay() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class COutPointLock
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
COutPoint outpoint; // utxo
|
||||||
|
std::map<COutPoint, CTxLockVote> mapMasternodeVotes; // masternode outpoint - vote
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const int SIGNATURES_REQUIRED = 6;
|
||||||
|
static const int SIGNATURES_TOTAL = 10;
|
||||||
|
|
||||||
|
COutPointLock(const COutPoint& outpointIn) :
|
||||||
|
outpoint(outpointIn),
|
||||||
|
mapMasternodeVotes()
|
||||||
|
{}
|
||||||
|
|
||||||
|
COutPoint GetOutpoint() const { return outpoint; }
|
||||||
|
|
||||||
|
bool AddVote(const CTxLockVote& vote);
|
||||||
|
bool HasMasternodeVoted(const COutPoint& outpointMasternodeIn);
|
||||||
|
int CountVotes() const { return mapMasternodeVotes.size(); }
|
||||||
|
bool IsReady() const { return CountVotes() >= SIGNATURES_REQUIRED; }
|
||||||
|
|
||||||
|
void Relay() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CTxLockCandidate
|
class CTxLockCandidate
|
||||||
{
|
{
|
||||||
|
private:
|
||||||
|
int nConfirmedHeight; // when corresponding tx is 0-confirmed or conflicted, nConfirmedHeight is -1
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int nBlockHeight;
|
CTxLockCandidate(const CTxLockRequest& txLockRequestIn) :
|
||||||
uint256 txHash;
|
nConfirmedHeight(-1),
|
||||||
std::vector<CTxLockVote> vecTxLockVotes;
|
txLockRequest(txLockRequestIn),
|
||||||
int nExpirationBlock;
|
mapOutPointLocks()
|
||||||
int nTimeout;
|
{}
|
||||||
|
|
||||||
uint256 GetHash() const { return txHash; }
|
CTxLockRequest txLockRequest;
|
||||||
|
std::map<COutPoint, COutPointLock> mapOutPointLocks;
|
||||||
|
|
||||||
bool IsAllVotesValid();
|
uint256 GetHash() const { return txLockRequest.GetHash(); }
|
||||||
void AddVote(const CTxLockVote& vote);
|
|
||||||
int CountVotes();
|
void AddOutPointLock(const COutPoint& outpoint);
|
||||||
|
bool AddVote(const CTxLockVote& vote);
|
||||||
|
bool IsAllOutPointsReady() const;
|
||||||
|
|
||||||
|
bool HasMasternodeVoted(const COutPoint& outpointIn, const COutPoint& outpointMasternodeIn);
|
||||||
|
int CountVotes() const;
|
||||||
|
|
||||||
|
void SetConfirmedHeight(int nConfirmedHeightIn) { nConfirmedHeight = nConfirmedHeightIn; }
|
||||||
|
bool IsExpired(int nHeight) const;
|
||||||
|
|
||||||
|
void Relay() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
128
src/main.cpp
128
src/main.cpp
@ -941,6 +941,18 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in
|
|||||||
return nSigOps;
|
return nSigOps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int GetUTXOHeight(const COutPoint& outpoint)
|
||||||
|
{
|
||||||
|
LOCK(cs_main);
|
||||||
|
CCoins coins;
|
||||||
|
if(!pcoinsTip->GetCoins(outpoint.hash, coins) ||
|
||||||
|
(unsigned int)outpoint.n>=coins.vout.size() ||
|
||||||
|
coins.vout[outpoint.n].IsNull()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return coins.nHeight;
|
||||||
|
}
|
||||||
|
|
||||||
int GetInputAge(const CTxIn &txin)
|
int GetInputAge(const CTxIn &txin)
|
||||||
{
|
{
|
||||||
CCoinsView viewDummy;
|
CCoinsView viewDummy;
|
||||||
@ -966,7 +978,7 @@ int GetInputAgeIX(const uint256 &nTXHash, const CTxIn &txin)
|
|||||||
int nResult = GetInputAge(txin);
|
int nResult = GetInputAge(txin);
|
||||||
if(nResult < 0) return -1;
|
if(nResult < 0) return -1;
|
||||||
|
|
||||||
if (nResult < 6 && IsLockedInstandSendTransaction(nTXHash))
|
if (nResult < 6 && instantsend.IsLockedInstantSendTransaction(nTXHash))
|
||||||
return nInstantSendDepth + nResult;
|
return nInstantSendDepth + nResult;
|
||||||
|
|
||||||
return nResult;
|
return nResult;
|
||||||
@ -974,7 +986,7 @@ int GetInputAgeIX(const uint256 &nTXHash, const CTxIn &txin)
|
|||||||
|
|
||||||
int GetIXConfirmations(const uint256 &nTXHash)
|
int GetIXConfirmations(const uint256 &nTXHash)
|
||||||
{
|
{
|
||||||
if (IsLockedInstandSendTransaction(nTXHash))
|
if (instantsend.IsLockedInstantSendTransaction(nTXHash))
|
||||||
return nInstantSendDepth;
|
return nInstantSendDepth;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -1088,14 +1100,19 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C
|
|||||||
if (pool.exists(hash))
|
if (pool.exists(hash))
|
||||||
return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-in-mempool");
|
return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-in-mempool");
|
||||||
|
|
||||||
// ----------- InstantSend transaction scanning -----------
|
// If this is a Transaction Lock Request check to see if it's valid
|
||||||
|
if(instantsend.HasTxLockRequest(hash) && !CTxLockRequest(tx).IsValid())
|
||||||
|
return state.DoS(10, error("AcceptToMemoryPool : CTxLockRequest %s is invalid", hash.ToString()),
|
||||||
|
REJECT_INVALID, "bad-txlockrequest");
|
||||||
|
|
||||||
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
|
// Check for conflicts with a completed Transaction Lock
|
||||||
if(mapLockedInputs.count(txin.prevout) && mapLockedInputs[txin.prevout] != tx.GetHash()) {
|
BOOST_FOREACH(const CTxIn &txin, tx.vin)
|
||||||
return state.DoS(0,
|
{
|
||||||
error("AcceptToMemoryPool : conflicts with existing transaction lock: %s", reason),
|
uint256 hashLocked;
|
||||||
REJECT_INVALID, "tx-lock-conflict");
|
if(instantsend.GetLockedOutPointTxHash(txin.prevout, hashLocked) && hash != hashLocked)
|
||||||
}
|
return state.DoS(10, error("AcceptToMemoryPool : Transaction %s conflicts with completed Transaction Lock %s",
|
||||||
|
hash.ToString(), hashLocked.ToString()),
|
||||||
|
REJECT_INVALID, "tx-txlock-conflict");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for conflicts with in-memory transactions
|
// Check for conflicts with in-memory transactions
|
||||||
@ -1109,6 +1126,18 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C
|
|||||||
const CTransaction *ptxConflicting = pool.mapNextTx[txin.prevout].ptx;
|
const CTransaction *ptxConflicting = pool.mapNextTx[txin.prevout].ptx;
|
||||||
if (!setConflicts.count(ptxConflicting->GetHash()))
|
if (!setConflicts.count(ptxConflicting->GetHash()))
|
||||||
{
|
{
|
||||||
|
// InstantSend txes are not replacable
|
||||||
|
if(instantsend.HasTxLockRequest(ptxConflicting->GetHash())) {
|
||||||
|
// this tx conflicts with a Transaction Lock Request candidate
|
||||||
|
return state.DoS(0, error("AcceptToMemoryPool : Transaction %s conflicts with Transaction Lock Request %s",
|
||||||
|
hash.ToString(), ptxConflicting->GetHash().ToString()),
|
||||||
|
REJECT_INVALID, "tx-txlockreq-mempool-conflict");
|
||||||
|
} else if (instantsend.HasTxLockRequest(hash)) {
|
||||||
|
// this tx is a tx lock request and it conflicts with a normal tx
|
||||||
|
return state.DoS(0, error("AcceptToMemoryPool : Transaction Lock Request %s conflicts with transaction %s",
|
||||||
|
hash.ToString(), ptxConflicting->GetHash().ToString()),
|
||||||
|
REJECT_INVALID, "txlockreq-tx-mempool-conflict");
|
||||||
|
}
|
||||||
// Allow opt-out of transaction replacement by setting
|
// Allow opt-out of transaction replacement by setting
|
||||||
// nSequence >= maxint-1 on all inputs.
|
// nSequence >= maxint-1 on all inputs.
|
||||||
//
|
//
|
||||||
@ -3701,24 +3730,33 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo
|
|||||||
REJECT_INVALID, "bad-cb-multiple");
|
REJECT_INVALID, "bad-cb-multiple");
|
||||||
|
|
||||||
|
|
||||||
// DASH : CHECK TRANSACTIONS FOR INSTANT SEND
|
// DASH : CHECK TRANSACTIONS FOR INSTANTSEND
|
||||||
|
|
||||||
if(sporkManager.IsSporkActive(SPORK_3_INSTANTSEND_BLOCK_FILTERING)) {
|
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.
|
||||||
BOOST_FOREACH(const CTransaction& tx, block.vtx) {
|
BOOST_FOREACH(const CTransaction& tx, block.vtx) {
|
||||||
// skip coinbase, it has no inputs
|
// skip coinbase, it has no inputs
|
||||||
if (tx.IsCoinBase()) continue;
|
if (tx.IsCoinBase()) continue;
|
||||||
// LOOK FOR TRANSACTION LOCK IN OUR MAP OF INPUTS
|
// LOOK FOR TRANSACTION LOCK IN OUR MAP OF OUTPOINTS
|
||||||
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
|
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
|
||||||
if(mapLockedInputs.count(txin.prevout) && mapLockedInputs[txin.prevout] != tx.GetHash()) {
|
uint256 hashLocked;
|
||||||
|
if(instantsend.GetLockedOutPointTxHash(txin.prevout, hashLocked) && hashLocked != tx.GetHash()) {
|
||||||
|
// Every node which relayed this block to us must invalidate it
|
||||||
|
// but they probably need more data.
|
||||||
|
// Relay corresponding transaction lock request and all its votes
|
||||||
|
// to let other nodes complete the lock.
|
||||||
|
instantsend.Relay(hashLocked);
|
||||||
mapRejectedBlocks.insert(make_pair(block.GetHash(), GetTime()));
|
mapRejectedBlocks.insert(make_pair(block.GetHash(), GetTime()));
|
||||||
LogPrintf("CheckBlock(DASH): found conflicting transaction with transaction lock %s %s\n", mapLockedInputs[txin.prevout].ToString(), tx.GetHash().ToString());
|
return state.DoS(0, error("CheckBlock(DASH): transaction %s conflicts with transaction lock %s",
|
||||||
return state.DoS(0, error("CheckBlock(DASH): found conflicting transaction with transaction lock"),
|
tx.GetHash().ToString(), hashLocked.ToString()),
|
||||||
REJECT_INVALID, "conflicting-tx-ix");
|
REJECT_INVALID, "conflict-tx-lock");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LogPrintf("CheckBlock(DASH): skipping transaction locking checks\n");
|
LogPrintf("CheckBlock(DASH): spork is off, skipping transaction locking checks\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// END DASH
|
// END DASH
|
||||||
@ -4901,10 +4939,10 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
|||||||
We want to only update the time on new hits, so that we can time out appropriately if needed.
|
We want to only update the time on new hits, so that we can time out appropriately if needed.
|
||||||
*/
|
*/
|
||||||
case MSG_TXLOCK_REQUEST:
|
case MSG_TXLOCK_REQUEST:
|
||||||
return mapLockRequestAccepted.count(inv.hash) || mapLockRequestRejected.count(inv.hash);
|
return instantsend.AlreadyHave(inv.hash);
|
||||||
|
|
||||||
case MSG_TXLOCK_VOTE:
|
case MSG_TXLOCK_VOTE:
|
||||||
return mapTxLockVotes.count(inv.hash);
|
return instantsend.AlreadyHave(inv.hash);
|
||||||
|
|
||||||
case MSG_SPORK:
|
case MSG_SPORK:
|
||||||
return mapSporks.count(inv.hash);
|
return mapSporks.count(inv.hash);
|
||||||
@ -5063,20 +5101,22 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!pushed && inv.type == MSG_TXLOCK_REQUEST) {
|
if (!pushed && inv.type == MSG_TXLOCK_REQUEST) {
|
||||||
if(mapLockRequestAccepted.count(inv.hash)) {
|
CTxLockRequest txLockRequest;
|
||||||
|
if(instantsend.GetTxLockRequest(inv.hash, txLockRequest)) {
|
||||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
ss.reserve(1000);
|
ss.reserve(1000);
|
||||||
ss << mapLockRequestAccepted[inv.hash];
|
ss << txLockRequest;
|
||||||
pfrom->PushMessage(NetMsgType::TXLOCKREQUEST, ss);
|
pfrom->PushMessage(NetMsgType::TXLOCKREQUEST, ss);
|
||||||
pushed = true;
|
pushed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pushed && inv.type == MSG_TXLOCK_VOTE) {
|
if (!pushed && inv.type == MSG_TXLOCK_VOTE) {
|
||||||
if(mapTxLockVotes.count(inv.hash)) {
|
CTxLockVote vote;
|
||||||
|
if(instantsend.GetTxLockVote(inv.hash, vote)) {
|
||||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
ss.reserve(1000);
|
ss.reserve(1000);
|
||||||
ss << mapTxLockVotes[inv.hash];
|
ss << vote;
|
||||||
pfrom->PushMessage(NetMsgType::TXLOCKVOTE, ss);
|
pfrom->PushMessage(NetMsgType::TXLOCKVOTE, ss);
|
||||||
pushed = true;
|
pushed = true;
|
||||||
}
|
}
|
||||||
@ -5687,13 +5727,16 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
|||||||
vector<uint256> vWorkQueue;
|
vector<uint256> vWorkQueue;
|
||||||
vector<uint256> vEraseQueue;
|
vector<uint256> vEraseQueue;
|
||||||
CTransaction tx;
|
CTransaction tx;
|
||||||
|
CTxLockRequest txLockRequest;
|
||||||
CDarksendBroadcastTx dstx;
|
CDarksendBroadcastTx dstx;
|
||||||
int nInvType = MSG_TX;
|
int nInvType = MSG_TX;
|
||||||
|
|
||||||
|
// Read data and assign inv type
|
||||||
if(strCommand == NetMsgType::TX) {
|
if(strCommand == NetMsgType::TX) {
|
||||||
vRecv >> tx;
|
vRecv >> tx;
|
||||||
} else if(strCommand == NetMsgType::TXLOCKREQUEST) {
|
} else if(strCommand == NetMsgType::TXLOCKREQUEST) {
|
||||||
vRecv >> tx;
|
vRecv >> txLockRequest;
|
||||||
|
tx = txLockRequest;
|
||||||
nInvType = MSG_TXLOCK_REQUEST;
|
nInvType = MSG_TXLOCK_REQUEST;
|
||||||
} else if (strCommand == NetMsgType::DSTX) {
|
} else if (strCommand == NetMsgType::DSTX) {
|
||||||
vRecv >> dstx;
|
vRecv >> dstx;
|
||||||
@ -5705,7 +5748,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
|||||||
pfrom->AddInventoryKnown(inv);
|
pfrom->AddInventoryKnown(inv);
|
||||||
pfrom->setAskFor.erase(inv.hash);
|
pfrom->setAskFor.erase(inv.hash);
|
||||||
|
|
||||||
if (strCommand == NetMsgType::DSTX) {
|
// Process custom logic, no matter if tx will be accepted to mempool later or not
|
||||||
|
if (strCommand == NetMsgType::TXLOCKREQUEST) {
|
||||||
|
if(!instantsend.ProcessTxLockRequest(txLockRequest)) {
|
||||||
|
LogPrint("instantsend", "TXLOCKREQUEST -- failed %s\n", txLockRequest.GetHash().ToString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (strCommand == NetMsgType::DSTX) {
|
||||||
uint256 hashTx = tx.GetHash();
|
uint256 hashTx = tx.GetHash();
|
||||||
|
|
||||||
if(mapDarksendBroadcastTxes.count(hashTx)) {
|
if(mapDarksendBroadcastTxes.count(hashTx)) {
|
||||||
@ -5745,11 +5794,15 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
|||||||
|
|
||||||
if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs))
|
if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs))
|
||||||
{
|
{
|
||||||
// Process custom txes
|
// Process custom txes, this changes AlreadyHave to "true"
|
||||||
if (strCommand == NetMsgType::DSTX) {
|
if (strCommand == NetMsgType::DSTX) {
|
||||||
|
LogPrintf("DSTX -- Masternode transaction accepted, txid=%s, peer=%d\n",
|
||||||
|
tx.GetHash().ToString(), pfrom->id);
|
||||||
mapDarksendBroadcastTxes.insert(make_pair(tx.GetHash(), dstx));
|
mapDarksendBroadcastTxes.insert(make_pair(tx.GetHash(), dstx));
|
||||||
} else if (strCommand == NetMsgType::TXLOCKREQUEST) {
|
} else if (strCommand == NetMsgType::TXLOCKREQUEST) {
|
||||||
if(!ProcessTxLockRequest(pfrom, tx)) return false;
|
LogPrintf("TXLOCKREQUEST -- Transaction Lock Request accepted, txid=%s, peer=%d\n",
|
||||||
|
tx.GetHash().ToString(), pfrom->id);
|
||||||
|
instantsend.AcceptLockRequest(txLockRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
mempool.check(pcoinsTip);
|
mempool.check(pcoinsTip);
|
||||||
@ -5828,18 +5881,17 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
|||||||
assert(recentRejects);
|
assert(recentRejects);
|
||||||
recentRejects->insert(tx.GetHash());
|
recentRejects->insert(tx.GetHash());
|
||||||
|
|
||||||
if (strCommand == NetMsgType::TXLOCKREQUEST && !AlreadyHave(inv)) { // i.e. AcceptToMemoryPool failed
|
if (strCommand == NetMsgType::TXLOCKREQUEST && !AlreadyHave(inv)) {
|
||||||
mapLockRequestRejected.insert(std::make_pair(tx.GetHash(), tx));
|
// 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.
|
||||||
|
|
||||||
// can we get the conflicting transaction as proof?
|
// It's the first time we failed for this tx lock request,
|
||||||
|
// this should switch AlreadyHave to "true".
|
||||||
LogPrintf("TXLOCKREQUEST -- Transaction Lock Request: %s %s : rejected %s\n",
|
instantsend.RejectLockRequest(txLockRequest);
|
||||||
pfrom->addr.ToString(), pfrom->cleanSubVer,
|
// this lets other nodes to create lock request candidate i.e.
|
||||||
tx.GetHash().ToString()
|
// this allows multiple conflicting lock requests to compete for votes
|
||||||
);
|
RelayTransaction(tx);
|
||||||
|
|
||||||
LockTransactionInputs(tx);
|
|
||||||
ResolveConflicts(tx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pfrom->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {
|
if (pfrom->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {
|
||||||
@ -6259,7 +6311,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
|||||||
darkSendPool.ProcessMessage(pfrom, strCommand, vRecv);
|
darkSendPool.ProcessMessage(pfrom, strCommand, vRecv);
|
||||||
mnodeman.ProcessMessage(pfrom, strCommand, vRecv);
|
mnodeman.ProcessMessage(pfrom, strCommand, vRecv);
|
||||||
mnpayments.ProcessMessage(pfrom, strCommand, vRecv);
|
mnpayments.ProcessMessage(pfrom, strCommand, vRecv);
|
||||||
ProcessMessageInstantSend(pfrom, strCommand, vRecv);
|
instantsend.ProcessMessage(pfrom, strCommand, vRecv);
|
||||||
sporkManager.ProcessSpork(pfrom, strCommand, vRecv);
|
sporkManager.ProcessSpork(pfrom, strCommand, vRecv);
|
||||||
masternodeSync.ProcessMessage(pfrom, strCommand, vRecv);
|
masternodeSync.ProcessMessage(pfrom, strCommand, vRecv);
|
||||||
governance.ProcessMessage(pfrom, strCommand, vRecv);
|
governance.ProcessMessage(pfrom, strCommand, vRecv);
|
||||||
|
@ -291,6 +291,7 @@ void PruneAndFlush();
|
|||||||
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
|
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
|
||||||
bool* pfMissingInputs, bool fOverrideMempoolLimit=false, bool fRejectAbsurdFee=false, bool fDryRun=false);
|
bool* pfMissingInputs, bool fOverrideMempoolLimit=false, bool fRejectAbsurdFee=false, bool fDryRun=false);
|
||||||
|
|
||||||
|
int GetUTXOHeight(const COutPoint& outpoint);
|
||||||
int GetInputAge(const CTxIn &txin);
|
int GetInputAge(const CTxIn &txin);
|
||||||
int GetInputAgeIX(const uint256 &nTXHash, const CTxIn &txin);
|
int GetInputAgeIX(const uint256 &nTXHash, const CTxIn &txin);
|
||||||
int GetIXConfirmations(const uint256 &nTXHash);
|
int GetIXConfirmations(const uint256 &nTXHash);
|
||||||
|
@ -2111,10 +2111,11 @@ void RelayTransaction(const CTransaction& tx)
|
|||||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
ss.reserve(10000);
|
ss.reserve(10000);
|
||||||
uint256 hash = tx.GetHash();
|
uint256 hash = tx.GetHash();
|
||||||
|
CTxLockRequest txLockRequest;
|
||||||
if(mapDarksendBroadcastTxes.count(hash)) { // MSG_DSTX
|
if(mapDarksendBroadcastTxes.count(hash)) { // MSG_DSTX
|
||||||
ss << mapDarksendBroadcastTxes[hash];
|
ss << mapDarksendBroadcastTxes[hash];
|
||||||
} else if(mapLockRequestAccepted.count(hash)) { // MSG_TXLOCK_REQUEST
|
} else if(instantsend.GetTxLockRequest(hash, txLockRequest)) { // MSG_TXLOCK_REQUEST
|
||||||
ss << mapLockRequestAccepted[hash];
|
ss << txLockRequest;
|
||||||
} else { // MSG_TX
|
} else { // MSG_TX
|
||||||
ss << tx;
|
ss << tx;
|
||||||
}
|
}
|
||||||
@ -2125,7 +2126,7 @@ void RelayTransaction(const CTransaction& tx, const CDataStream& ss)
|
|||||||
{
|
{
|
||||||
uint256 hash = tx.GetHash();
|
uint256 hash = tx.GetHash();
|
||||||
int nInv = mapDarksendBroadcastTxes.count(hash) ? MSG_DSTX :
|
int nInv = mapDarksendBroadcastTxes.count(hash) ? MSG_DSTX :
|
||||||
(mapLockRequestAccepted.count(hash) ? MSG_TXLOCK_REQUEST : MSG_TX);
|
(instantsend.HasTxLockRequest(hash) ? MSG_TXLOCK_REQUEST : MSG_TX);
|
||||||
CInv inv(nInv, hash);
|
CInv inv(nInv, hash);
|
||||||
{
|
{
|
||||||
LOCK(cs_mapRelay);
|
LOCK(cs_mapRelay);
|
||||||
|
@ -585,6 +585,10 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
|
|||||||
nBytesInputs += 148; // in all error cases, simply assume 148 here
|
nBytesInputs += 148; // in all error cases, simply assume 148 here
|
||||||
}
|
}
|
||||||
else nBytesInputs += 148;
|
else nBytesInputs += 148;
|
||||||
|
|
||||||
|
// Add inputs to calculate InstantSend Fee later
|
||||||
|
if(coinControl->fUseInstantSend)
|
||||||
|
txDummy.vin.push_back(CTxIn());
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculation
|
// calculation
|
||||||
@ -609,7 +613,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
|
|||||||
nPayFee = coinControl->nMinimumTotalFee;
|
nPayFee = coinControl->nMinimumTotalFee;
|
||||||
|
|
||||||
// InstantSend Fee
|
// InstantSend Fee
|
||||||
if (coinControl->fUseInstantSend) nPayFee = std::max(nPayFee, INSTANTSEND_MIN_FEE);
|
if (coinControl->fUseInstantSend) nPayFee = std::max(nPayFee, CTxLockRequest(txDummy).GetMinFee());
|
||||||
|
|
||||||
// Allow free? (require at least hard-coded threshold and default to that if no estimate)
|
// Allow free? (require at least hard-coded threshold and default to that if no estimate)
|
||||||
double dPriorityNeeded = std::max(mempoolEstimatePriority, AllowFreeThreshold());
|
double dPriorityNeeded = std::max(mempoolEstimatePriority, AllowFreeThreshold());
|
||||||
|
@ -41,7 +41,6 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx)
|
|||||||
|
|
||||||
QString strTxStatus;
|
QString strTxStatus;
|
||||||
bool fOffline = (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60) && (wtx.GetRequestCount() == 0);
|
bool fOffline = (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60) && (wtx.GetRequestCount() == 0);
|
||||||
int nSignatures = GetTransactionLockSignatures(wtx.GetHash());
|
|
||||||
|
|
||||||
if (fOffline) {
|
if (fOffline) {
|
||||||
strTxStatus = tr("%1/offline").arg(nDepth);
|
strTxStatus = tr("%1/offline").arg(nDepth);
|
||||||
@ -51,14 +50,16 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx)
|
|||||||
strTxStatus = tr("%1 confirmations").arg(nDepth);
|
strTxStatus = tr("%1 confirmations").arg(nDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(nSignatures < 0) return strTxStatus; // regular tx
|
if(!instantsend.HasTxLockRequest(wtx.GetHash())) return strTxStatus; // regular tx
|
||||||
|
|
||||||
|
int nSignatures = instantsend.GetTransactionLockSignatures(wtx.GetHash());
|
||||||
|
int nSignaturesMax = CTxLockRequest(wtx).GetMaxSignatures();
|
||||||
// InstantSend
|
// InstantSend
|
||||||
strTxStatus += " (";
|
strTxStatus += " (";
|
||||||
if(nSignatures >= INSTANTSEND_SIGNATURES_REQUIRED) {
|
if(instantsend.IsLockedInstantSendTransaction(wtx.GetHash())) {
|
||||||
strTxStatus += tr("verified via InstantSend");
|
strTxStatus += tr("verified via InstantSend");
|
||||||
} else if(!IsTransactionLockTimedOut(wtx.GetHash())) {
|
} else if(!instantsend.IsTxLockRequestTimedOut(wtx.GetHash())) {
|
||||||
strTxStatus += tr("InstantSend verification in progress - %1 of %2 signatures").arg(nSignatures).arg(INSTANTSEND_SIGNATURES_TOTAL);
|
strTxStatus += tr("InstantSend verification in progress - %1 of %2 signatures").arg(nSignatures).arg(nSignaturesMax);
|
||||||
} else {
|
} else {
|
||||||
strTxStatus += tr("InstantSend verification failed");
|
strTxStatus += tr("InstantSend verification failed");
|
||||||
}
|
}
|
||||||
|
@ -307,10 +307,17 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
|
|||||||
if (fSubtractFeeFromAmount && fCreated)
|
if (fSubtractFeeFromAmount && fCreated)
|
||||||
transaction.reassignAmounts(nChangePosRet);
|
transaction.reassignAmounts(nChangePosRet);
|
||||||
|
|
||||||
if(recipients[0].fUseInstantSend && newTx->GetValueOut() > sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE)*COIN){
|
if(recipients[0].fUseInstantSend) {
|
||||||
Q_EMIT message(tr("Send Coins"), tr("InstantSend doesn't support sending values that high yet. Transactions are currently limited to %1 DASH.").arg(sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE)),
|
if(newTx->GetValueOut() > sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE)*COIN) {
|
||||||
CClientUIInterface::MSG_ERROR);
|
Q_EMIT message(tr("Send Coins"), tr("InstantSend doesn't support sending values that high yet. Transactions are currently limited to %1 DASH.").arg(sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE)),
|
||||||
return TransactionCreationFailed;
|
CClientUIInterface::MSG_ERROR);
|
||||||
|
return TransactionCreationFailed;
|
||||||
|
}
|
||||||
|
if(newTx->vin.size() > CTxLockRequest::MAX_INPUTS) {
|
||||||
|
Q_EMIT message(tr("Send Coins"), tr("InstantSend doesn't support transactions with more than %1 inputs.").arg(CTxLockRequest::MAX_INPUTS),
|
||||||
|
CClientUIInterface::MSG_ERROR);
|
||||||
|
return TransactionCreationFailed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!fCreated)
|
if(!fCreated)
|
||||||
|
@ -871,12 +871,8 @@ UniValue sendrawtransaction(const UniValue& params, bool fHelp)
|
|||||||
} else if (fHaveChain) {
|
} else if (fHaveChain) {
|
||||||
throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain");
|
throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain");
|
||||||
}
|
}
|
||||||
if (fInstantSend) {
|
if (fInstantSend && !instantsend.ProcessTxLockRequest(tx)) {
|
||||||
if (!IsInstantSendTxValid(tx)) {
|
throw JSONRPCError(RPC_TRANSACTION_ERROR, "Not a valid InstantSend transaction, see debug.log for more info");
|
||||||
throw JSONRPCError(RPC_TRANSACTION_ERROR, "Not a valid InstantSend transaction");
|
|
||||||
}
|
|
||||||
mapLockRequestAccepted.insert(make_pair(hashTx, tx));
|
|
||||||
CreateTxLockCandidate(tx);
|
|
||||||
}
|
}
|
||||||
RelayTransaction(tx);
|
RelayTransaction(tx);
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
* network protocol versioning
|
* network protocol versioning
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static const int PROTOCOL_VERSION = 70204;
|
static const int PROTOCOL_VERSION = 70205;
|
||||||
|
|
||||||
//! initial proto version, to be increased after version/verack negotiation
|
//! initial proto version, to be increased after version/verack negotiation
|
||||||
static const int INIT_PROTO_VERSION = 209;
|
static const int INIT_PROTO_VERSION = 209;
|
||||||
|
@ -1472,9 +1472,8 @@ bool CWalletTx::RelayWalletTransaction(std::string strCommand)
|
|||||||
uint256 hash = GetHash();
|
uint256 hash = GetHash();
|
||||||
LogPrintf("Relaying wtx %s\n", hash.ToString());
|
LogPrintf("Relaying wtx %s\n", hash.ToString());
|
||||||
|
|
||||||
if(strCommand == NetMsgType::TXLOCKREQUEST){
|
if(strCommand == NetMsgType::TXLOCKREQUEST) {
|
||||||
mapLockRequestAccepted.insert(make_pair(hash, (CTransaction)*this));
|
instantsend.ProcessTxLockRequest(((CTxLockRequest)*this));
|
||||||
CreateTxLockCandidate(((CTransaction)*this));
|
|
||||||
}
|
}
|
||||||
RelayTransaction((CTransaction)*this);
|
RelayTransaction((CTransaction)*this);
|
||||||
return true;
|
return true;
|
||||||
@ -2098,8 +2097,8 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
int nDepth = pcoin->GetDepthInMainChain(false);
|
int nDepth = pcoin->GetDepthInMainChain(false);
|
||||||
// do not use IX for inputs that have less then 6 blockchain confirmations
|
// do not use IX for inputs that have less then INSTANTSEND_CONFIRMATIONS_REQUIRED blockchain confirmations
|
||||||
if (fUseInstantSend && nDepth < 6)
|
if (fUseInstantSend && nDepth < INSTANTSEND_CONFIRMATIONS_REQUIRED)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// We should not consider coins which aren't at least in our mempool
|
// We should not consider coins which aren't at least in our mempool
|
||||||
@ -2358,6 +2357,10 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*
|
|||||||
nValueRet += out.tx->vout[out.i].nValue;
|
nValueRet += out.tx->vout[out.i].nValue;
|
||||||
setCoinsRet.insert(make_pair(out.tx, out.i));
|
setCoinsRet.insert(make_pair(out.tx, out.i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(fUseInstantSend && setCoinsRet.size() > CTxLockRequest::MAX_INPUTS)
|
||||||
|
return false;
|
||||||
|
|
||||||
return (nValueRet >= nTargetValue);
|
return (nValueRet >= nTargetValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2885,7 +2888,7 @@ bool CWallet::ConvertList(std::vector<CTxIn> vecTxIn, std::vector<CAmount>& vecA
|
|||||||
bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet,
|
bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet,
|
||||||
int& nChangePosRet, std::string& strFailReason, const CCoinControl* coinControl, bool sign, AvailableCoinsType nCoinType, bool fUseInstantSend)
|
int& nChangePosRet, std::string& strFailReason, const CCoinControl* coinControl, bool sign, AvailableCoinsType nCoinType, bool fUseInstantSend)
|
||||||
{
|
{
|
||||||
CAmount nFeePay = fUseInstantSend ? INSTANTSEND_MIN_FEE : 0;
|
CAmount nFeePay = fUseInstantSend ? CTxLockRequest().GetMinFee() : 0;
|
||||||
|
|
||||||
CAmount nValue = 0;
|
CAmount nValue = 0;
|
||||||
unsigned int nSubtractFeeFromAmount = 0;
|
unsigned int nSubtractFeeFromAmount = 0;
|
||||||
@ -3000,19 +3003,26 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
|
|||||||
|
|
||||||
if (!SelectCoins(nValueToSelect, setCoins, nValueIn, coinControl, nCoinType, fUseInstantSend))
|
if (!SelectCoins(nValueToSelect, setCoins, nValueIn, coinControl, nCoinType, fUseInstantSend))
|
||||||
{
|
{
|
||||||
if (nCoinType == ALL_COINS) {
|
if (nCoinType == ONLY_NOT1000IFMN) {
|
||||||
strFailReason = _("Insufficient funds.");
|
|
||||||
} else if (nCoinType == ONLY_NOT1000IFMN) {
|
|
||||||
strFailReason = _("Unable to locate enough funds for this transaction that are not equal 1000 DASH.");
|
strFailReason = _("Unable to locate enough funds for this transaction that are not equal 1000 DASH.");
|
||||||
} else if (nCoinType == ONLY_NONDENOMINATED_NOT1000IFMN) {
|
} else if (nCoinType == ONLY_NONDENOMINATED_NOT1000IFMN) {
|
||||||
strFailReason = _("Unable to locate enough PrivateSend non-denominated funds for this transaction that are not equal 1000 DASH.");
|
strFailReason = _("Unable to locate enough PrivateSend non-denominated funds for this transaction that are not equal 1000 DASH.");
|
||||||
} else {
|
} else if (nCoinType == ONLY_DENOMINATED) {
|
||||||
strFailReason = _("Unable to locate enough PrivateSend denominated funds for this transaction.");
|
strFailReason = _("Unable to locate enough PrivateSend denominated funds for this transaction.");
|
||||||
strFailReason += " " + _("PrivateSend uses exact denominated amounts to send funds, you might simply need to anonymize some more coins.");
|
strFailReason += " " + _("PrivateSend uses exact denominated amounts to send funds, you might simply need to anonymize some more coins.");
|
||||||
|
} else if (nValueIn < nValueToSelect) {
|
||||||
|
strFailReason = _("Insufficient funds.");
|
||||||
}
|
}
|
||||||
|
if (fUseInstantSend) {
|
||||||
if(fUseInstantSend){
|
size_t nMaxInputs = CTxLockRequest::MAX_INPUTS;
|
||||||
strFailReason += " " + _("InstantSend requires inputs with at least 6 confirmations, you might need to wait a few minutes and try again.");
|
if(setCoins.size() > nMaxInputs) {
|
||||||
|
strFailReason += " " + strprintf(_("InstantSend doesn't support transactions with more than %d inputs."), nMaxInputs);
|
||||||
|
} else if (nValueIn > sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE)*COIN) {
|
||||||
|
strFailReason += " " + strprintf(_("InstantSend doesn't support sending values that high yet. Transactions are currently limited to %1 DASH."), sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE));
|
||||||
|
} else {
|
||||||
|
// could be not true but most likely that's the reason
|
||||||
|
strFailReason += " " + strprintf(_("InstantSend requires inputs with at least %d confirmations, you might need to wait a few minutes and try again."), INSTANTSEND_CONFIRMATIONS_REQUIRED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -3205,6 +3215,9 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
|
|||||||
if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) {
|
if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) {
|
||||||
nFeeNeeded = coinControl->nMinimumTotalFee;
|
nFeeNeeded = coinControl->nMinimumTotalFee;
|
||||||
}
|
}
|
||||||
|
if(fUseInstantSend) {
|
||||||
|
nFeeNeeded = std::max(nFeeNeeded, CTxLockRequest(txNew).GetMinFee());
|
||||||
|
}
|
||||||
|
|
||||||
// If we made it here and we aren't even able to meet the relay fee on the next pass, give up
|
// If we made it here and we aren't even able to meet the relay fee on the next pass, give up
|
||||||
// because we must be at the maximum allowed fee.
|
// because we must be at the maximum allowed fee.
|
||||||
@ -4045,7 +4058,7 @@ int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet, bool enableIX)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(enableIX && nResult < 6 && IsLockedInstandSendTransaction(GetHash()))
|
if(enableIX && nResult < 6 && instantsend.IsLockedInstantSendTransaction(GetHash()))
|
||||||
return nInstantSendDepth + nResult;
|
return nInstantSendDepth + nResult;
|
||||||
|
|
||||||
return nResult;
|
return nResult;
|
||||||
|
Loading…
Reference in New Issue
Block a user