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:
UdjinM6 2017-01-29 12:22:14 +04:00 committed by GitHub
parent 9c5db04953
commit 60409df822
16 changed files with 1190 additions and 535 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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();

View File

@ -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);
}

View File

@ -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:
}; };

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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());

View File

@ -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");
} }

View File

@ -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)

View File

@ -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);

View File

@ -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;

View File

@ -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;