diff options
Diffstat (limited to 'src/wallet.cpp')
| -rw-r--r-- | src/wallet.cpp | 242 |
1 files changed, 201 insertions, 41 deletions
diff --git a/src/wallet.cpp b/src/wallet.cpp index 9065ba848..823c96949 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2013 The Bitcoin developers +// Copyright (c) 2009-2014 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -18,6 +18,7 @@ using namespace std; // Settings int64_t nTransactionFee = 0; +bool bSpendZeroConfChange = true; ////////////////////////////////////////////////////////////////////////////// // @@ -191,18 +192,6 @@ void CWallet::SetBestChain(const CBlockLocator& loc) walletdb.WriteBestBlock(loc); } -// This class implements an addrIncoming entry that causes pre-0.4 -// clients to crash on startup if reading a private-key-encrypted wallet. -class CCorruptAddress -{ -public: - IMPLEMENT_SERIALIZE - ( - if (nType & SER_DISK) - READWRITE(nVersion); - ) -}; - bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit) { AssertLockHeld(cs_wallet); // nWalletVersion @@ -221,13 +210,6 @@ bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, if (fFileBacked) { CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile); - if (nWalletVersion >= 40000) - { - // Versions prior to 0.4.0 did not support the "minversion" record. - // Use a CCorruptAddress to make them crash instead. - CCorruptAddress corruptAddress; - pwalletdb->WriteSetting("addrIncoming", corruptAddress); - } if (nWalletVersion > 40000) pwalletdb->WriteMinVersion(nWalletVersion); if (!pwalletdbIn) @@ -249,6 +231,82 @@ bool CWallet::SetMaxVersion(int nVersion) return true; } +set<uint256> CWallet::GetConflicts(const uint256& txid) const +{ + set<uint256> result; + AssertLockHeld(cs_wallet); + + std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(txid); + if (it == mapWallet.end()) + return result; + const CWalletTx& wtx = it->second; + + std::pair<TxConflicts::const_iterator, TxConflicts::const_iterator> 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<TxConflicts::iterator, TxConflicts::iterator> 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<int>::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<TxConflicts::iterator, TxConflicts::iterator> range; + range = mapTxConflicts.equal_range(txin.prevout); + if (range.first != range.second) + SyncMetaData(range); + } +} + bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) { if (IsCrypted()) @@ -381,10 +439,10 @@ void CWallet::WalletUpdateSpent(const CTransaction &tx) { CWalletTx& wtx = (*mi).second; if (txin.prevout.n >= wtx.vout.size()) - LogPrintf("WalletUpdateSpent: bad wtx %s\n", wtx.GetHash().ToString().c_str()); + LogPrintf("WalletUpdateSpent: bad wtx %s\n", wtx.GetHash().ToString()); else if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n])) { - LogPrintf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); + LogPrintf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()), wtx.GetHash().ToString()); wtx.MarkSpent(txin.prevout.n); wtx.WriteToDisk(); NotifyTransactionChanged(this, txin.prevout.hash, CT_UPDATED); @@ -403,9 +461,16 @@ 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 @@ -460,9 +525,10 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) } else LogPrintf("AddToWallet() : found %s in block %s not in index\n", - wtxIn.GetHash().ToString().c_str(), - wtxIn.hashBlock.ToString().c_str()); + wtxIn.GetHash().ToString(), + wtxIn.hashBlock.ToString()); } + AddToConflicts(hash); } bool fUpdated = false; @@ -489,7 +555,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) } //// debug print - LogPrintf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString().c_str(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); + LogPrintf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); // Write to disk if (fInsertedNew || fUpdated) @@ -690,7 +756,7 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived, if (!ExtractDestination(txout.scriptPubKey, address)) { LogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", - this->GetHash().ToString().c_str()); + this->GetHash().ToString()); address = CNoDestination(); } @@ -883,7 +949,7 @@ void CWallet::ReacceptWalletTransactions() } if (fUpdated) { - LogPrintf("ReacceptWalletTransactions found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); + LogPrintf("ReacceptWalletTransactions found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()), wtx.GetHash().ToString()); wtx.MarkDirty(); wtx.WriteToDisk(); } @@ -919,12 +985,24 @@ void CWalletTx::RelayWalletTransaction() { if (GetDepthInMainChain() == 0) { uint256 hash = GetHash(); - LogPrintf("Relaying wtx %s\n", hash.ToString().c_str()); + LogPrintf("Relaying wtx %s\n", hash.ToString()); RelayTransaction((CTransaction)*this, hash); } } } +set<uint256> CWalletTx::GetConflicts() const +{ + set<uint256> result; + if (pwallet != NULL) + { + uint256 myHash = GetHash(); + result = pwallet->GetConflicts(myHash); + result.erase(myHash); + } + return result; +} + void CWallet::ResendWalletTransactions() { // Do this infrequently and randomly to avoid giving away @@ -982,7 +1060,7 @@ int64_t CWallet::GetBalance() const for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; - if (pcoin->IsConfirmed()) + if (pcoin->IsTrusted()) nTotal += pcoin->GetAvailableCredit(); } } @@ -998,7 +1076,7 @@ int64_t CWallet::GetUnconfirmedBalance() const for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; - if (!IsFinalTx(*pcoin) || !pcoin->IsConfirmed()) + if (!IsFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0)) nTotal += pcoin->GetAvailableCredit(); } } @@ -1033,17 +1111,21 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const if (!IsFinalTx(*pcoin)) continue; - if (fOnlyConfirmed && !pcoin->IsConfirmed()) + if (fOnlyConfirmed && !pcoin->IsTrusted()) continue; if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) continue; + int nDepth = pcoin->GetDepthInMainChain(); + if (nDepth < 0) + continue; + for (unsigned int i = 0; i < pcoin->vout.size(); i++) { if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) && !IsLockedCoin((*it).first, i) && pcoin->vout[i].nValue > 0 && (!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i))) - vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain())); + vCoins.push_back(COutput(pcoin, i, nDepth)); } } } @@ -1186,8 +1268,8 @@ bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, int nConfMine, int nConfT LogPrint("selectcoins", "SelectCoins() best subset: "); for (unsigned int i = 0; i < vValue.size(); i++) if (vfBest[i]) - LogPrint("selectcoins", "%s ", FormatMoney(vValue[i].first).c_str()); - LogPrint("selectcoins", "total %s\n", FormatMoney(nBest).c_str()); + LogPrint("selectcoins", "%s ", FormatMoney(vValue[i].first)); + LogPrint("selectcoins", "total %s\n", FormatMoney(nBest)); } return true; @@ -1211,7 +1293,7 @@ bool CWallet::SelectCoins(int64_t nTargetValue, set<pair<const CWalletTx*,unsign return (SelectCoinsMinConf(nTargetValue, 1, 6, vCoins, setCoinsRet, nValueRet) || SelectCoinsMinConf(nTargetValue, 1, 1, vCoins, setCoinsRet, nValueRet) || - SelectCoinsMinConf(nTargetValue, 0, 1, vCoins, setCoinsRet, nValueRet)); + (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue, 0, 1, vCoins, setCoinsRet, nValueRet))); } @@ -1396,7 +1478,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) { { LOCK2(cs_main, cs_wallet); - LogPrintf("CommitTransaction:\n%s", wtxNew.ToString().c_str()); + LogPrintf("CommitTransaction:\n%s", wtxNew.ToString()); { // This is only to keep the database open to defeat the auto-flush for the // duration of this scope. This is the only place where this optimization @@ -1451,15 +1533,15 @@ string CWallet::SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNe if (IsLocked()) { string strError = _("Error: Wallet locked, unable to create transaction!"); - LogPrintf("SendMoney() : %s", strError.c_str()); + LogPrintf("SendMoney() : %s", strError); return strError; } string strError; if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired, strError)) { if (nValue + nFeeRequired > GetBalance()) - strError = strprintf(_("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!"), FormatMoney(nFeeRequired).c_str()); - LogPrintf("SendMoney() : %s\n", strError.c_str()); + strError = strprintf(_("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!"), FormatMoney(nFeeRequired)); + LogPrintf("SendMoney() : %s\n", strError); return strError; } @@ -1515,6 +1597,30 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) } +DBErrors CWallet::ZapWalletTx() +{ + if (!fFileBacked) + return DB_LOAD_OK; + DBErrors nZapWalletTxRet = CWalletDB(strWalletFile,"cr+").ZapWalletTx(this); + if (nZapWalletTxRet == DB_NEED_REWRITE) + { + if (CDB::Rewrite(strWalletFile, "\x04pool")) + { + LOCK(cs_wallet); + setKeyPool.clear(); + // Note: can't top-up keypool here, because wallet is locked. + // User will be prompted to unlock wallet the next operation + // the requires a new key. + } + } + + if (nZapWalletTxRet != DB_LOAD_OK) + return nZapWalletTxRet; + + return DB_LOAD_OK; +} + + bool CWallet::SetAddressBook(const CTxDestination& address, const string& strName, const string& strPurpose) { AssertLockHeld(cs_wallet); // mapAddressBook @@ -1534,7 +1640,19 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const string& strNam bool CWallet::DelAddressBook(const CTxDestination& address) { + AssertLockHeld(cs_wallet); // mapAddressBook + + if(fFileBacked) + { + // Delete destdata tuples associated with address + std::string strAddress = CBitcoinAddress(address).ToString(); + BOOST_FOREACH(const PAIRTYPE(string, string) &item, mapAddressBook[address].destdata) + { + CWalletDB(strWalletFile).EraseDestData(strAddress, item.first); + } + } + mapAddressBook.erase(address); NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address), "", CT_DELETED); if (!fFileBacked) @@ -1716,7 +1834,7 @@ std::map<CTxDestination, int64_t> CWallet::GetAddressBalances() { CWalletTx *pcoin = &walletEntry.second; - if (!IsFinalTx(*pcoin) || !pcoin->IsConfirmed()) + if (!IsFinalTx(*pcoin) || !pcoin->IsTrusted()) continue; if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) @@ -2008,3 +2126,45 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const { for (std::map<CKeyID, CBlockIndex*>::const_iterator it = mapKeyFirstBlock.begin(); it != mapKeyFirstBlock.end(); it++) mapKeyBirth[it->first] = it->second->nTime - 7200; // block times can be 2h off } + +bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, const std::string &value) +{ + if (boost::get<CNoDestination>(&dest)) + return false; + + mapAddressBook[dest].destdata.insert(std::make_pair(key, value)); + if (!fFileBacked) + return true; + return CWalletDB(strWalletFile).WriteDestData(CBitcoinAddress(dest).ToString(), key, value); +} + +bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key) +{ + if (!mapAddressBook[dest].destdata.erase(key)) + return false; + if (!fFileBacked) + return true; + return CWalletDB(strWalletFile).EraseDestData(CBitcoinAddress(dest).ToString(), key); +} + +bool CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value) +{ + mapAddressBook[dest].destdata.insert(std::make_pair(key, value)); + return true; +} + +bool CWallet::GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const +{ + std::map<CTxDestination, CAddressBookData>::const_iterator i = mapAddressBook.find(dest); + if(i != mapAddressBook.end()) + { + CAddressBookData::StringMap::const_iterator j = i->second.destdata.find(key); + if(j != i->second.destdata.end()) + { + if(value) + *value = j->second; + return true; + } + } + return false; +} |