aboutsummaryrefslogtreecommitdiff
path: root/src/wallet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet.cpp')
-rw-r--r--src/wallet.cpp584
1 files changed, 415 insertions, 169 deletions
diff --git a/src/wallet.cpp b/src/wallet.cpp
index c115a53340..1cfc6cab0c 100644
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -7,6 +7,7 @@
#include "walletdb.h"
#include "crypter.h"
#include "ui_interface.h"
+#include "base58.h"
using namespace std;
@@ -16,7 +17,16 @@ using namespace std;
// mapWallet
//
-std::vector<unsigned char> CWallet::GenerateNewKey()
+struct CompareValueOnly
+{
+ bool operator()(const pair<int64, pair<const CWalletTx*, unsigned int> >& t1,
+ const pair<int64, pair<const CWalletTx*, unsigned int> >& t2) const
+ {
+ return t1.first < t2.first;
+ }
+};
+
+CPubKey CWallet::GenerateNewKey()
{
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
@@ -44,7 +54,7 @@ bool CWallet::AddKey(const CKey& key)
return true;
}
-bool CWallet::AddCryptedKey(const vector<unsigned char> &vchPubKey, const vector<unsigned char> &vchCryptedSecret)
+bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, const vector<unsigned char> &vchCryptedSecret)
{
if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret))
return false;
@@ -274,11 +284,44 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
// Need to completely rewrite the wallet file; if we don't, bdb might keep
// bits of the unencrypted private key in slack space in the database file.
CDB::Rewrite(strWalletFile);
+
}
+ NotifyStatusChanged(this);
return true;
}
+int64 CWallet::IncOrderPosNext()
+{
+ int64 nRet = nOrderPosNext;
+ CWalletDB(strWalletFile).WriteOrderPosNext(++nOrderPosNext);
+ return nRet;
+}
+
+CWallet::TxItems CWallet::OrderedTxItems(std::list<CAccountingEntry>& acentries, std::string strAccount)
+{
+ CWalletDB walletdb(strWalletFile);
+
+ // First: get all CWalletTx and CAccountingEntry into a sorted-by-order multimap.
+ TxItems txOrdered;
+
+ // Note: maintaining indices in the database of (account,time) --> txid and (account, time) --> acentry
+ // would make this much faster for applications that do this a lot.
+ for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ {
+ CWalletTx* wtx = &((*it).second);
+ txOrdered.insert(make_pair(wtx->nOrderPos, TxPair(wtx, (CAccountingEntry*)0)));
+ }
+ acentries.clear();
+ walletdb.ListAccountCreditDebit(strAccount, acentries);
+ BOOST_FOREACH(CAccountingEntry& entry, acentries)
+ {
+ txOrdered.insert(make_pair(entry.nOrderPos, TxPair((CWalletTx*)0, &entry)));
+ }
+
+ return txOrdered;
+}
+
void CWallet::WalletUpdateSpent(const CTransaction &tx)
{
// Anytime a signature is successfully verified, it's proof the outpoint is spent.
@@ -292,12 +335,14 @@ void CWallet::WalletUpdateSpent(const CTransaction &tx)
if (mi != mapWallet.end())
{
CWalletTx& wtx = (*mi).second;
- if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n]))
+ if (txin.prevout.n >= wtx.vout.size())
+ printf("WalletUpdateSpent: bad wtx %s\n", wtx.GetHash().ToString().c_str());
+ else if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n]))
{
printf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
wtx.MarkSpent(txin.prevout.n);
wtx.WriteToDisk();
- vWalletUpdated.push_back(txin.prevout.hash);
+ NotifyTransactionChanged(this, txin.prevout.hash, CT_UPDATED);
}
}
}
@@ -324,7 +369,56 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn)
wtx.BindWallet(this);
bool fInsertedNew = ret.second;
if (fInsertedNew)
+ {
wtx.nTimeReceived = GetAdjustedTime();
+ wtx.nOrderPos = IncOrderPosNext();
+
+ wtx.nTimeSmart = wtx.nTimeReceived;
+ if (wtxIn.hashBlock != 0)
+ {
+ if (mapBlockIndex.count(wtxIn.hashBlock))
+ {
+ unsigned int latestNow = wtx.nTimeReceived;
+ unsigned int latestEntry = 0;
+ {
+ // Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future
+ int64 latestTolerated = latestNow + 300;
+ std::list<CAccountingEntry> acentries;
+ TxItems txOrdered = OrderedTxItems(acentries);
+ for (TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
+ {
+ CWalletTx *const pwtx = (*it).second.first;
+ if (pwtx == &wtx)
+ continue;
+ CAccountingEntry *const pacentry = (*it).second.second;
+ int64 nSmartTime;
+ if (pwtx)
+ {
+ nSmartTime = pwtx->nTimeSmart;
+ if (!nSmartTime)
+ nSmartTime = pwtx->nTimeReceived;
+ }
+ else
+ nSmartTime = pacentry->nTime;
+ if (nSmartTime <= latestTolerated)
+ {
+ latestEntry = nSmartTime;
+ if (nSmartTime > latestNow)
+ latestNow = nSmartTime;
+ break;
+ }
+ }
+ }
+
+ unsigned int& blocktime = mapBlockIndex[wtxIn.hashBlock]->nTime;
+ wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow));
+ }
+ else
+ printf("AddToWallet() : found %s in block %s not in index\n",
+ wtxIn.GetHash().ToString().substr(0,10).c_str(),
+ wtxIn.hashBlock.ToString().c_str());
+ }
+ }
bool fUpdated = false;
if (!fInsertedNew)
@@ -359,29 +453,26 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn)
#ifndef QT_GUI
// If default receiving address gets used, replace it with a new one
CScript scriptDefaultKey;
- scriptDefaultKey.SetBitcoinAddress(vchDefaultKey);
+ scriptDefaultKey.SetDestination(vchDefaultKey.GetID());
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
{
if (txout.scriptPubKey == scriptDefaultKey)
{
- std::vector<unsigned char> newDefaultKey;
+ CPubKey newDefaultKey;
if (GetKeyFromPool(newDefaultKey, false))
{
SetDefaultKey(newDefaultKey);
- SetAddressBookName(CBitcoinAddress(vchDefaultKey), "");
+ SetAddressBookName(vchDefaultKey.GetID(), "");
}
}
}
#endif
- // Notify UI
- vWalletUpdated.push_back(hash);
-
// since AddToWallet is called directly for self-originating transactions, check for consumption of own coins
WalletUpdateSpent(wtx);
- }
- // Refresh UI
- MainFrameRepaint();
+ // Notify UI of new or updated transaction
+ NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED);
+ }
return true;
}
@@ -456,7 +547,7 @@ int64 CWallet::GetDebit(const CTxIn &txin) const
bool CWallet::IsChange(const CTxOut& txout) const
{
- CBitcoinAddress address;
+ CTxDestination address;
// TODO: fix handling of 'change' outputs. The assumption is that any
// payment to a TX_PUBKEYHASH that is mine but isn't in the address book
@@ -465,7 +556,7 @@ bool CWallet::IsChange(const CTxOut& txout) const
// a better way of identifying which outputs are 'the send' and which are
// 'the change' will need to be implemented (maybe extend CWalletTx to remember
// which output, if any, was change).
- if (ExtractAddress(txout.scriptPubKey, address) && HaveKey(address))
+ if (ExtractDestination(txout.scriptPubKey, address) && ::IsMine(*this, address))
{
LOCK(cs_wallet);
if (!mapAddressBook.count(address))
@@ -476,7 +567,8 @@ bool CWallet::IsChange(const CTxOut& txout) const
int64 CWalletTx::GetTxTime() const
{
- return nTimeReceived;
+ int64 n = nTimeSmart;
+ return n ? n : nTimeReceived;
}
int CWalletTx::GetRequestCount() const
@@ -518,23 +610,14 @@ int CWalletTx::GetRequestCount() const
return nRequests;
}
-void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, list<pair<CBitcoinAddress, int64> >& listReceived,
- list<pair<CBitcoinAddress, int64> >& listSent, int64& nFee, string& strSentAccount) const
+void CWalletTx::GetAmounts(list<pair<CTxDestination, int64> >& listReceived,
+ list<pair<CTxDestination, int64> >& listSent, int64& nFee, string& strSentAccount) const
{
- nGeneratedImmature = nGeneratedMature = nFee = 0;
+ nFee = 0;
listReceived.clear();
listSent.clear();
strSentAccount = strFromAccount;
- if (IsCoinBase())
- {
- if (GetBlocksToMaturity() > 0)
- nGeneratedImmature = pwallet->GetCredit(*this);
- else
- nGeneratedMature = GetCredit();
- return;
- }
-
// Compute fee:
int64 nDebit = GetDebit();
if (nDebit > 0) // debit>0 means we signed/sent this transaction
@@ -546,13 +629,12 @@ void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, l
// Sent/received.
BOOST_FOREACH(const CTxOut& txout, vout)
{
- CBitcoinAddress address;
+ CTxDestination address;
vector<unsigned char> vchPubKey;
- if (!ExtractAddress(txout.scriptPubKey, address))
+ if (!ExtractDestination(txout.scriptPubKey, address))
{
printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n",
this->GetHash().ToString().c_str());
- address = " unknown ";
}
// Don't report 'change' txouts
@@ -568,33 +650,30 @@ void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, l
}
-void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nGenerated, int64& nReceived,
+void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nReceived,
int64& nSent, int64& nFee) const
{
- nGenerated = nReceived = nSent = nFee = 0;
+ nReceived = nSent = nFee = 0;
- int64 allGeneratedImmature, allGeneratedMature, allFee;
- allGeneratedImmature = allGeneratedMature = allFee = 0;
+ int64 allFee;
string strSentAccount;
- list<pair<CBitcoinAddress, int64> > listReceived;
- list<pair<CBitcoinAddress, int64> > listSent;
- GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
+ list<pair<CTxDestination, int64> > listReceived;
+ list<pair<CTxDestination, int64> > listSent;
+ GetAmounts(listReceived, listSent, allFee, strSentAccount);
- if (strAccount == "")
- nGenerated = allGeneratedMature;
if (strAccount == strSentAccount)
{
- BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& s, listSent)
+ BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& s, listSent)
nSent += s.second;
nFee = allFee;
}
{
LOCK(pwallet->cs_wallet);
- BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listReceived)
+ BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listReceived)
{
if (pwallet->mapAddressBook.count(r.first))
{
- map<CBitcoinAddress, string>::const_iterator mi = pwallet->mapAddressBook.find(r.first);
+ map<CTxDestination, string>::const_iterator mi = pwallet->mapAddressBook.find(r.first);
if (mi != pwallet->mapAddressBook.end() && (*mi).second == strAccount)
nReceived += r.second;
}
@@ -727,7 +806,7 @@ void CWallet::ReacceptWalletTransactions()
// Update fSpent if a tx got spent somewhere else by a copy of wallet.dat
if (txindex.vSpent.size() != wtx.vout.size())
{
- printf("ERROR: ReacceptWalletTransactions() : txindex.vSpent.size() %d != wtx.vout.size() %d\n", txindex.vSpent.size(), wtx.vout.size());
+ printf("ERROR: ReacceptWalletTransactions() : txindex.vSpent.size() %"PRIszu" != wtx.vout.size() %"PRIszu"\n", txindex.vSpent.size(), wtx.vout.size());
continue;
}
for (unsigned int i = 0; i < txindex.vSpent.size(); i++)
@@ -852,9 +931,8 @@ int64 CWallet::GetBalance() const
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
const CWalletTx* pcoin = &(*it).second;
- if (!pcoin->IsFinal() || !pcoin->IsConfirmed())
- continue;
- nTotal += pcoin->GetAvailableCredit();
+ if (pcoin->IsFinal() && pcoin->IsConfirmed())
+ nTotal += pcoin->GetAvailableCredit();
}
}
@@ -869,106 +947,64 @@ int64 CWallet::GetUnconfirmedBalance() const
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
const CWalletTx* pcoin = &(*it).second;
- if (pcoin->IsFinal() && pcoin->IsConfirmed())
- continue;
- nTotal += pcoin->GetAvailableCredit();
+ if (!pcoin->IsFinal() || !pcoin->IsConfirmed())
+ nTotal += pcoin->GetAvailableCredit();
}
}
return nTotal;
}
-bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
+int64 CWallet::GetImmatureBalance() const
{
- setCoinsRet.clear();
- nValueRet = 0;
+ int64 nTotal = 0;
+ {
+ LOCK(cs_wallet);
+ for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ {
+ const CWalletTx& pcoin = (*it).second;
+ if (pcoin.IsCoinBase() && pcoin.GetBlocksToMaturity() > 0 && pcoin.IsInMainChain())
+ nTotal += GetCredit(pcoin);
+ }
+ }
+ return nTotal;
+}
- // List of values less than target
- pair<int64, pair<const CWalletTx*,unsigned int> > coinLowestLarger;
- coinLowestLarger.first = std::numeric_limits<int64>::max();
- coinLowestLarger.second.first = NULL;
- vector<pair<int64, pair<const CWalletTx*,unsigned int> > > vValue;
- int64 nTotalLower = 0;
+// populate vCoins with vector of spendable COutputs
+void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed) const
+{
+ vCoins.clear();
{
- LOCK(cs_wallet);
- vector<const CWalletTx*> vCoins;
- vCoins.reserve(mapWallet.size());
- for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
- vCoins.push_back(&(*it).second);
- random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt);
-
- BOOST_FOREACH(const CWalletTx* pcoin, vCoins)
- {
- if (!pcoin->IsFinal() || !pcoin->IsConfirmed())
+ LOCK(cs_wallet);
+ for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ {
+ const CWalletTx* pcoin = &(*it).second;
+
+ if (!pcoin->IsFinal())
continue;
- if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
+ if (fOnlyConfirmed && !pcoin->IsConfirmed())
continue;
- int nDepth = pcoin->GetDepthInMainChain();
- if (nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs))
+ if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
continue;
for (unsigned int i = 0; i < pcoin->vout.size(); i++)
- {
- if (pcoin->IsSpent(i) || !IsMine(pcoin->vout[i]))
- continue;
-
- int64 n = pcoin->vout[i].nValue;
-
- if (n <= 0)
- continue;
-
- pair<int64,pair<const CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin,i));
-
- if (n == nTargetValue)
- {
- setCoinsRet.insert(coin.second);
- nValueRet += coin.first;
- return true;
- }
- else if (n < nTargetValue + CENT)
- {
- vValue.push_back(coin);
- nTotalLower += n;
- }
- else if (n < coinLowestLarger.first)
- {
- coinLowestLarger = coin;
- }
- }
- }
- }
-
- if (nTotalLower == nTargetValue || nTotalLower == nTargetValue + CENT)
- {
- for (unsigned int i = 0; i < vValue.size(); ++i)
- {
- setCoinsRet.insert(vValue[i].second);
- nValueRet += vValue[i].first;
+ if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) && pcoin->vout[i].nValue > 0)
+ vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain()));
}
- return true;
}
+}
- if (nTotalLower < nTargetValue + (coinLowestLarger.second.first ? CENT : 0))
- {
- if (coinLowestLarger.second.first == NULL)
- return false;
- setCoinsRet.insert(coinLowestLarger.second);
- nValueRet += coinLowestLarger.first;
- return true;
- }
-
- if (nTotalLower >= nTargetValue + CENT)
- nTargetValue += CENT;
-
- // Solve subset sum by stochastic approximation
- sort(vValue.rbegin(), vValue.rend());
+static void ApproximateBestSubset(vector<pair<int64, pair<const CWalletTx*,unsigned int> > >vValue, int64 nTotalLower, int64 nTargetValue,
+ vector<char>& vfBest, int64& nBest, int iterations = 1000)
+{
vector<char> vfIncluded;
- vector<char> vfBest(vValue.size(), true);
- int64 nBest = nTotalLower;
- for (int nRep = 0; nRep < 1000 && nBest != nTargetValue; nRep++)
+ vfBest.assign(vValue.size(), true);
+ nBest = nTotalLower;
+
+ for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++)
{
vfIncluded.assign(vValue.size(), false);
int64 nTotal = 0;
@@ -996,9 +1032,84 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfThe
}
}
}
+}
- // If the next larger is still closer, return it
- if (coinLowestLarger.second.first && coinLowestLarger.first - nTargetValue <= nBest - nTargetValue)
+bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, vector<COutput> vCoins,
+ set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
+{
+ setCoinsRet.clear();
+ nValueRet = 0;
+
+ // List of values less than target
+ pair<int64, pair<const CWalletTx*,unsigned int> > coinLowestLarger;
+ coinLowestLarger.first = std::numeric_limits<int64>::max();
+ coinLowestLarger.second.first = NULL;
+ vector<pair<int64, pair<const CWalletTx*,unsigned int> > > vValue;
+ int64 nTotalLower = 0;
+
+ random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt);
+
+ BOOST_FOREACH(COutput output, vCoins)
+ {
+ const CWalletTx *pcoin = output.tx;
+
+ if (output.nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs))
+ continue;
+
+ int i = output.i;
+ int64 n = pcoin->vout[i].nValue;
+
+ pair<int64,pair<const CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin, i));
+
+ if (n == nTargetValue)
+ {
+ setCoinsRet.insert(coin.second);
+ nValueRet += coin.first;
+ return true;
+ }
+ else if (n < nTargetValue + CENT)
+ {
+ vValue.push_back(coin);
+ nTotalLower += n;
+ }
+ else if (n < coinLowestLarger.first)
+ {
+ coinLowestLarger = coin;
+ }
+ }
+
+ if (nTotalLower == nTargetValue)
+ {
+ for (unsigned int i = 0; i < vValue.size(); ++i)
+ {
+ setCoinsRet.insert(vValue[i].second);
+ nValueRet += vValue[i].first;
+ }
+ return true;
+ }
+
+ if (nTotalLower < nTargetValue)
+ {
+ if (coinLowestLarger.second.first == NULL)
+ return false;
+ setCoinsRet.insert(coinLowestLarger.second);
+ nValueRet += coinLowestLarger.first;
+ return true;
+ }
+
+ // Solve subset sum by stochastic approximation
+ sort(vValue.rbegin(), vValue.rend(), CompareValueOnly());
+ vector<char> vfBest;
+ int64 nBest;
+
+ ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest, 1000);
+ if (nBest != nTargetValue && nTotalLower >= nTargetValue + CENT)
+ ApproximateBestSubset(vValue, nTotalLower, nTargetValue + CENT, vfBest, nBest, 1000);
+
+ // If we have a bigger coin and (either the stochastic approximation didn't find a good solution,
+ // or the next bigger coin is closer), return the bigger coin
+ if (coinLowestLarger.second.first &&
+ ((nBest != nTargetValue && nBest < nTargetValue + CENT) || coinLowestLarger.first <= nBest))
{
setCoinsRet.insert(coinLowestLarger.second);
nValueRet += coinLowestLarger.first;
@@ -1024,9 +1135,12 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfThe
bool CWallet::SelectCoins(int64 nTargetValue, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
{
- return (SelectCoinsMinConf(nTargetValue, 1, 6, setCoinsRet, nValueRet) ||
- SelectCoinsMinConf(nTargetValue, 1, 1, setCoinsRet, nValueRet) ||
- SelectCoinsMinConf(nTargetValue, 0, 1, setCoinsRet, nValueRet));
+ vector<COutput> vCoins;
+ AvailableCoins(vCoins);
+
+ return (SelectCoinsMinConf(nTargetValue, 1, 6, vCoins, setCoinsRet, nValueRet) ||
+ SelectCoinsMinConf(nTargetValue, 1, 1, vCoins, setCoinsRet, nValueRet) ||
+ SelectCoinsMinConf(nTargetValue, 0, 1, vCoins, setCoinsRet, nValueRet));
}
@@ -1096,14 +1210,14 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CW
// post-backup change.
// Reserve a new key pair from key pool
- vector<unsigned char> vchPubKey = reservekey.GetReservedKey();
+ CPubKey vchPubKey = reservekey.GetReservedKey();
// assert(mapKeys.count(vchPubKey));
// Fill a vout to ourself
// TODO: pass in scriptChange instead of reservekey so
// change transaction isn't always pay-to-bitcoin-address
CScript scriptChange;
- scriptChange.SetBitcoinAddress(vchPubKey);
+ scriptChange.SetDestination(vchPubKey.GetID());
// Insert change txn at random position:
vector<CTxOut>::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size());
@@ -1183,7 +1297,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
coin.BindWallet(this);
coin.MarkSpent(txin.prevout.n);
coin.WriteToDisk();
- vWalletUpdated.push_back(coin.GetHash());
+ NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED);
}
if (fFileBacked)
@@ -1202,7 +1316,6 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
}
wtxNew.RelayWalletTransaction();
}
- MainFrameRepaint();
return true;
}
@@ -1231,19 +1344,18 @@ string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew,
return strError;
}
- if (fAskFee && !ThreadSafeAskFee(nFeeRequired, _("Sending...")))
+ if (fAskFee && !uiInterface.ThreadSafeAskFee(nFeeRequired, _("Sending...")))
return "ABORTED";
if (!CommitTransaction(wtxNew, reservekey))
return _("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.");
- MainFrameRepaint();
return "";
}
-string CWallet::SendMoneyToBitcoinAddress(const CBitcoinAddress& address, int64 nValue, CWalletTx& wtxNew, bool fAskFee)
+string CWallet::SendMoneyToDestination(const CTxDestination& address, int64 nValue, CWalletTx& wtxNew, bool fAskFee)
{
// Check amount
if (nValue <= 0)
@@ -1251,9 +1363,9 @@ string CWallet::SendMoneyToBitcoinAddress(const CBitcoinAddress& address, int64
if (nValue + nTransactionFee > GetBalance())
return _("Insufficient funds");
- // Parse bitcoin address
+ // Parse Bitcoin address
CScript scriptPubKey;
- scriptPubKey.SetBitcoinAddress(address);
+ scriptPubKey.SetDestination(address);
return SendMoney(scriptPubKey, nValue, wtxNew, fAskFee);
}
@@ -1261,12 +1373,12 @@ string CWallet::SendMoneyToBitcoinAddress(const CBitcoinAddress& address, int64
-int CWallet::LoadWallet(bool& fFirstRunRet)
+DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
{
if (!fFileBacked)
return DB_LOAD_OK;
fFirstRunRet = false;
- int nLoadWalletRet = CWalletDB(strWalletFile,"cr+").LoadWallet(this);
+ DBErrors nLoadWalletRet = CWalletDB(strWalletFile,"cr+").LoadWallet(this);
if (nLoadWalletRet == DB_NEED_REWRITE)
{
if (CDB::Rewrite(strWalletFile, "\x04pool"))
@@ -1276,34 +1388,34 @@ int CWallet::LoadWallet(bool& fFirstRunRet)
// User will be prompted to unlock wallet the next operation
// the requires a new key.
}
- nLoadWalletRet = DB_NEED_REWRITE;
}
if (nLoadWalletRet != DB_LOAD_OK)
return nLoadWalletRet;
- fFirstRunRet = vchDefaultKey.empty();
+ fFirstRunRet = !vchDefaultKey.IsValid();
- CreateThread(ThreadFlushWalletDB, &strWalletFile);
+ NewThread(ThreadFlushWalletDB, &strWalletFile);
return DB_LOAD_OK;
}
-bool CWallet::SetAddressBookName(const CBitcoinAddress& address, const string& strName)
+bool CWallet::SetAddressBookName(const CTxDestination& address, const string& strName)
{
+ std::map<CTxDestination, std::string>::iterator mi = mapAddressBook.find(address);
mapAddressBook[address] = strName;
- AddressBookRepaint();
+ NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address), (mi == mapAddressBook.end()) ? CT_NEW : CT_UPDATED);
if (!fFileBacked)
return false;
- return CWalletDB(strWalletFile).WriteName(address.ToString(), strName);
+ return CWalletDB(strWalletFile).WriteName(CBitcoinAddress(address).ToString(), strName);
}
-bool CWallet::DelAddressBookName(const CBitcoinAddress& address)
+bool CWallet::DelAddressBookName(const CTxDestination& address)
{
mapAddressBook.erase(address);
- AddressBookRepaint();
+ NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address), CT_DELETED);
if (!fFileBacked)
return false;
- return CWalletDB(strWalletFile).EraseName(address.ToString());
+ return CWalletDB(strWalletFile).EraseName(CBitcoinAddress(address).ToString());
}
@@ -1314,7 +1426,7 @@ void CWallet::PrintWallet(const CBlock& block)
if (mapWallet.count(block.vtx[0].GetHash()))
{
CWalletTx& wtx = mapWallet[block.vtx[0].GetHash()];
- printf(" mine: %d %d %d", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit());
+ printf(" mine: %d %d %"PRI64d"", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit());
}
}
printf("\n");
@@ -1334,7 +1446,7 @@ bool CWallet::GetTransaction(const uint256 &hashTx, CWalletTx& wtx)
return false;
}
-bool CWallet::SetDefaultKey(const std::vector<unsigned char> &vchPubKey)
+bool CWallet::SetDefaultKey(const CPubKey &vchPubKey)
{
if (fFileBacked)
{
@@ -1401,7 +1513,7 @@ bool CWallet::TopUpKeyPool()
if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey())))
throw runtime_error("TopUpKeyPool() : writing generated key failed");
setKeyPool.insert(nEnd);
- printf("keypool added key %"PRI64d", size=%d\n", nEnd, setKeyPool.size());
+ printf("keypool added key %"PRI64d", size=%"PRIszu"\n", nEnd, setKeyPool.size());
}
}
return true;
@@ -1410,7 +1522,7 @@ bool CWallet::TopUpKeyPool()
void CWallet::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool)
{
nIndex = -1;
- keypool.vchPubKey.clear();
+ keypool.vchPubKey = CPubKey();
{
LOCK(cs_wallet);
@@ -1427,9 +1539,9 @@ void CWallet::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool)
setKeyPool.erase(setKeyPool.begin());
if (!walletdb.ReadPool(nIndex, keypool))
throw runtime_error("ReserveKeyFromKeyPool() : read failed");
- if (!HaveKey(Hash160(keypool.vchPubKey)))
+ if (!HaveKey(keypool.vchPubKey.GetID()))
throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool");
- assert(!keypool.vchPubKey.empty());
+ assert(keypool.vchPubKey.IsValid());
printf("keypool reserve %"PRI64d"\n", nIndex);
}
}
@@ -1470,7 +1582,7 @@ void CWallet::ReturnKey(int64 nIndex)
printf("keypool return %"PRI64d"\n", nIndex);
}
-bool CWallet::GetKeyFromPool(vector<unsigned char>& result, bool fAllowReuse)
+bool CWallet::GetKeyFromPool(CPubKey& result, bool fAllowReuse)
{
int64 nIndex = 0;
CKeyPool keypool;
@@ -1479,7 +1591,7 @@ bool CWallet::GetKeyFromPool(vector<unsigned char>& result, bool fAllowReuse)
ReserveKeyFromKeyPool(nIndex, keypool);
if (nIndex == -1)
{
- if (fAllowReuse && !vchDefaultKey.empty())
+ if (fAllowReuse && vchDefaultKey.IsValid())
{
result = vchDefaultKey;
return true;
@@ -1505,7 +1617,130 @@ int64 CWallet::GetOldestKeyPoolTime()
return keypool.nTime;
}
-vector<unsigned char> CReserveKey::GetReservedKey()
+std::map<CTxDestination, int64> CWallet::GetAddressBalances()
+{
+ map<CTxDestination, int64> balances;
+
+ {
+ LOCK(cs_wallet);
+ BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet)
+ {
+ CWalletTx *pcoin = &walletEntry.second;
+
+ if (!pcoin->IsFinal() || !pcoin->IsConfirmed())
+ continue;
+
+ if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
+ continue;
+
+ int nDepth = pcoin->GetDepthInMainChain();
+ if (nDepth < (pcoin->IsFromMe() ? 0 : 1))
+ continue;
+
+ for (unsigned int i = 0; i < pcoin->vout.size(); i++)
+ {
+ CTxDestination addr;
+ if (!IsMine(pcoin->vout[i]))
+ continue;
+ if(!ExtractDestination(pcoin->vout[i].scriptPubKey, addr))
+ continue;
+
+ int64 n = pcoin->IsSpent(i) ? 0 : pcoin->vout[i].nValue;
+
+ if (!balances.count(addr))
+ balances[addr] = 0;
+ balances[addr] += n;
+ }
+ }
+ }
+
+ return balances;
+}
+
+set< set<CTxDestination> > CWallet::GetAddressGroupings()
+{
+ set< set<CTxDestination> > groupings;
+ set<CTxDestination> grouping;
+
+ BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet)
+ {
+ CWalletTx *pcoin = &walletEntry.second;
+
+ if (pcoin->vin.size() > 0 && IsMine(pcoin->vin[0]))
+ {
+ // group all input addresses with each other
+ BOOST_FOREACH(CTxIn txin, pcoin->vin)
+ {
+ CTxDestination address;
+ if(!ExtractDestination(mapWallet[txin.prevout.hash].vout[txin.prevout.n].scriptPubKey, address))
+ continue;
+ grouping.insert(address);
+ }
+
+ // group change with input addresses
+ BOOST_FOREACH(CTxOut txout, pcoin->vout)
+ if (IsChange(txout))
+ {
+ CWalletTx tx = mapWallet[pcoin->vin[0].prevout.hash];
+ CTxDestination txoutAddr;
+ if(!ExtractDestination(txout.scriptPubKey, txoutAddr))
+ continue;
+ grouping.insert(txoutAddr);
+ }
+ groupings.insert(grouping);
+ grouping.clear();
+ }
+
+ // group lone addrs by themselves
+ for (unsigned int i = 0; i < pcoin->vout.size(); i++)
+ if (IsMine(pcoin->vout[i]))
+ {
+ CTxDestination address;
+ if(!ExtractDestination(pcoin->vout[i].scriptPubKey, address))
+ continue;
+ grouping.insert(address);
+ groupings.insert(grouping);
+ grouping.clear();
+ }
+ }
+
+ set< set<CTxDestination>* > uniqueGroupings; // a set of pointers to groups of addresses
+ map< CTxDestination, set<CTxDestination>* > setmap; // map addresses to the unique group containing it
+ BOOST_FOREACH(set<CTxDestination> grouping, groupings)
+ {
+ // make a set of all the groups hit by this new group
+ set< set<CTxDestination>* > hits;
+ map< CTxDestination, set<CTxDestination>* >::iterator it;
+ BOOST_FOREACH(CTxDestination address, grouping)
+ if ((it = setmap.find(address)) != setmap.end())
+ hits.insert((*it).second);
+
+ // merge all hit groups into a new single group and delete old groups
+ set<CTxDestination>* merged = new set<CTxDestination>(grouping);
+ BOOST_FOREACH(set<CTxDestination>* hit, hits)
+ {
+ merged->insert(hit->begin(), hit->end());
+ uniqueGroupings.erase(hit);
+ delete hit;
+ }
+ uniqueGroupings.insert(merged);
+
+ // update setmap
+ BOOST_FOREACH(CTxDestination element, *merged)
+ setmap[element] = merged;
+ }
+
+ set< set<CTxDestination> > ret;
+ BOOST_FOREACH(set<CTxDestination>* uniqueGrouping, uniqueGroupings)
+ {
+ ret.insert(*uniqueGrouping);
+ delete uniqueGrouping;
+ }
+
+ return ret;
+}
+
+CPubKey CReserveKey::GetReservedKey()
{
if (nIndex == -1)
{
@@ -1515,11 +1750,11 @@ vector<unsigned char> CReserveKey::GetReservedKey()
vchPubKey = keypool.vchPubKey;
else
{
- printf("CReserveKey::GetReservedKey(): Warning: using default key instead of a new key, top up your keypool.");
+ printf("CReserveKey::GetReservedKey(): Warning: Using default key instead of a new key, top up your keypool!");
vchPubKey = pwallet->vchDefaultKey;
}
}
- assert(!vchPubKey.empty());
+ assert(vchPubKey.IsValid());
return vchPubKey;
}
@@ -1528,7 +1763,7 @@ void CReserveKey::KeepKey()
if (nIndex != -1)
pwallet->KeepKey(nIndex);
nIndex = -1;
- vchPubKey.clear();
+ vchPubKey = CPubKey();
}
void CReserveKey::ReturnKey()
@@ -1536,10 +1771,10 @@ void CReserveKey::ReturnKey()
if (nIndex != -1)
pwallet->ReturnKey(nIndex);
nIndex = -1;
- vchPubKey.clear();
+ vchPubKey = CPubKey();
}
-void CWallet::GetAllReserveAddresses(set<CBitcoinAddress>& setAddress)
+void CWallet::GetAllReserveKeys(set<CKeyID>& setAddress)
{
setAddress.clear();
@@ -1551,10 +1786,21 @@ void CWallet::GetAllReserveAddresses(set<CBitcoinAddress>& setAddress)
CKeyPool keypool;
if (!walletdb.ReadPool(id, keypool))
throw runtime_error("GetAllReserveKeyHashes() : read failed");
- CBitcoinAddress address(keypool.vchPubKey);
- assert(!keypool.vchPubKey.empty());
- if (!HaveKey(address))
+ assert(keypool.vchPubKey.IsValid());
+ CKeyID keyID = keypool.vchPubKey.GetID();
+ if (!HaveKey(keyID))
throw runtime_error("GetAllReserveKeyHashes() : unknown key in key pool");
- setAddress.insert(address);
+ setAddress.insert(keyID);
+ }
+}
+
+void CWallet::UpdatedTransaction(const uint256 &hashTx)
+{
+ {
+ LOCK(cs_wallet);
+ // Only notify UI if this transaction is in this wallet
+ map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(hashTx);
+ if (mi != mapWallet.end())
+ NotifyTransactionChanged(this, hashTx, CT_UPDATED);
}
}