diff --git a/doc/instantsend.md b/doc/instantsend.md index d85cbf139..bea11735c 100644 --- a/doc/instantsend.md +++ b/doc/instantsend.md @@ -25,56 +25,4 @@ When a wallet InstantSend transaction is successfully locked a shell command pro #### RPC -Details pertaining to an observed "Transaction Lock" can also be retrieved through RPC, it’s important however to understand the underlying mechanism. - -By default, the Dash Core daemon will launch using the following constant: - -``` -static const int DEFAULT_INSTANTSEND_DEPTH = 5; -``` - -This value can be overridden by passing the following argument to the Dash Core daemon: - -``` --instantsenddepth= -``` - -The key thing to understand is that this value indicates the number of "confirmations" a successful Transaction Lock represents. When Wallet RPC commands which support `minconf` and `addlockconf` parameters (such as `listreceivedbyaddress`) are performed and `addlockconf` is `true`, then `instantsenddepth` attribute is taken into account when returning information about the transaction. In this case the value in `confirmations` field you see through RPC is showing the number of `"Blockchain Confirmations" + "InstantSend Depth"` (assuming the funds were sent via InstantSend). - -There is also a field named `instantlock` (that is present in commands such as `listsinceblock`). The value in this field indicates whether a given transaction is locked via InstantSend. - -**Examples** - -1. `listreceivedbyaddress 0 true` - * InstantSend transaction just occurred: - * confirmations: 5 - * InstantSend transaction received one confirmation from blockchain: - * confirmations: 6 - * non-InstantSend transaction just occurred: - * confirmations: 0 - * non-InstantSend transaction received one confirmation from blockchain: - * confirmations: 1 - -2. `listreceivedbyaddress 0` - * InstantSend transaction just occurred: - * confirmations: 0 - * InstantSend transaction received one confirmation from blockchain: - * confirmations: 1 - * non-InstantSend transaction just occurred: - * confirmations: 0 - * non-InstantSend transaction received one confirmation from blockchain: - * confirmations: 1 - -3. `listsinceblock` - * InstantSend transaction just occurred: - * confirmations: 0 - * instantlock: true - * InstantSend transaction received one confirmation from blockchain: - * confirmations: 1 - * instantlock: true - * non-InstantSend transaction just occurred: - * confirmations: 0 - * instantlock: false - * non-InstantSend transaction received one confirmation from blockchain: - * confirmations: 1 - * instantlock: false +Details pertaining to an observed "Transaction Lock" can also be retrieved through RPC. There is a boolean field named `instantlock` which indicates whether a given transaction is locked via InstantSend. This field is present in the output of some wallet RPC commands e.g. `listsinceblock`, `gettransaction` etc. as well as in the output of some mempool RPC commands e.g. `getmempoolentry` and a couple of others like `getrawmempool` (for `verbose=true` only). diff --git a/src/governance-object.cpp b/src/governance-object.cpp index 0677ac1e9..c62a28177 100644 --- a/src/governance-object.cpp +++ b/src/governance-object.cpp @@ -605,7 +605,7 @@ bool CGovernanceObject::IsCollateralValid(std::string& strError, bool& fMissingC // GET CONFIRMATIONS FOR TRANSACTION AssertLockHeld(cs_main); - int nConfirmationsIn = instantsend.GetConfirmations(nCollateralHash); + int nConfirmationsIn = 0; if (nBlockHash != uint256()) { BlockMap::iterator mi = mapBlockIndex.find(nBlockHash); if (mi != mapBlockIndex.end() && (*mi).second) { @@ -616,7 +616,8 @@ bool CGovernanceObject::IsCollateralValid(std::string& strError, bool& fMissingC } } - if(nConfirmationsIn < GOVERNANCE_FEE_CONFIRMATIONS) { + if((nConfirmationsIn < GOVERNANCE_FEE_CONFIRMATIONS) && + (!instantsend.IsLockedInstantSendTransaction(nCollateralHash))){ strError = strprintf("Collateral requires at least %d confirmations to be relayed throughout the network (it has only %d)", GOVERNANCE_FEE_CONFIRMATIONS, nConfirmationsIn); if (nConfirmationsIn >= GOVERNANCE_MIN_RELAY_FEE_CONFIRMATIONS) { fMissingConfirmations = true; diff --git a/src/init.cpp b/src/init.cpp index b9416bcde..cc6f38b7f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -576,7 +576,6 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageGroup(_("InstantSend options:")); strUsage += HelpMessageOpt("-enableinstantsend=", strprintf(_("Enable InstantSend, show confirmations for locked transactions (0-1, default: %u)"), 1)); - strUsage += HelpMessageOpt("-instantsenddepth=", strprintf(_("Show N confirmations for a successfully locked transaction (%u-%u, default: %u)"), MIN_INSTANTSEND_DEPTH, MAX_INSTANTSEND_DEPTH, DEFAULT_INSTANTSEND_DEPTH)); strUsage += HelpMessageOpt("-instantsendnotify=", _("Execute command when a wallet InstantSend transaction is successfully locked (%s in cmd is replaced by TxID)")); @@ -1873,11 +1872,8 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) #endif // ENABLE_WALLET fEnableInstantSend = GetBoolArg("-enableinstantsend", 1); - nInstantSendDepth = GetArg("-instantsenddepth", DEFAULT_INSTANTSEND_DEPTH); - nInstantSendDepth = std::min(std::max(nInstantSendDepth, MIN_INSTANTSEND_DEPTH), MAX_INSTANTSEND_DEPTH); LogPrintf("fLiteMode %d\n", fLiteMode); - LogPrintf("nInstantSendDepth %d\n", nInstantSendDepth); #ifdef ENABLE_WALLET LogPrintf("PrivateSend liquidityprovider: %d\n", privateSendClient.nLiquidityProvider); LogPrintf("PrivateSend rounds: %d\n", privateSendClient.nPrivateSendRounds); diff --git a/src/instantx.cpp b/src/instantx.cpp index 3f02f4794..3ede2725d 100644 --- a/src/instantx.cpp +++ b/src/instantx.cpp @@ -33,7 +33,6 @@ extern CWallet* pwalletMain; extern CTxMemPool mempool; bool fEnableInstantSend = true; -int nInstantSendDepth = DEFAULT_INSTANTSEND_DEPTH; int nCompleteTXLocks; CInstantSend instantsend; @@ -838,11 +837,6 @@ int CInstantSend::GetTransactionLockSignatures(const uint256& txHash) return -1; } -int CInstantSend::GetConfirmations(const uint256 &nTXHash) -{ - return IsLockedInstantSendTransaction(nTXHash) ? nInstantSendDepth : 0; -} - bool CInstantSend::IsTxLockCandidateTimedOut(const uint256& txHash) { if(!fEnableInstantSend) return false; diff --git a/src/instantx.h b/src/instantx.h index 3448144e1..469994439 100644 --- a/src/instantx.h +++ b/src/instantx.h @@ -26,14 +26,6 @@ extern CInstantSend instantsend; (1000/2900.0)**5 = 0.004875397277841433 */ -// The INSTANTSEND_DEPTH is the "pseudo block depth" level assigned to locked -// txs to indicate the degree of confidence in their eventual confirmation and -// inability to be double-spent (adjustable via command line argument) -static const int MIN_INSTANTSEND_DEPTH = 0; -static const int MAX_INSTANTSEND_DEPTH = 60; -/// Default number of "pseudo-confirmations" for an InstantSend tx -static const int DEFAULT_INSTANTSEND_DEPTH = 5; - static const int MIN_INSTANTSEND_PROTO_VERSION = 70208; /// For how long we are going to accept votes/locks @@ -145,8 +137,6 @@ public: bool IsLockedInstantSendTransaction(const uint256& txHash); /// Get the actual number of accepted lock signatures int GetTransactionLockSignatures(const uint256& txHash); - /// Get instantsend confirmations (only) - int GetConfirmations(const uint256 &nTXHash); /// Remove expired entries from maps void CheckAndRemove(); diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index d1d6ae3c4..e19910f2a 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -34,6 +34,8 @@ static const bool DEFAULT_SPLASHSCREEN = true; #define COLOR_TX_STATUS_DANGER QColor(200, 100, 100) /* Transaction list -- TX status decoration - default color */ #define COLOR_BLACK QColor(0, 0, 0) +/* Transaction list -- TX status decoration - LockedByInstantSend color */ +#define COLOR_TX_STATUS_LOCKED QColor(0, 128, 255) /* Tooltips longer than this (in characters) are converted into rich text, so that they can be word-wrapped. diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 21ee35593..e61667050 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -291,6 +291,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) } else { + status.lockedByInstantSend = wtx.IsLockedByInstantSend(); if (status.depth < 0) { status.status = TransactionStatus::Conflicted; diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h index 673b01736..9e6bfc377 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -42,6 +42,8 @@ public: /// Transaction counts towards available balance bool countsForBalance; + /// Transaction was locked via InstantSend + bool lockedByInstantSend; /// Sorting key based on status std::string sortKey; diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index d24087aca..db16222d9 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -484,6 +484,24 @@ QString TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool return QString(str); } +QIcon IconWithShiftedColor(const QString& filename, bool shift) +{ + if (!shift) + return QIcon(filename); + + QImage img(filename); + img = img.convertToFormat(QImage::Format_ARGB32); + for (int x = img.width(); x--; ) + { + for (int y = img.height(); y--; ) + { + const QRgb rgb = img.pixel(x, y); + img.setPixel(x, y, qRgba(qRed(rgb), qGreen(rgb), qBlue(rgb)+128, qAlpha(rgb))); + } + } + return QIcon(QPixmap::fromImage(img)); +} + QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx) const { QString theme = GUIUtil::getThemeName(); @@ -495,17 +513,19 @@ QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx) case TransactionStatus::Offline: return COLOR_TX_STATUS_OFFLINE; case TransactionStatus::Unconfirmed: + // TODO: use special icon for InstantSend 0-conf return QIcon(":/icons/" + theme + "/transaction_0"); case TransactionStatus::Abandoned: return QIcon(":/icons/" + theme + "/transaction_abandoned"); case TransactionStatus::Confirming: switch(wtx->status.depth) { - case 1: return QIcon(":/icons/" + theme + "/transaction_1"); - case 2: return QIcon(":/icons/" + theme + "/transaction_2"); - case 3: return QIcon(":/icons/" + theme + "/transaction_3"); - case 4: return QIcon(":/icons/" + theme + "/transaction_4"); - default: return QIcon(":/icons/" + theme + "/transaction_5"); + // TODO: use special icons for InstantSend instead of color shifting + case 1: return IconWithShiftedColor(":/icons/" + theme + "/transaction_1", wtx->status.lockedByInstantSend); + case 2: return IconWithShiftedColor(":/icons/" + theme + "/transaction_2", wtx->status.lockedByInstantSend); + case 3: return IconWithShiftedColor(":/icons/" + theme + "/transaction_3", wtx->status.lockedByInstantSend); + case 4: return IconWithShiftedColor(":/icons/" + theme + "/transaction_4", wtx->status.lockedByInstantSend); + default: return IconWithShiftedColor(":/icons/" + theme + "/transaction_5", wtx->status.lockedByInstantSend); }; case TransactionStatus::Confirmed: return QIcon(":/icons/" + theme + "/transaction_confirmed"); @@ -608,6 +628,10 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const { return COLOR_TX_STATUS_DANGER; } + if(rec->status.lockedByInstantSend) + { + return COLOR_TX_STATUS_LOCKED; + } // Non-confirmed (but not immature) as transactions are grey if(!rec->status.countsForBalance && rec->status.status != TransactionStatus::Immature) { diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index d5e5550b5..c71bf374e 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -754,7 +754,7 @@ bool WalletModel::transactionCanBeAbandoned(uint256 hash) const { LOCK2(cs_main, wallet->cs_wallet); const CWalletTx *wtx = wallet->GetWalletTx(hash); - if (!wtx || wtx->isAbandoned() || wtx->GetDepthInMainChain() > 0 || wtx->InMempool()) + if (!wtx || wtx->isAbandoned() || wtx->GetDepthInMainChain() > 0 || wtx->IsLockedByInstantSend() || wtx->InMempool()) return false; return true; } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 54daf6c4a..d3dd34e86 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -58,7 +58,7 @@ void EnsureWalletIsUnlocked() void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) { - int confirms = wtx.GetDepthInMainChain(false); + int confirms = wtx.GetDepthInMainChain(); bool fLocked = instantsend.IsLockedInstantSendTransaction(wtx.GetHash()); entry.push_back(Pair("confirmations", confirms)); entry.push_back(Pair("instantlock", fLocked)); @@ -657,12 +657,12 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request) if (request.fHelp || request.params.size() < 1 || request.params.size() > 3) throw std::runtime_error( - "getreceivedbyaddress \"address\" ( minconf addlockconf )\n" + "getreceivedbyaddress \"address\" ( minconf addlocked )\n" "\nReturns the total amount received by the given address in transactions with at least minconf confirmations.\n" "\nArguments:\n" "1. \"address\" (string, required) The dash address for transactions.\n" "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" - "3. addlockconf (bool, optional, default=false) Whether to add " + std::to_string(nInstantSendDepth) + " confirmations to transactions locked via InstantSend.\n" + "3. addlocked (bool, optional, default=false) Whether to include transactions locked via InstantSend.\n" "\nResult:\n" "amount (numeric) The total amount in " + CURRENCY_UNIT + " received at this address.\n" "\nExamples:\n" @@ -690,7 +690,7 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request) int nMinDepth = 1; if (request.params.size() > 1) nMinDepth = request.params[1].get_int(); - bool fAddLockConf = (request.params.size() > 2 && request.params[2].get_bool()); + bool fAddLocked = (request.params.size() > 2 && request.params[2].get_bool()); // Tally CAmount nAmount = 0; @@ -702,7 +702,7 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request) BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout) if (txout.scriptPubKey == scriptPubKey) - if (wtx.GetDepthInMainChain(fAddLockConf) >= nMinDepth) + if ((wtx.GetDepthInMainChain() >= nMinDepth) || (fAddLocked && wtx.IsLockedByInstantSend())) nAmount += txout.nValue; } @@ -717,12 +717,12 @@ UniValue getreceivedbyaccount(const JSONRPCRequest& request) if (request.fHelp || request.params.size() < 1 || request.params.size() > 3) throw std::runtime_error( - "getreceivedbyaccount \"account\" ( minconf addlockconf )\n" + "getreceivedbyaccount \"account\" ( minconf addlocked )\n" "\nDEPRECATED. Returns the total amount received by addresses with in transactions with specified minimum number of confirmations.\n" "\nArguments:\n" "1. \"account\" (string, required) The selected account, may be the default account using \"\".\n" "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" - "3. addlockconf (bool, optional, default=false) Whether to add " + std::to_string(nInstantSendDepth) + " confirmations to transactions locked via InstantSend.\n" + "3. addlocked (bool, optional, default=false) Whether to include transactions locked via InstantSend.\n" "\nResult:\n" "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this account.\n" "\nExamples:\n" @@ -742,7 +742,7 @@ UniValue getreceivedbyaccount(const JSONRPCRequest& request) int nMinDepth = 1; if (request.params.size() > 1) nMinDepth = request.params[1].get_int(); - bool fAddLockConf = (request.params.size() > 2 && request.params[2].get_bool()); + bool fAddLocked = (request.params.size() > 2 && request.params[2].get_bool()); // Get the set of pub keys assigned to account std::string strAccount = AccountFromValue(request.params[0]); @@ -760,7 +760,7 @@ UniValue getreceivedbyaccount(const JSONRPCRequest& request) { CTxDestination address; if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address)) - if (wtx.GetDepthInMainChain(fAddLockConf) >= nMinDepth) + if ((wtx.GetDepthInMainChain() >= nMinDepth) || (fAddLocked && wtx.IsLockedByInstantSend())) nAmount += txout.nValue; } } @@ -776,7 +776,7 @@ UniValue getbalance(const JSONRPCRequest& request) if (request.fHelp || request.params.size() > 4) throw std::runtime_error( - "getbalance ( \"account\" minconf addlockconf include_watchonly )\n" + "getbalance ( \"account\" minconf addlocked include_watchonly )\n" "\nIf account is not specified, returns the server's total available balance.\n" "If account is specified (DEPRECATED), returns the balance in the account.\n" "Note that the account \"\" is not the same as leaving the parameter out.\n" @@ -784,7 +784,7 @@ UniValue getbalance(const JSONRPCRequest& request) "\nArguments:\n" "1. \"account\" (string, optional) DEPRECATED. The selected account, or \"*\" for entire wallet. It may be the default account using \"\".\n" "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" - "3. addlockconf (bool, optional, default=false) Whether to add " + std::to_string(nInstantSendDepth) + " confirmations to transactions locked via InstantSend.\n" + "3. addlocked (bool, optional, default=false) Whether to add balance from transactions locked via InstantSend.\n" "4. include_watchonly (bool, optional, default=false) Also include balance in watch-only addresses (see 'importaddress')\n" "\nResult:\n" "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this account.\n" @@ -805,7 +805,7 @@ UniValue getbalance(const JSONRPCRequest& request) int nMinDepth = 1; if (request.params.size() > 1) nMinDepth = request.params[1].get_int(); - bool fAddLockConf = (request.params.size() > 2 && request.params[2].get_bool()); + bool fAddLocked = (request.params.size() > 2 && request.params[2].get_bool()); isminefilter filter = ISMINE_SPENDABLE; if(request.params.size() > 3) if(request.params[3].get_bool()) @@ -827,7 +827,7 @@ UniValue getbalance(const JSONRPCRequest& request) std::list listReceived; std::list listSent; wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount, filter); - if (wtx.GetDepthInMainChain(fAddLockConf) >= nMinDepth) + if ((wtx.GetDepthInMainChain() >= nMinDepth) || (fAddLocked && wtx.IsLockedByInstantSend())) { BOOST_FOREACH(const COutputEntry& r, listReceived) nBalance += r.amount; @@ -841,7 +841,7 @@ UniValue getbalance(const JSONRPCRequest& request) std::string strAccount = AccountFromValue(request.params[0]); - CAmount nBalance = pwalletMain->GetAccountBalance(strAccount, nMinDepth, filter, fAddLockConf); + CAmount nBalance = pwalletMain->GetAccountBalance(strAccount, nMinDepth, filter, fAddLocked); return ValueFromAmount(nBalance); } @@ -916,7 +916,7 @@ UniValue sendfrom(const JSONRPCRequest& request) if (request.fHelp || request.params.size() < 3 || request.params.size() > 7) throw std::runtime_error( - "sendfrom \"fromaccount\" \"toaddress\" amount ( minconf addlockconf \"comment\" \"comment_to\" )\n" + "sendfrom \"fromaccount\" \"toaddress\" amount ( minconf addlocked \"comment\" \"comment_to\" )\n" "\nDEPRECATED (use sendtoaddress). Sent an amount from an account to a dash address." + HelpRequiringPassphrase() + "\n" "\nArguments:\n" @@ -927,7 +927,7 @@ UniValue sendfrom(const JSONRPCRequest& request) "2. \"toaddress\" (string, required) The dash address to send funds to.\n" "3. amount (numeric or string, required) The amount in " + CURRENCY_UNIT + " (transaction fee is added on top).\n" "4. minconf (numeric, optional, default=1) Only use funds with at least this many confirmations.\n" - "5. addlockconf (bool, optional, default=false) Whether to add " + std::to_string(nInstantSendDepth) + " confirmations to transactions locked via InstantSend.\n" + "5. addlocked (bool, optional, default=false) Whether to include transactions locked via InstantSend.\n" "6. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" " This is not part of the transaction, just kept in your wallet.\n" "7. \"comment_to\" (string, optional) An optional comment to store the name of the person or organization \n" @@ -956,7 +956,7 @@ UniValue sendfrom(const JSONRPCRequest& request) int nMinDepth = 1; if (request.params.size() > 3) nMinDepth = request.params[3].get_int(); - bool fAddLockConf = (request.params.size() > 4 && request.params[4].get_bool()); + bool fAddLocked = (request.params.size() > 4 && request.params[4].get_bool()); CWalletTx wtx; wtx.strFromAccount = strAccount; @@ -968,7 +968,7 @@ UniValue sendfrom(const JSONRPCRequest& request) EnsureWalletIsUnlocked(); // Check funds - CAmount nBalance = pwalletMain->GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE, fAddLockConf); + CAmount nBalance = pwalletMain->GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE, fAddLocked); if (nAmount > nBalance) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); @@ -985,7 +985,7 @@ UniValue sendmany(const JSONRPCRequest& request) if (request.fHelp || request.params.size() < 2 || request.params.size() > 8) throw std::runtime_error( - "sendmany \"fromaccount\" {\"address\":amount,...} ( minconf addlockconf \"comment\" [\"address\",...] subtractfeefromamount use_is use_ps )\n" + "sendmany \"fromaccount\" {\"address\":amount,...} ( minconf addlocked \"comment\" [\"address\",...] subtractfeefromamount use_is use_ps )\n" "\nSend multiple times. Amounts are double-precision floating point numbers." + HelpRequiringPassphrase() + "\n" "\nArguments:\n" @@ -996,7 +996,7 @@ UniValue sendmany(const JSONRPCRequest& request) " ,...\n" " }\n" "3. minconf (numeric, optional, default=1) Only use the balance confirmed at least this many times.\n" - "4. addlockconf (bool, optional, default=false) Whether to add " + std::to_string(nInstantSendDepth) + " confirmations to transactions locked via InstantSend.\n" + "4. addlocked (bool, optional, default=false) Whether to include transactions locked via InstantSend.\n" "5. \"comment\" (string, optional) A comment\n" "6. subtractfeefromamount (array, optional) A json array with addresses.\n" " The fee will be equally deducted from the amount of each selected address.\n" @@ -1030,7 +1030,7 @@ UniValue sendmany(const JSONRPCRequest& request) int nMinDepth = 1; if (request.params.size() > 2) nMinDepth = request.params[2].get_int(); - bool fAddLockConf = (request.params.size() > 3 && request.params[3].get_bool()); + bool fAddLocked = (request.params.size() > 3 && request.params[3].get_bool()); CWalletTx wtx; wtx.strFromAccount = strAccount; @@ -1076,7 +1076,7 @@ UniValue sendmany(const JSONRPCRequest& request) EnsureWalletIsUnlocked(); // Check funds - CAmount nBalance = pwalletMain->GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE, fAddLockConf); + CAmount nBalance = pwalletMain->GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE, fAddLocked); if (totalAmount > nBalance) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); @@ -1177,7 +1177,7 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts) int nMinDepth = 1; if (params.size() > 0) nMinDepth = params[0].get_int(); - bool fAddLockConf = (params.size() > 1 && params[1].get_bool()); + bool fAddLocked = (params.size() > 1 && params[1].get_bool()); // Whether to include empty accounts bool fIncludeEmpty = false; @@ -1198,8 +1198,8 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts) if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx)) continue; - int nDepth = wtx.GetDepthInMainChain(fAddLockConf); - if (nDepth < nMinDepth) + int nDepth = wtx.GetDepthInMainChain(); + if ((nDepth < nMinDepth) && !(fAddLocked && wtx.IsLockedByInstantSend())) continue; BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout) @@ -1303,11 +1303,11 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request) if (request.fHelp || request.params.size() > 4) throw std::runtime_error( - "listreceivedbyaddress ( minconf addlockconf include_empty include_watchonly)\n" + "listreceivedbyaddress ( minconf addlocked include_empty include_watchonly)\n" "\nList incoming payments grouped by receiving address.\n" "\nArguments:\n" "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n" - "2. addlockconf (bool, optional, default=false) Whether to add " + std::to_string(nInstantSendDepth) + " confirmations to transactions locked via InstantSend.\n" + "2. addlocked (bool, optional, default=false) Whether to include transactions locked via InstantSend.\n" "3. include_empty (bool, optional, default=false) Whether to include addresses that haven't received any payments.\n" "4. include_watchonly (bool, optional, default=false) Whether to include watch-only addresses (see 'importaddress').\n" @@ -1319,8 +1319,8 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request) " \"account\" : \"accountname\", (string) DEPRECATED. The account of the receiving address. The default account is \"\".\n" " \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " received by the address\n" " \"confirmations\" : n (numeric) The number of confirmations of the most recent transaction included.\n" - " If 'addlockconf' is true, the minimum number of confirmations is calculated\n" - " including additional " + std::to_string(nInstantSendDepth) + " confirmations for transactions locked via InstantSend\n" + " If 'addlocked' is true, the number of confirmations can be less than\n" + " configured for transactions locked via InstantSend\n" " \"label\" : \"label\", (string) A comment for the address/transaction, if any\n" " \"txids\": [\n" " n, (numeric) The ids of transactions received with the address \n" @@ -1348,11 +1348,11 @@ UniValue listreceivedbyaccount(const JSONRPCRequest& request) if (request.fHelp || request.params.size() > 4) throw std::runtime_error( - "listreceivedbyaccount ( minconf addlockconf include_empty include_watchonly)\n" + "listreceivedbyaccount ( minconf addlocked include_empty include_watchonly)\n" "\nDEPRECATED. List incoming payments grouped by account.\n" "\nArguments:\n" "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n" - "2. addlockconf (bool, optional, default=false) Whether to add " + std::to_string(nInstantSendDepth) + " confirmations to transactions locked via InstantSend.\n" + "2. addlocked (bool, optional, default=false) Whether to include transactions locked via InstantSend.\n" "3. include_empty (bool, optional, default=false) Whether to include accounts that haven't received any payments.\n" "4. include_watchonly (bool, optional, default=false) Whether to include watch-only addresses (see 'importaddress').\n" @@ -1423,7 +1423,7 @@ void ListTransactions(const CWalletTx& wtx, const std::string& strAccount, int n } // Received - if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) + if (listReceived.size() > 0 && ((wtx.GetDepthInMainChain() >= nMinDepth) || wtx.IsLockedByInstantSend())) { BOOST_FOREACH(const COutputEntry& r, listReceived) { @@ -1615,11 +1615,11 @@ UniValue listaccounts(const JSONRPCRequest& request) if (request.fHelp || request.params.size() > 3) throw std::runtime_error( - "listaccounts ( minconf addlockconf include_watchonly)\n" + "listaccounts ( minconf addlocked include_watchonly)\n" "\nDEPRECATED. Returns Object that has account names as keys, account balances as values.\n" "\nArguments:\n" "1. minconf (numeric, optional, default=1) Only include transactions with at least this many confirmations\n" - "2. addlockconf (bool, optional, default=false) Whether to add " + std::to_string(nInstantSendDepth) + " confirmations to transactions locked via InstantSend.\n" + "2. addlocked (bool, optional, default=false) Whether to include transactions locked via InstantSend.\n" "3. include_watchonly (bool, optional, default=false) Include balances in watch-only addresses (see 'importaddress')\n" "\nResult:\n" "{ (json object where keys are account names, and values are numeric balances\n" @@ -1642,7 +1642,7 @@ UniValue listaccounts(const JSONRPCRequest& request) int nMinDepth = 1; if (request.params.size() > 0) nMinDepth = request.params[0].get_int(); - bool fAddLockConf = (request.params.size() > 1 && request.params[1].get_bool()); + bool fAddLocked = (request.params.size() > 1 && request.params[1].get_bool()); isminefilter includeWatchonly = ISMINE_SPENDABLE; if(request.params.size() > 2) if(request.params[2].get_bool()) @@ -1661,14 +1661,14 @@ UniValue listaccounts(const JSONRPCRequest& request) std::string strSentAccount; std::list listReceived; std::list listSent; - int nDepth = wtx.GetDepthInMainChain(fAddLockConf); + int nDepth = wtx.GetDepthInMainChain(); if (wtx.GetBlocksToMaturity() > 0 || nDepth < 0) continue; wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, includeWatchonly); mapAccountBalances[strSentAccount] -= nFee; BOOST_FOREACH(const COutputEntry& s, listSent) mapAccountBalances[strSentAccount] -= s.amount; - if (nDepth >= nMinDepth) + if ((nDepth >= nMinDepth) || (fAddLocked && wtx.IsLockedByInstantSend())) { BOOST_FOREACH(const COutputEntry& r, listReceived) if (pwalletMain->mapAddressBook.count(r.destination)) @@ -1784,7 +1784,7 @@ UniValue listsinceblock(const JSONRPCRequest& request) { CWalletTx tx = (*it).second; - if (depth == -1 || tx.GetDepthInMainChain(false) < depth) + if (depth == -1 || tx.GetDepthInMainChain() < depth) ListTransactions(tx, "*", 0, true, transactions, filter); } @@ -2856,11 +2856,11 @@ static const CRPCCommand commands[] = { "wallet", "getaccountaddress", &getaccountaddress, true, {"account"} }, { "wallet", "getaccount", &getaccount, true, {"address"} }, { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, true, {"account"} }, - { "wallet", "getbalance", &getbalance, false, {"account","minconf","addlockconf","include_watchonly"} }, + { "wallet", "getbalance", &getbalance, false, {"account","minconf","addlocked","include_watchonly"} }, { "wallet", "getnewaddress", &getnewaddress, true, {"account"} }, { "wallet", "getrawchangeaddress", &getrawchangeaddress, true, {} }, - { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, false, {"account","minconf","addlockconf"} }, - { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, false, {"address","minconf","addlockconf"} }, + { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, false, {"account","minconf","addlocked"} }, + { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, false, {"address","minconf","addlocked"} }, { "wallet", "gettransaction", &gettransaction, false, {"txid","include_watchonly"} }, { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, false, {} }, { "wallet", "getwalletinfo", &getwalletinfo, false, {} }, @@ -2871,19 +2871,19 @@ static const CRPCCommand commands[] = { "wallet", "importprunedfunds", &importprunedfunds, true, {"rawtransaction","txoutproof"} }, { "wallet", "importpubkey", &importpubkey, true, {"pubkey","label","rescan"} }, { "wallet", "keypoolrefill", &keypoolrefill, true, {"newsize"} }, - { "wallet", "listaccounts", &listaccounts, false, {"minconf","addlockconf","include_watchonly"} }, + { "wallet", "listaccounts", &listaccounts, false, {"minconf","addlocked","include_watchonly"} }, { "wallet", "listaddressgroupings", &listaddressgroupings, false, {} }, { "wallet", "listaddressbalances", &listaddressbalances, false, {"minamount"} }, { "wallet", "listlockunspent", &listlockunspent, false, {} }, - { "wallet", "listreceivedbyaccount", &listreceivedbyaccount, false, {"minconf","addlockconf","include_empty","include_watchonly"} }, - { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false, {"minconf","addlockconf","include_empty","include_watchonly"} }, + { "wallet", "listreceivedbyaccount", &listreceivedbyaccount, false, {"minconf","addlocked","include_empty","include_watchonly"} }, + { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false, {"minconf","addlocked","include_empty","include_watchonly"} }, { "wallet", "listsinceblock", &listsinceblock, false, {"blockhash","target_confirmations","include_watchonly"} }, { "wallet", "listtransactions", &listtransactions, false, {"account","count","skip","include_watchonly"} }, { "wallet", "listunspent", &listunspent, false, {"minconf","maxconf","addresses","include_unsafe"} }, { "wallet", "lockunspent", &lockunspent, true, {"unlock","transactions"} }, { "wallet", "move", &movecmd, false, {"fromaccount","toaccount","amount","minconf","comment"} }, - { "wallet", "sendfrom", &sendfrom, false, {"fromaccount","toaddress","amount","minconf","addlockconf","comment","comment_to"} }, - { "wallet", "sendmany", &sendmany, false, {"fromaccount","amounts","minconf","addlockconf","comment","subtractfeefrom"} }, + { "wallet", "sendfrom", &sendfrom, false, {"fromaccount","toaddress","amount","minconf","addlocked","comment","comment_to"} }, + { "wallet", "sendmany", &sendmany, false, {"fromaccount","amounts","minconf","addlocked","comment","subtractfeefrom"} }, { "wallet", "sendtoaddress", &sendtoaddress, false, {"address","amount","comment","comment_to","subtractfeefromamount"} }, { "wallet", "setaccount", &setaccount, true, {"address","account"} }, { "wallet", "settxfee", &settxfee, true, {"amount"} }, diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 2260fc5da..6ae8b8b24 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1259,7 +1259,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx) // Can't mark abandoned if confirmed or in mempool assert(mapWallet.count(hashTx)); CWalletTx& origtx = mapWallet[hashTx]; - if (origtx.GetDepthInMainChain() > 0 || origtx.InMempool()) { + if (origtx.GetDepthInMainChain() > 0 || origtx.InMempool() || origtx.IsLockedByInstantSend()) { return false; } @@ -1945,7 +1945,7 @@ void CWallet::ReacceptWalletTransactions() int nDepth = wtx.GetDepthInMainChain(); - if (!wtx.IsCoinBase() && (nDepth == 0 && !wtx.isAbandoned())) { + if (!wtx.IsCoinBase() && (nDepth == 0 && !wtx.IsLockedByInstantSend() && !wtx.isAbandoned())) { mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx)); } } @@ -2194,7 +2194,7 @@ CAmount CWalletTx::GetDenominatedCredit(bool unconfirmed, bool fUseCache) const if (IsCoinBase() && GetBlocksToMaturity() > 0) return 0; - int nDepth = GetDepthInMainChain(false); + int nDepth = GetDepthInMainChain(); if(nDepth < 0) return 0; bool isUnconfirmed = IsTrusted() && nDepth == 0; @@ -2258,6 +2258,8 @@ bool CWalletTx::IsTrusted() const return true; if (nDepth < 0) return false; + if (IsLockedByInstantSend()) + return true; if (!bSpendZeroConfChange || !IsFromMe(ISMINE_ALL)) // using wtx's cached debit return false; @@ -2501,7 +2503,7 @@ CAmount CWallet::GetUnconfirmedBalance() const for (std::map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; - if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool()) + if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && !pcoin->IsLockedByInstantSend() && pcoin->InMempool()) nTotal += pcoin->GetAvailableCredit(); } } @@ -2546,7 +2548,7 @@ CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const for (std::map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; - if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool()) + if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && !pcoin->IsLockedByInstantSend() && pcoin->InMempool()) nTotal += pcoin->GetAvailableWatchOnlyCredit(); } } @@ -2589,7 +2591,7 @@ void CWallet::AvailableCoins(std::vector& vCoins, bool fOnlyConfirmed, if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) continue; - int nDepth = pcoin->GetDepthInMainChain(false); + int nDepth = pcoin->GetDepthInMainChain(); // do not use IX for inputs that have less then nInstantSendConfirmationsRequired blockchain confirmations if (fUseInstantSend && nDepth < nInstantSendConfirmationsRequired) continue; @@ -3277,7 +3279,7 @@ int CWallet::CountInputsWithAmount(CAmount nInputAmount) { const CWalletTx* pcoin = &(*it).second; if (pcoin->IsTrusted()){ - int nDepth = pcoin->GetDepthInMainChain(false); + int nDepth = pcoin->GetDepthInMainChain(); for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { COutput out = COutput(pcoin, i, nDepth, true, true); @@ -4315,7 +4317,7 @@ std::map CWallet::GetAddressBalances() continue; int nDepth = pcoin->GetDepthInMainChain(); - if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1)) + if ((nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1)) && !pcoin->IsLockedByInstantSend()) continue; for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) @@ -4431,13 +4433,13 @@ std::set< std::set > CWallet::GetAddressGroupings() return ret; } -CAmount CWallet::GetAccountBalance(const std::string& strAccount, int nMinDepth, const isminefilter& filter, bool fAddLockConf) +CAmount CWallet::GetAccountBalance(const std::string& strAccount, int nMinDepth, const isminefilter& filter, bool fAddLocked) { CWalletDB walletdb(strWalletFile); - return GetAccountBalance(walletdb, strAccount, nMinDepth, filter, fAddLockConf); + return GetAccountBalance(walletdb, strAccount, nMinDepth, filter, fAddLocked); } -CAmount CWallet::GetAccountBalance(CWalletDB& walletdb, const std::string& strAccount, int nMinDepth, const isminefilter& filter, bool fAddLockConf) +CAmount CWallet::GetAccountBalance(CWalletDB& walletdb, const std::string& strAccount, int nMinDepth, const isminefilter& filter, bool fAddLocked) { CAmount nBalance = 0; @@ -4445,13 +4447,13 @@ CAmount CWallet::GetAccountBalance(CWalletDB& walletdb, const std::string& strAc for (std::map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain(fAddLockConf) < 0) + if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) continue; CAmount nReceived, nSent, nFee; wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee, filter); - if (nReceived != 0 && wtx.GetDepthInMainChain(fAddLockConf) >= nMinDepth) + if (nReceived != 0 && ((wtx.GetDepthInMainChain() >= nMinDepth) || (fAddLocked && wtx.IsLockedByInstantSend()))) nBalance += nReceived; nBalance -= nSent + nFee; } @@ -5349,7 +5351,7 @@ void CMerkleTx::SetMerkleBranch(const CBlockIndex* pindex, int posInBlock) nIndex = posInBlock; } -int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet, bool enableIX) const +int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const { int nResult; @@ -5376,12 +5378,14 @@ int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet, bool enableIX) } } - if(enableIX && nResult < 6 && instantsend.IsLockedInstantSendTransaction(GetHash())) - return nInstantSendDepth + nResult; - return nResult; } +bool CMerkleTx::IsLockedByInstantSend() const +{ + return instantsend.IsLockedInstantSendTransaction(GetHash()); +} + int CMerkleTx::GetBlocksToMaturity() const { if (!IsCoinBase()) diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index ee7b9376c..3f443332f 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -271,9 +271,10 @@ public: * 0 : in memory pool, waiting to be included in a block * >=1 : this many blocks deep in the main chain */ - int GetDepthInMainChain(const CBlockIndex* &pindexRet, bool enableIX = true) const; - int GetDepthInMainChain(bool enableIX = true) const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet, enableIX); } + int GetDepthInMainChain(const CBlockIndex* &pindexRet) const; + int GetDepthInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); } bool IsInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet) > 0; } + bool IsLockedByInstantSend() const; int GetBlocksToMaturity() const; /** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */ bool AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state); @@ -977,8 +978,8 @@ public: std::set< std::set > GetAddressGroupings(); std::map GetAddressBalances(); - CAmount GetAccountBalance(const std::string& strAccount, int nMinDepth, const isminefilter& filter, bool fAddLockConf); - CAmount GetAccountBalance(CWalletDB& walletdb, const std::string& strAccount, int nMinDepth, const isminefilter& filter, bool fAddLockConf); + CAmount GetAccountBalance(const std::string& strAccount, int nMinDepth, const isminefilter& filter, bool fAddLocked); + CAmount GetAccountBalance(CWalletDB& walletdb, const std::string& strAccount, int nMinDepth, const isminefilter& filter, bool fAddLocked); std::set GetAccountAddresses(const std::string& strAccount) const; isminetype IsMine(const CTxIn& txin) const;