From: MASM fan Date: Sun, 16 Feb 2014 16:21:21 +0000 (+0400) Subject: Add getunconfirmedbalance() RPC call, fix balance calculation. X-Git-Tag: v0.4.4.7-nvc-bugfix3~6 X-Git-Url: https://git.novaco.in/?p=novacoin.git;a=commitdiff_plain;h=80b9f08abdbf992c6ecd759b73c852cf35c03d40 Add getunconfirmedbalance() RPC call, fix balance calculation. --- diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 2ebf313..a8079cd 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -268,6 +268,7 @@ static const CRPCCommand vRPCCommands[] = { "validateaddress", &validateaddress, true, false }, { "validatepubkey", &validatepubkey, true, false }, { "getbalance", &getbalance, false, false }, + { "getunconfirmedbalance", &getunconfirmedbalance, false, false }, { "move", &movecmd, false, false }, { "sendfrom", &sendfrom, false, false }, { "sendmany", &sendmany, false, false }, diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h index 6ab4820..d43e283 100644 --- a/src/bitcoinrpc.h +++ b/src/bitcoinrpc.h @@ -171,6 +171,7 @@ extern json_spirit::Value verifymessage(const json_spirit::Array& params, bool f extern json_spirit::Value getreceivedbyaddress(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getreceivedbyaccount(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getbalance(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getunconfirmedbalance(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value movecmd(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value sendfrom(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value sendmany(const json_spirit::Array& params, bool fHelp); diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 828d11d..d1d4eee 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -44,7 +44,12 @@ void WalletTxToJSON(const CWalletTx& wtx, Object& entry) entry.push_back(Pair("blockindex", wtx.nIndex)); entry.push_back(Pair("blocktime", (boost::int64_t)(mapBlockIndex[wtx.hashBlock]->nTime))); } - entry.push_back(Pair("txid", wtx.GetHash().GetHex())); + uint256 hash = wtx.GetHash(); + entry.push_back(Pair("txid", hash.GetHex())); + Array conflicts; + BOOST_FOREACH(const uint256& conflict, wtx.GetConflicts()) + conflicts.push_back(conflict.GetHex()); + entry.push_back(Pair("walletconflicts", conflicts)); entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime())); entry.push_back(Pair("timereceived", (boost::int64_t)wtx.nTimeReceived)); BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue) @@ -564,7 +569,7 @@ Value getbalance(const Array& params, bool fHelp) for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - if (!wtx.IsTrusted()) + if (!wtx.IsTrusted() || wtx.GetBlocksToMaturity() > 0) continue; int64 allGeneratedImmature, allGeneratedMature, allFee; @@ -594,6 +599,14 @@ Value getbalance(const Array& params, bool fHelp) return ValueFromAmount(nBalance); } +Value getunconfirmedbalance(const Array ¶ms, bool fHelp) +{ + if (fHelp || params.size() > 0) + throw runtime_error( + "getunconfirmedbalance\n" + "Returns the server's total unconfirmed balance\n"); + return ValueFromAmount(pwalletMain->GetUnconfirmedBalance()); +} Value movecmd(const Array& params, bool fHelp) { @@ -1177,6 +1190,8 @@ Value listaccounts(const Array& params, bool fHelp) string strSentAccount; list > listReceived; list > listSent; + if (wtx.GetBlocksToMaturity() > 0) + continue; wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount); mapAccountBalances[strSentAccount] -= nFee; BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent) diff --git a/src/wallet.cpp b/src/wallet.cpp index ef0b5e1..bdcbc0c 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -237,6 +237,82 @@ bool CWallet::SetMaxVersion(int nVersion) return true; } +set CWallet::GetConflicts(const uint256& txid) const +{ + set result; + LOCK(cs_wallet); + + std::map::const_iterator it = mapWallet.find(txid); + if (it == mapWallet.end()) + return result; + const CWalletTx& wtx = it->second; + + std::pair range; + + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + { + range = mapTxConflicts.equal_range(txin.prevout); + for (TxConflicts::const_iterator it = range.first; it != range.second; ++it) + result.insert(it->second); + } + return result; +} + +void CWallet::SyncMetaData(pair range) +{ + // We want all the wallet transactions in range to have the same metadata as + // the oldest (smallest nOrderPos). + // So: find smallest nOrderPos: + + int nMinOrderPos = std::numeric_limits::max(); + const CWalletTx* copyFrom = NULL; + for (TxConflicts::iterator it = range.first; it != range.second; ++it) + { + const uint256& hash = it->second; + int n = mapWallet[hash].nOrderPos; + if (n < nMinOrderPos) + { + nMinOrderPos = n; + copyFrom = &mapWallet[hash]; + } + } + // Now copy data from copyFrom to rest: + for (TxConflicts::iterator it = range.first; it != range.second; ++it) + { + const uint256& hash = it->second; + CWalletTx* copyTo = &mapWallet[hash]; + if (copyFrom == copyTo) continue; + copyTo->mapValue = copyFrom->mapValue; + copyTo->vOrderForm = copyFrom->vOrderForm; + // fTimeReceivedIsTxTime not copied on purpose + // nTimeReceived not copied on purpose + copyTo->nTimeSmart = copyFrom->nTimeSmart; + copyTo->fFromMe = copyFrom->fFromMe; + copyTo->strFromAccount = copyFrom->strFromAccount; + // vfSpent not copied on purpose + // nOrderPos not copied on purpose + // cached members not copied on purpose + } +} + +void CWallet::AddToConflicts(const uint256& wtxhash) +{ + assert(mapWallet.count(wtxhash)); + CWalletTx& thisTx = mapWallet[wtxhash]; + if (thisTx.IsCoinBase()) + return; + + BOOST_FOREACH(const CTxIn& txin, thisTx.vin) + { + mapTxConflicts.insert(make_pair(txin.prevout, wtxhash)); + + pair range; + range = mapTxConflicts.equal_range(txin.prevout); + if (range.first != range.second) + SyncMetaData(range); + } +} + bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) { if (IsCrypted()) @@ -406,9 +482,15 @@ void CWallet::MarkDirty() } } -bool CWallet::AddToWallet(const CWalletTx& wtxIn) +bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet) { uint256 hash = wtxIn.GetHash(); + if (fFromLoadWallet) + { + mapWallet[hash] = wtxIn; + AddToConflicts(hash); + } + else { LOCK(cs_wallet); // Inserts only if not already there, returns tx inserted or tx found @@ -463,9 +545,10 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) } else printf("AddToWallet() : found %s in block %s not in index\n", - hash.ToString().substr(0,10).c_str(), - wtxIn.hashBlock.ToString().c_str()); + hash.ToString().c_str(), + wtxIn.hashBlock.ToString().c_str()); } + AddToConflicts(hash); } bool fUpdated = false; @@ -888,6 +971,18 @@ void CWallet::ReacceptWalletTransactions() } } +set CWalletTx::GetConflicts() const +{ + set result; + if (pwallet != NULL) + { + uint256 myHash = GetHash(); + result = pwallet->GetConflicts(myHash); + result.erase(myHash); + } + return result; +} + void CWalletTx::RelayWalletTransaction() { CCoinsViewCache& coins = *pcoinsTip; @@ -989,7 +1084,7 @@ int64 CWallet::GetUnconfirmedBalance() const for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; - if (!pcoin->IsFinal() || !pcoin->IsTrusted()) + if (!pcoin->IsFinal() || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0)) nTotal += pcoin->GetAvailableCredit(); } } diff --git a/src/wallet.h b/src/wallet.h index ddb944e..f2d1fc0 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -82,6 +82,12 @@ private: // the maximum wallet format version: memory-only variable that specifies to what version this wallet may be upgraded int nWalletMaxVersion; + // Used to detect and report conflicted transactions: + typedef std::multimap TxConflicts; + TxConflicts mapTxConflicts; + void AddToConflicts(const uint256& wtxhash); + void SyncMetaData(std::pair); + public: mutable CCriticalSection cs_wallet; @@ -172,7 +178,7 @@ public: TxItems OrderedTxItems(std::list& acentries, std::string strAccount = ""); void MarkDirty(); - bool AddToWallet(const CWalletTx& wtxIn); + bool AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet=false); bool AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate = false, bool fFindBlock = false); bool EraseFromWallet(uint256 hash); void WalletUpdateSpent(const CTransaction& prevout, bool fBlock = false); @@ -312,6 +318,9 @@ public: // get the current wallet format (the oldest client version guaranteed to understand this wallet) int GetVersion() { return nWalletVersion; } + // get wallet transactions that conflict with given transaction (spend same outputs) + std::set GetConflicts(const uint256& txid) const; + void FixSpentCoins(int& nMismatchSpent, int64& nBalanceInQuestion, bool fCheckOnly = false); void DisableTransaction(const CTransaction &tx); @@ -702,6 +711,8 @@ public: bool AcceptWalletTransaction(bool fCheckInputs=true); void RelayWalletTransaction(); + + std::set GetConflicts() const; }; diff --git a/src/walletdb.cpp b/src/walletdb.cpp index 43730c1..14e5b37 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -219,7 +219,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, { uint256 hash; ssKey >> hash; - CWalletTx& wtx = pwallet->mapWallet[hash]; + CWalletTx wtx; ssValue >> wtx; if (wtx.CheckTransaction() && (wtx.GetHash() == hash)) wtx.BindWallet(pwallet); @@ -252,6 +252,8 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, if (wtx.nOrderPos == -1) wss.fAnyUnordered = true; + pwallet->AddToWallet(wtx, true); + //// debug print //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str()); //printf(" %12"PRI64d" %s %s %s\n",