diff --git a/src/db.cpp b/src/db.cpp index b67e2a64..30e4bb0d 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -584,9 +584,6 @@ bool LoadAddresses() // CWalletDB // -static set setKeyPool; -static CCriticalSection cs_setKeyPool; - bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account) { account.SetNull(); @@ -831,34 +828,6 @@ bool CWalletDB::LoadWallet() return true; } -bool LoadWallet(bool& fFirstRunRet) -{ - fFirstRunRet = false; - if (!CWalletDB("cr+").LoadWallet()) - return false; - fFirstRunRet = vchDefaultKey.empty(); - - if (mapKeys.count(vchDefaultKey)) - { - // Set keyUser - keyUser.SetPubKey(vchDefaultKey); - keyUser.SetPrivKey(mapKeys[vchDefaultKey]); - } - else - { - // Create new keyUser and set as default key - RandAddSeedPerfmon(); - - CWalletDB walletdb; - vchDefaultKey = GetKeyFromKeyPool(); - walletdb.WriteDefaultKey(vchDefaultKey); - walletdb.WriteName(PubKeyToAddress(vchDefaultKey), ""); - } - - CreateThread(ThreadFlushWalletDB, NULL); - return true; -} - void ThreadFlushWalletDB(void* parg) { static bool fOneThread; @@ -954,75 +923,3 @@ void BackupWallet(const string& strDest) } -void CWalletDB::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool) -{ - nIndex = -1; - keypool.vchPubKey.clear(); - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(cs_mapWallet) - CRITICAL_BLOCK(cs_setKeyPool) - { - // Top up key pool - int64 nTargetSize = max(GetArg("-keypool", 100), (int64)0); - while (setKeyPool.size() < nTargetSize+1) - { - int64 nEnd = 1; - if (!setKeyPool.empty()) - nEnd = *(--setKeyPool.end()) + 1; - if (!Write(make_pair(string("pool"), nEnd), CKeyPool(GenerateNewKey()))) - throw runtime_error("ReserveKeyFromKeyPool() : writing generated key failed"); - setKeyPool.insert(nEnd); - printf("keypool added key %"PRI64d", size=%d\n", nEnd, setKeyPool.size()); - } - - // Get the oldest key - assert(!setKeyPool.empty()); - nIndex = *(setKeyPool.begin()); - setKeyPool.erase(setKeyPool.begin()); - if (!Read(make_pair(string("pool"), nIndex), keypool)) - throw runtime_error("ReserveKeyFromKeyPool() : read failed"); - if (!mapKeys.count(keypool.vchPubKey)) - throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool"); - assert(!keypool.vchPubKey.empty()); - printf("keypool reserve %"PRI64d"\n", nIndex); - } -} - -void CWalletDB::KeepKey(int64 nIndex) -{ - // Remove from key pool - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(cs_mapWallet) - { - Erase(make_pair(string("pool"), nIndex)); - } - printf("keypool keep %"PRI64d"\n", nIndex); -} - -void CWalletDB::ReturnKey(int64 nIndex) -{ - // Return to key pool - CRITICAL_BLOCK(cs_setKeyPool) - setKeyPool.insert(nIndex); - printf("keypool return %"PRI64d"\n", nIndex); -} - -vector GetKeyFromKeyPool() -{ - CWalletDB walletdb; - int64 nIndex = 0; - CKeyPool keypool; - walletdb.ReserveKeyFromKeyPool(nIndex, keypool); - walletdb.KeepKey(nIndex); - return keypool.vchPubKey; -} - -int64 GetOldestKeyPoolTime() -{ - CWalletDB walletdb; - int64 nIndex = 0; - CKeyPool keypool; - walletdb.ReserveKeyFromKeyPool(nIndex, keypool); - walletdb.ReturnKey(nIndex); - return keypool.nTime; -} diff --git a/src/db.h b/src/db.h index 9826194e..57798372 100644 --- a/src/db.h +++ b/src/db.h @@ -25,8 +25,6 @@ class CAccount; class CAccountingEntry; class CBlockLocator; -extern std::map mapAddressBook; -extern CCriticalSection cs_mapAddressBook; extern std::vector vchDefaultKey; extern bool fClient; extern int nBestHeight; @@ -39,6 +37,8 @@ extern DbEnv dbenv; extern void DBFlush(bool fShutdown); extern std::vector GetKeyFromKeyPool(); extern int64 GetOldestKeyPoolTime(); +extern void ThreadFlushWalletDB(void* parg); + @@ -494,33 +494,9 @@ public: ReturnKey(); } - std::vector GetReservedKey() - { - if (nIndex == -1) - { - CKeyPool keypool; - CWalletDB().ReserveKeyFromKeyPool(nIndex, keypool); - vchPubKey = keypool.vchPubKey; - } - assert(!vchPubKey.empty()); - return vchPubKey; - } - - void KeepKey() - { - if (nIndex != -1) - CWalletDB().KeepKey(nIndex); - nIndex = -1; - vchPubKey.clear(); - } - - void ReturnKey() - { - if (nIndex != -1) - CWalletDB::ReturnKey(nIndex); - nIndex = -1; - vchPubKey.clear(); - } + std::vector GetReservedKey(); + void KeepKey(); + void ReturnKey(); }; #endif diff --git a/src/headers.h b/src/headers.h index 9e81e27d..d1844eb2 100644 --- a/src/headers.h +++ b/src/headers.h @@ -91,10 +91,8 @@ #include "serialize.h" #include "uint256.h" #include "util.h" -#include "key.h" #include "bignum.h" #include "base58.h" -#include "script.h" #include "main.h" #ifdef GUI #include "uibase.h" diff --git a/src/keystore.cpp b/src/keystore.cpp new file mode 100644 index 00000000..51f39a52 --- /dev/null +++ b/src/keystore.cpp @@ -0,0 +1,33 @@ +// Copyright (c) 2009-2011 Satoshi Nakamoto & Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include "headers.h" +#include "db.h" + + + +////////////////////////////////////////////////////////////////////////////// +// +// mapKeys +// + +std::vector GenerateNewKey() +{ + RandAddSeedPerfmon(); + CKey key; + key.MakeNewKey(); + if (!AddKey(key)) + throw std::runtime_error("GenerateNewKey() : AddKey failed"); + return key.GetPubKey(); +} + +bool AddKey(const CKey& key) +{ + CRITICAL_BLOCK(cs_mapKeys) + { + mapKeys[key.GetPubKey()] = key.GetPrivKey(); + mapPubKeys[Hash160(key.GetPubKey())] = key.GetPubKey(); + } + return CWalletDB().WriteKey(key.GetPubKey(), key.GetPrivKey()); +} diff --git a/src/keystore.h b/src/keystore.h new file mode 100644 index 00000000..2f37ec50 --- /dev/null +++ b/src/keystore.h @@ -0,0 +1,10 @@ +// Copyright (c) 2009-2011 Satoshi Nakamoto & Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_KEYSTORE_H +#define BITCOIN_KEYSTORE_H + +bool AddKey(const CKey& key); +std::vector GenerateNewKey(); + +#endif diff --git a/src/main.cpp b/src/main.cpp index 108842f3..8949b97b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -54,6 +54,9 @@ CCriticalSection cs_mapRequestCount; map mapAddressBook; CCriticalSection cs_mapAddressBook; +set setKeyPool; +CCriticalSection cs_setKeyPool; + vector vchDefaultKey; double dHashesPerSec; @@ -79,160 +82,6 @@ int fUseUPnP = false; -////////////////////////////////////////////////////////////////////////////// -// -// mapKeys -// - -bool AddKey(const CKey& key) -{ - CRITICAL_BLOCK(cs_mapKeys) - { - mapKeys[key.GetPubKey()] = key.GetPrivKey(); - mapPubKeys[Hash160(key.GetPubKey())] = key.GetPubKey(); - } - return CWalletDB().WriteKey(key.GetPubKey(), key.GetPrivKey()); -} - -vector GenerateNewKey() -{ - RandAddSeedPerfmon(); - CKey key; - key.MakeNewKey(); - if (!AddKey(key)) - throw runtime_error("GenerateNewKey() : AddKey failed"); - return key.GetPubKey(); -} - - - - -////////////////////////////////////////////////////////////////////////////// -// -// mapWallet -// - -bool AddToWallet(const CWalletTx& wtxIn) -{ - uint256 hash = wtxIn.GetHash(); - CRITICAL_BLOCK(cs_mapWallet) - { - // Inserts only if not already there, returns tx inserted or tx found - pair::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn)); - CWalletTx& wtx = (*ret.first).second; - bool fInsertedNew = ret.second; - if (fInsertedNew) - wtx.nTimeReceived = GetAdjustedTime(); - - bool fUpdated = false; - if (!fInsertedNew) - { - // Merge - if (wtxIn.hashBlock != 0 && wtxIn.hashBlock != wtx.hashBlock) - { - wtx.hashBlock = wtxIn.hashBlock; - fUpdated = true; - } - if (wtxIn.nIndex != -1 && (wtxIn.vMerkleBranch != wtx.vMerkleBranch || wtxIn.nIndex != wtx.nIndex)) - { - wtx.vMerkleBranch = wtxIn.vMerkleBranch; - wtx.nIndex = wtxIn.nIndex; - fUpdated = true; - } - if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) - { - wtx.fFromMe = wtxIn.fFromMe; - fUpdated = true; - } - fUpdated |= wtx.UpdateSpent(wtxIn.vfSpent); - } - - //// debug print - printf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString().substr(0,10).c_str(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); - - // Write to disk - if (fInsertedNew || fUpdated) - if (!wtx.WriteToDisk()) - return false; - - // If default receiving address gets used, replace it with a new one - CScript scriptDefaultKey; - scriptDefaultKey.SetBitcoinAddress(vchDefaultKey); - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - { - if (txout.scriptPubKey == scriptDefaultKey) - { - CWalletDB walletdb; - vchDefaultKey = GetKeyFromKeyPool(); - walletdb.WriteDefaultKey(vchDefaultKey); - walletdb.WriteName(PubKeyToAddress(vchDefaultKey), ""); - } - } - - // Notify UI - vWalletUpdated.push_back(hash); - } - - // Refresh UI - MainFrameRepaint(); - return true; -} - -bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate = false) -{ - uint256 hash = tx.GetHash(); - bool fExisted = mapWallet.count(hash); - if (fExisted && !fUpdate) return false; - if (fExisted || tx.IsMine() || tx.IsFromMe()) - { - CWalletTx wtx(tx); - // Get merkle branch if transaction was found in a block - if (pblock) - wtx.SetMerkleBranch(pblock); - return AddToWallet(wtx); - } - return false; -} - -bool EraseFromWallet(uint256 hash) -{ - CRITICAL_BLOCK(cs_mapWallet) - { - if (mapWallet.erase(hash)) - CWalletDB().EraseTx(hash); - } - return true; -} - -void WalletUpdateSpent(const COutPoint& prevout) -{ - // Anytime a signature is successfully verified, it's proof the outpoint is spent. - // Update the wallet spent flag if it doesn't know due to wallet.dat being - // restored from backup or the user making copies of wallet.dat. - CRITICAL_BLOCK(cs_mapWallet) - { - map::iterator mi = mapWallet.find(prevout.hash); - if (mi != mapWallet.end()) - { - CWalletTx& wtx = (*mi).second; - if (!wtx.IsSpent(prevout.n) && wtx.vout[prevout.n].IsMine()) - { - printf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); - wtx.MarkSpent(prevout.n); - wtx.WriteToDisk(); - vWalletUpdated.push_back(prevout.hash); - } - } - } -} - - - - - - - - ////////////////////////////////////////////////////////////////////////////// // // mapOrphanTransactions @@ -312,190 +161,6 @@ bool CTransaction::ReadFromDisk(COutPoint prevout) return ReadFromDisk(txdb, prevout, txindex); } -bool CTxIn::IsMine() const -{ - CRITICAL_BLOCK(cs_mapWallet) - { - map::iterator mi = mapWallet.find(prevout.hash); - if (mi != mapWallet.end()) - { - const CWalletTx& prev = (*mi).second; - if (prevout.n < prev.vout.size()) - if (prev.vout[prevout.n].IsMine()) - return true; - } - } - return false; -} - -int64 CTxIn::GetDebit() const -{ - CRITICAL_BLOCK(cs_mapWallet) - { - map::iterator mi = mapWallet.find(prevout.hash); - if (mi != mapWallet.end()) - { - const CWalletTx& prev = (*mi).second; - if (prevout.n < prev.vout.size()) - if (prev.vout[prevout.n].IsMine()) - return prev.vout[prevout.n].nValue; - } - } - return 0; -} - -int64 CWalletTx::GetTxTime() const -{ - if (!fTimeReceivedIsTxTime && hashBlock != 0) - { - // If we did not receive the transaction directly, we rely on the block's - // time to figure out when it happened. We use the median over a range - // of blocks to try to filter out inaccurate block times. - map::iterator mi = mapBlockIndex.find(hashBlock); - if (mi != mapBlockIndex.end()) - { - CBlockIndex* pindex = (*mi).second; - if (pindex) - return pindex->GetMedianTime(); - } - } - return nTimeReceived; -} - -int CWalletTx::GetRequestCount() const -{ - // Returns -1 if it wasn't being tracked - int nRequests = -1; - CRITICAL_BLOCK(cs_mapRequestCount) - { - if (IsCoinBase()) - { - // Generated block - if (hashBlock != 0) - { - map::iterator mi = mapRequestCount.find(hashBlock); - if (mi != mapRequestCount.end()) - nRequests = (*mi).second; - } - } - else - { - // Did anyone request this transaction? - map::iterator mi = mapRequestCount.find(GetHash()); - if (mi != mapRequestCount.end()) - { - nRequests = (*mi).second; - - // How about the block it's in? - if (nRequests == 0 && hashBlock != 0) - { - map::iterator mi = mapRequestCount.find(hashBlock); - if (mi != mapRequestCount.end()) - nRequests = (*mi).second; - else - nRequests = 1; // If it's in someone else's block it must have got out - } - } - } - } - return nRequests; -} - -void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, list >& listReceived, - list >& listSent, int64& nFee, string& strSentAccount) const -{ - nGeneratedImmature = nGeneratedMature = nFee = 0; - listReceived.clear(); - listSent.clear(); - strSentAccount = strFromAccount; - - if (IsCoinBase()) - { - if (GetBlocksToMaturity() > 0) - nGeneratedImmature = CTransaction::GetCredit(); - else - nGeneratedMature = GetCredit(); - return; - } - - // Compute fee: - int64 nDebit = GetDebit(); - if (nDebit > 0) // debit>0 means we signed/sent this transaction - { - int64 nValueOut = GetValueOut(); - nFee = nDebit - nValueOut; - } - - // Sent/received. Standard client will never generate a send-to-multiple-recipients, - // but non-standard clients might (so return a list of address/amount pairs) - BOOST_FOREACH(const CTxOut& txout, vout) - { - string address; - uint160 hash160; - vector vchPubKey; - if (ExtractHash160(txout.scriptPubKey, hash160)) - address = Hash160ToAddress(hash160); - else if (ExtractPubKey(txout.scriptPubKey, false, vchPubKey)) - address = PubKeyToAddress(vchPubKey); - else - { - printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", - this->GetHash().ToString().c_str()); - address = " unknown "; - } - - // Don't report 'change' txouts - if (nDebit > 0 && txout.IsChange()) - continue; - - if (nDebit > 0) - listSent.push_back(make_pair(address, txout.nValue)); - - if (txout.IsMine()) - listReceived.push_back(make_pair(address, txout.nValue)); - } - -} - -void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nGenerated, int64& nReceived, - int64& nSent, int64& nFee) const -{ - nGenerated = nReceived = nSent = nFee = 0; - - int64 allGeneratedImmature, allGeneratedMature, allFee; - allGeneratedImmature = allGeneratedMature = allFee = 0; - string strSentAccount; - list > listReceived; - list > listSent; - GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount); - - if (strAccount == "") - nGenerated = allGeneratedMature; - if (strAccount == strSentAccount) - { - BOOST_FOREACH(const PAIRTYPE(string,int64)& s, listSent) - nSent += s.second; - nFee = allFee; - } - CRITICAL_BLOCK(cs_mapAddressBook) - { - BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listReceived) - { - if (mapAddressBook.count(r.first)) - { - if (mapAddressBook[r.first] == strAccount) - { - nReceived += r.second; - } - } - else if (strAccount.empty()) - { - nReceived += r.second; - } - } - } -} - int CMerkleTx::SetMerkleBranch(const CBlock* pblock) @@ -551,69 +216,6 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) -void CWalletTx::AddSupportingTransactions(CTxDB& txdb) -{ - vtxPrev.clear(); - - const int COPY_DEPTH = 3; - if (SetMerkleBranch() < COPY_DEPTH) - { - vector vWorkQueue; - BOOST_FOREACH(const CTxIn& txin, vin) - vWorkQueue.push_back(txin.prevout.hash); - - // This critsect is OK because txdb is already open - CRITICAL_BLOCK(cs_mapWallet) - { - map mapWalletPrev; - set setAlreadyDone; - for (int i = 0; i < vWorkQueue.size(); i++) - { - uint256 hash = vWorkQueue[i]; - if (setAlreadyDone.count(hash)) - continue; - setAlreadyDone.insert(hash); - - CMerkleTx tx; - if (mapWallet.count(hash)) - { - tx = mapWallet[hash]; - BOOST_FOREACH(const CMerkleTx& txWalletPrev, mapWallet[hash].vtxPrev) - mapWalletPrev[txWalletPrev.GetHash()] = &txWalletPrev; - } - else if (mapWalletPrev.count(hash)) - { - tx = *mapWalletPrev[hash]; - } - else if (!fClient && txdb.ReadDiskTx(hash, tx)) - { - ; - } - else - { - printf("ERROR: AddSupportingTransactions() : unsupported transaction\n"); - continue; - } - - int nDepth = tx.SetMerkleBranch(); - vtxPrev.push_back(tx); - - if (nDepth < COPY_DEPTH) - BOOST_FOREACH(const CTxIn& txin, tx.vin) - vWorkQueue.push_back(txin.prevout.hash); - } - } - } - - reverse(vtxPrev.begin(), vtxPrev.end()); -} - - - - - - - @@ -784,6 +386,11 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi return true; } +bool CTransaction::AcceptToMemoryPool(bool fCheckInputs, bool* pfMissingInputs) +{ + CTxDB txdb("r"); + return AcceptToMemoryPool(txdb, fCheckInputs, pfMissingInputs); +} bool CTransaction::AddToMemoryPoolUnchecked() { @@ -867,6 +474,12 @@ bool CMerkleTx::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs) } } +bool CMerkleTx::AcceptToMemoryPool() +{ + CTxDB txdb("r"); + return AcceptToMemoryPool(txdb); +} + bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs) @@ -888,148 +501,10 @@ bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs) return false; } -int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) -{ - int ret = 0; - - CBlockIndex* pindex = pindexStart; - CRITICAL_BLOCK(cs_mapWallet) - { - while (pindex) - { - CBlock block; - block.ReadFromDisk(pindex, true); - BOOST_FOREACH(CTransaction& tx, block.vtx) - { - if (AddToWalletIfInvolvingMe(tx, &block, fUpdate)) - ret++; - } - pindex = pindex->pnext; - } - } - return ret; -} - -void ReacceptWalletTransactions() -{ - CTxDB txdb("r"); - bool fRepeat = true; - while (fRepeat) CRITICAL_BLOCK(cs_mapWallet) - { - fRepeat = false; - vector vMissingTx; - BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) - { - CWalletTx& wtx = item.second; - if (wtx.IsCoinBase() && wtx.IsSpent(0)) - continue; - - CTxIndex txindex; - bool fUpdated = false; - if (txdb.ReadTxIndex(wtx.GetHash(), txindex)) - { - // 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()); - continue; - } - for (int i = 0; i < txindex.vSpent.size(); i++) - { - if (wtx.IsSpent(i)) - continue; - if (!txindex.vSpent[i].IsNull() && wtx.vout[i].IsMine()) - { - wtx.MarkSpent(i); - fUpdated = true; - vMissingTx.push_back(txindex.vSpent[i]); - } - } - if (fUpdated) - { - printf("ReacceptWalletTransactions found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); - wtx.MarkDirty(); - wtx.WriteToDisk(); - } - } - else - { - // Reaccept any txes of ours that aren't already in a block - if (!wtx.IsCoinBase()) - wtx.AcceptWalletTransaction(txdb, false); - } - } - if (!vMissingTx.empty()) - { - // TODO: optimize this to scan just part of the block chain? - if (ScanForWalletTransactions(pindexGenesisBlock)) - fRepeat = true; // Found missing transactions: re-do Reaccept. - } - } -} - - -void CWalletTx::RelayWalletTransaction(CTxDB& txdb) -{ - BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) - { - if (!tx.IsCoinBase()) - { - uint256 hash = tx.GetHash(); - if (!txdb.ContainsTx(hash)) - RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx); - } - } - if (!IsCoinBase()) - { - uint256 hash = GetHash(); - if (!txdb.ContainsTx(hash)) - { - printf("Relaying wtx %s\n", hash.ToString().substr(0,10).c_str()); - RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this); - } - } -} - -void ResendWalletTransactions() +bool CWalletTx::AcceptWalletTransaction() { - // Do this infrequently and randomly to avoid giving away - // that these are our transactions. - static int64 nNextTime; - if (GetTime() < nNextTime) - return; - bool fFirst = (nNextTime == 0); - nNextTime = GetTime() + GetRand(30 * 60); - if (fFirst) - return; - - // Only do it if there's been a new block since last time - static int64 nLastTime; - if (nTimeBestReceived < nLastTime) - return; - nLastTime = GetTime(); - - // Rebroadcast any of our txes that aren't in a block yet - printf("ResendWalletTransactions()\n"); CTxDB txdb("r"); - CRITICAL_BLOCK(cs_mapWallet) - { - // Sort them in chronological order - multimap mapSorted; - BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) - { - CWalletTx& wtx = item.second; - // Don't rebroadcast until it's had plenty of time that - // it should have gotten in already by now. - if (nTimeBestReceived - (int64)wtx.nTimeReceived > 5 * 60) - mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx)); - } - BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) - { - CWalletTx& wtx = *item.second; - wtx.RelayWalletTransaction(txdb); - } - } + return AcceptWalletTransaction(txdb); } int CTxIndex::GetDepthInMainChain() const @@ -1076,6 +551,7 @@ bool CBlock::ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions) return true; } + uint256 GetOrphanRoot(const CBlock* pblock) { // Work back to the first block in the orphan chain @@ -2221,127 +1697,6 @@ bool AlreadyHave(CTxDB& txdb, const CInv& inv) char pchMessageStart[4] = { 0xf9, 0xbe, 0xb4, 0xd9 }; -bool ProcessMessages(CNode* pfrom) -{ - CDataStream& vRecv = pfrom->vRecv; - if (vRecv.empty()) - return true; - //if (fDebug) - // printf("ProcessMessages(%u bytes)\n", vRecv.size()); - - // - // Message format - // (4) message start - // (12) command - // (4) size - // (4) checksum - // (x) data - // - - loop - { - // Scan for message start - CDataStream::iterator pstart = search(vRecv.begin(), vRecv.end(), BEGIN(pchMessageStart), END(pchMessageStart)); - int nHeaderSize = vRecv.GetSerializeSize(CMessageHeader()); - if (vRecv.end() - pstart < nHeaderSize) - { - if (vRecv.size() > nHeaderSize) - { - printf("\n\nPROCESSMESSAGE MESSAGESTART NOT FOUND\n\n"); - vRecv.erase(vRecv.begin(), vRecv.end() - nHeaderSize); - } - break; - } - if (pstart - vRecv.begin() > 0) - printf("\n\nPROCESSMESSAGE SKIPPED %d BYTES\n\n", pstart - vRecv.begin()); - vRecv.erase(vRecv.begin(), pstart); - - // Read header - vector vHeaderSave(vRecv.begin(), vRecv.begin() + nHeaderSize); - CMessageHeader hdr; - vRecv >> hdr; - if (!hdr.IsValid()) - { - printf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand().c_str()); - continue; - } - string strCommand = hdr.GetCommand(); - - // Message size - unsigned int nMessageSize = hdr.nMessageSize; - if (nMessageSize > MAX_SIZE) - { - printf("ProcessMessage(%s, %u bytes) : nMessageSize > MAX_SIZE\n", strCommand.c_str(), nMessageSize); - continue; - } - if (nMessageSize > vRecv.size()) - { - // Rewind and wait for rest of message - vRecv.insert(vRecv.begin(), vHeaderSave.begin(), vHeaderSave.end()); - break; - } - - // Checksum - if (vRecv.GetVersion() >= 209) - { - uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize); - unsigned int nChecksum = 0; - memcpy(&nChecksum, &hash, sizeof(nChecksum)); - if (nChecksum != hdr.nChecksum) - { - printf("ProcessMessage(%s, %u bytes) : CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", - strCommand.c_str(), nMessageSize, nChecksum, hdr.nChecksum); - continue; - } - } - - // Copy message to its own buffer - CDataStream vMsg(vRecv.begin(), vRecv.begin() + nMessageSize, vRecv.nType, vRecv.nVersion); - vRecv.ignore(nMessageSize); - - // Process message - bool fRet = false; - try - { - CRITICAL_BLOCK(cs_main) - fRet = ProcessMessage(pfrom, strCommand, vMsg); - if (fShutdown) - return true; - } - catch (std::ios_base::failure& e) - { - if (strstr(e.what(), "end of data")) - { - // Allow exceptions from underlength message on vRecv - printf("ProcessMessage(%s, %u bytes) : Exception '%s' caught, normally caused by a message being shorter than its stated length\n", strCommand.c_str(), nMessageSize, e.what()); - } - else if (strstr(e.what(), "size too large")) - { - // Allow exceptions from overlong size - printf("ProcessMessage(%s, %u bytes) : Exception '%s' caught\n", strCommand.c_str(), nMessageSize, e.what()); - } - else - { - PrintExceptionContinue(&e, "ProcessMessage()"); - } - } - catch (std::exception& e) { - PrintExceptionContinue(&e, "ProcessMessage()"); - } catch (...) { - PrintExceptionContinue(NULL, "ProcessMessage()"); - } - - if (!fRet) - printf("ProcessMessage(%s, %u bytes) FAILED\n", strCommand.c_str(), nMessageSize); - } - - vRecv.Compact(); - return true; -} - - - - bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { static map > mapReuseKey; @@ -2885,9 +2240,123 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } +bool ProcessMessages(CNode* pfrom) +{ + CDataStream& vRecv = pfrom->vRecv; + if (vRecv.empty()) + return true; + //if (fDebug) + // printf("ProcessMessages(%u bytes)\n", vRecv.size()); + // + // Message format + // (4) message start + // (12) command + // (4) size + // (4) checksum + // (x) data + // + loop + { + // Scan for message start + CDataStream::iterator pstart = search(vRecv.begin(), vRecv.end(), BEGIN(pchMessageStart), END(pchMessageStart)); + int nHeaderSize = vRecv.GetSerializeSize(CMessageHeader()); + if (vRecv.end() - pstart < nHeaderSize) + { + if (vRecv.size() > nHeaderSize) + { + printf("\n\nPROCESSMESSAGE MESSAGESTART NOT FOUND\n\n"); + vRecv.erase(vRecv.begin(), vRecv.end() - nHeaderSize); + } + break; + } + if (pstart - vRecv.begin() > 0) + printf("\n\nPROCESSMESSAGE SKIPPED %d BYTES\n\n", pstart - vRecv.begin()); + vRecv.erase(vRecv.begin(), pstart); + // Read header + vector vHeaderSave(vRecv.begin(), vRecv.begin() + nHeaderSize); + CMessageHeader hdr; + vRecv >> hdr; + if (!hdr.IsValid()) + { + printf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand().c_str()); + continue; + } + string strCommand = hdr.GetCommand(); + + // Message size + unsigned int nMessageSize = hdr.nMessageSize; + if (nMessageSize > MAX_SIZE) + { + printf("ProcessMessage(%s, %u bytes) : nMessageSize > MAX_SIZE\n", strCommand.c_str(), nMessageSize); + continue; + } + if (nMessageSize > vRecv.size()) + { + // Rewind and wait for rest of message + vRecv.insert(vRecv.begin(), vHeaderSave.begin(), vHeaderSave.end()); + break; + } + + // Checksum + if (vRecv.GetVersion() >= 209) + { + uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize); + unsigned int nChecksum = 0; + memcpy(&nChecksum, &hash, sizeof(nChecksum)); + if (nChecksum != hdr.nChecksum) + { + printf("ProcessMessage(%s, %u bytes) : CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", + strCommand.c_str(), nMessageSize, nChecksum, hdr.nChecksum); + continue; + } + } + + // Copy message to its own buffer + CDataStream vMsg(vRecv.begin(), vRecv.begin() + nMessageSize, vRecv.nType, vRecv.nVersion); + vRecv.ignore(nMessageSize); + + // Process message + bool fRet = false; + try + { + CRITICAL_BLOCK(cs_main) + fRet = ProcessMessage(pfrom, strCommand, vMsg); + if (fShutdown) + return true; + } + catch (std::ios_base::failure& e) + { + if (strstr(e.what(), "end of data")) + { + // Allow exceptions from underlength message on vRecv + printf("ProcessMessage(%s, %u bytes) : Exception '%s' caught, normally caused by a message being shorter than its stated length\n", strCommand.c_str(), nMessageSize, e.what()); + } + else if (strstr(e.what(), "size too large")) + { + // Allow exceptions from overlong size + printf("ProcessMessage(%s, %u bytes) : Exception '%s' caught\n", strCommand.c_str(), nMessageSize, e.what()); + } + else + { + PrintExceptionContinue(&e, "ProcessMessage()"); + } + } + catch (std::exception& e) { + PrintExceptionContinue(&e, "ProcessMessage()"); + } catch (...) { + PrintExceptionContinue(NULL, "ProcessMessage()"); + } + + if (!fRet) + printf("ProcessMessage(%s, %u bytes) FAILED\n", strCommand.c_str(), nMessageSize); + } + + vRecv.Compact(); + return true; +} @@ -3096,56 +2565,6 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // BitcoinMiner // -void GenerateBitcoins(bool fGenerate) -{ - if (fGenerateBitcoins != fGenerate) - { - fGenerateBitcoins = fGenerate; - CWalletDB().WriteSetting("fGenerateBitcoins", fGenerateBitcoins); - MainFrameRepaint(); - } - if (fGenerateBitcoins) - { - int nProcessors = boost::thread::hardware_concurrency(); - printf("%d processors\n", nProcessors); - if (nProcessors < 1) - nProcessors = 1; - if (fLimitProcessors && nProcessors > nLimitProcessors) - nProcessors = nLimitProcessors; - int nAddThreads = nProcessors - vnThreadsRunning[3]; - printf("Starting %d BitcoinMiner threads\n", nAddThreads); - for (int i = 0; i < nAddThreads; i++) - { - if (!CreateThread(ThreadBitcoinMiner, NULL)) - printf("Error: CreateThread(ThreadBitcoinMiner) failed\n"); - Sleep(10); - } - } -} - -void ThreadBitcoinMiner(void* parg) -{ - try - { - vnThreadsRunning[3]++; - BitcoinMiner(); - vnThreadsRunning[3]--; - } - catch (std::exception& e) { - vnThreadsRunning[3]--; - PrintException(&e, "ThreadBitcoinMiner()"); - } catch (...) { - vnThreadsRunning[3]--; - PrintException(NULL, "ThreadBitcoinMiner()"); - } - UIThreadCall(boost::bind(CalledSetStatusBar, "", 0)); - nHPSTimerStart = 0; - if (vnThreadsRunning[3] == 0) - dHashesPerSec = 0; - printf("ThreadBitcoinMiner exiting, %d threads remaining\n", vnThreadsRunning[3]); -} - - int FormatHashBlocks(void* pbuffer, unsigned int len) { unsigned char* pdata = (unsigned char*)pbuffer; @@ -3473,7 +2892,6 @@ bool CheckWork(CBlock* pblock, CReserveKey& reservekey) return true; } - void BitcoinMiner() { printf("BitcoinMiner started\n"); @@ -3617,421 +3035,52 @@ void BitcoinMiner() } } - - - - - - - - - - - - - - - - - -////////////////////////////////////////////////////////////////////////////// -// -// Actions -// - - -int64 GetBalance() -{ - int64 nStart = GetTimeMillis(); - - int64 nTotal = 0; - CRITICAL_BLOCK(cs_mapWallet) - { - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - CWalletTx* pcoin = &(*it).second; - if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) - continue; - nTotal += pcoin->GetAvailableCredit(); - } - } - - //printf("GetBalance() %"PRI64d"ms\n", GetTimeMillis() - nStart); - return nTotal; -} - - -bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set >& setCoinsRet, int64& nValueRet) +void ThreadBitcoinMiner(void* parg) { - setCoinsRet.clear(); - nValueRet = 0; - - // List of values less than target - pair > coinLowestLarger; - coinLowestLarger.first = INT64_MAX; - coinLowestLarger.second.first = NULL; - vector > > vValue; - int64 nTotalLower = 0; - - CRITICAL_BLOCK(cs_mapWallet) - { - vector vCoins; - vCoins.reserve(mapWallet.size()); - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - vCoins.push_back(&(*it).second); - random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); - - BOOST_FOREACH(CWalletTx* pcoin, vCoins) - { - if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) - continue; - - if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) - continue; - - int nDepth = pcoin->GetDepthInMainChain(); - if (nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs)) - continue; - - for (int i = 0; i < pcoin->vout.size(); i++) - { - if (pcoin->IsSpent(i) || !pcoin->vout[i].IsMine()) - continue; - - int64 n = pcoin->vout[i].nValue; - - if (n <= 0) - continue; - - pair > 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 (int i = 0; i < vValue.size(); ++i) - { - setCoinsRet.insert(vValue[i].second); - nValueRet += vValue[i].first; - } - 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()); - vector vfIncluded; - vector vfBest(vValue.size(), true); - int64 nBest = nTotalLower; - - for (int nRep = 0; nRep < 1000 && nBest != nTargetValue; nRep++) - { - vfIncluded.assign(vValue.size(), false); - int64 nTotal = 0; - bool fReachedTarget = false; - for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) - { - for (int i = 0; i < vValue.size(); i++) - { - if (nPass == 0 ? rand() % 2 : !vfIncluded[i]) - { - nTotal += vValue[i].first; - vfIncluded[i] = true; - if (nTotal >= nTargetValue) - { - fReachedTarget = true; - if (nTotal < nBest) - { - nBest = nTotal; - vfBest = vfIncluded; - } - nTotal -= vValue[i].first; - vfIncluded[i] = false; - } - } - } - } - } - - // If the next larger is still closer, return it - if (coinLowestLarger.second.first && coinLowestLarger.first - nTargetValue <= nBest - nTargetValue) + try { - setCoinsRet.insert(coinLowestLarger.second); - nValueRet += coinLowestLarger.first; + vnThreadsRunning[3]++; + BitcoinMiner(); + vnThreadsRunning[3]--; } - else { - for (int i = 0; i < vValue.size(); i++) - if (vfBest[i]) - { - setCoinsRet.insert(vValue[i].second); - nValueRet += vValue[i].first; - } - - //// debug print - printf("SelectCoins() best subset: "); - for (int i = 0; i < vValue.size(); i++) - if (vfBest[i]) - printf("%s ", FormatMoney(vValue[i].first).c_str()); - printf("total %s\n", FormatMoney(nBest).c_str()); + catch (std::exception& e) { + vnThreadsRunning[3]--; + PrintException(&e, "ThreadBitcoinMiner()"); + } catch (...) { + vnThreadsRunning[3]--; + PrintException(NULL, "ThreadBitcoinMiner()"); } - - return true; -} - -bool SelectCoins(int64 nTargetValue, set >& setCoinsRet, int64& nValueRet) -{ - return (SelectCoinsMinConf(nTargetValue, 1, 6, setCoinsRet, nValueRet) || - SelectCoinsMinConf(nTargetValue, 1, 1, setCoinsRet, nValueRet) || - SelectCoinsMinConf(nTargetValue, 0, 1, setCoinsRet, nValueRet)); + UIThreadCall(boost::bind(CalledSetStatusBar, "", 0)); + nHPSTimerStart = 0; + if (vnThreadsRunning[3] == 0) + dHashesPerSec = 0; + printf("ThreadBitcoinMiner exiting, %d threads remaining\n", vnThreadsRunning[3]); } - - -bool CreateTransaction(const vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) +void GenerateBitcoins(bool fGenerate) { - int64 nValue = 0; - BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) - { - if (nValue < 0) - return false; - nValue += s.second; - } - if (vecSend.empty() || nValue < 0) - return false; - - CRITICAL_BLOCK(cs_main) + if (fGenerateBitcoins != fGenerate) { - // txdb must be opened before the mapWallet lock - CTxDB txdb("r"); - CRITICAL_BLOCK(cs_mapWallet) - { - nFeeRet = nTransactionFee; - loop - { - wtxNew.vin.clear(); - wtxNew.vout.clear(); - wtxNew.fFromMe = true; - - int64 nTotalValue = nValue + nFeeRet; - double dPriority = 0; - // vouts to the payees - BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) - wtxNew.vout.push_back(CTxOut(s.second, s.first)); - - // Choose coins to use - set > setCoins; - int64 nValueIn = 0; - if (!SelectCoins(nTotalValue, setCoins, nValueIn)) - return false; - BOOST_FOREACH(PAIRTYPE(CWalletTx*, unsigned int) pcoin, setCoins) - { - int64 nCredit = pcoin.first->vout[pcoin.second].nValue; - dPriority += (double)nCredit * pcoin.first->GetDepthInMainChain(); - } - - int64 nChange = nValueIn - nValue - nFeeRet; - - // if sub-cent change is required, the fee must be raised to at least MIN_TX_FEE - // or until nChange becomes zero - if (nFeeRet < MIN_TX_FEE && nChange > 0 && nChange < CENT) - { - int64 nMoveToFee = min(nChange, MIN_TX_FEE - nFeeRet); - nChange -= nMoveToFee; - nFeeRet += nMoveToFee; - } - - if (nChange > 0) - { - // Note: We use a new key here to keep it from being obvious which side is the change. - // The drawback is that by not reusing a previous key, the change may be lost if a - // backup is restored, if the backup doesn't have the new private key for the change. - // If we reused the old key, it would be possible to add code to look for and - // rediscover unknown transactions that were written with keys of ours to recover - // post-backup change. - - // Reserve a new key pair from key pool - vector vchPubKey = reservekey.GetReservedKey(); - assert(mapKeys.count(vchPubKey)); - - // Fill a vout to ourself, using same address type as the payment - CScript scriptChange; - if (vecSend[0].first.GetBitcoinAddressHash160() != 0) - scriptChange.SetBitcoinAddress(vchPubKey); - else - scriptChange << vchPubKey << OP_CHECKSIG; - - // Insert change txn at random position: - vector::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()); - wtxNew.vout.insert(position, CTxOut(nChange, scriptChange)); - } - else - reservekey.ReturnKey(); - - // Fill vin - BOOST_FOREACH(const PAIRTYPE(CWalletTx*,unsigned int)& coin, setCoins) - wtxNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second)); - - // Sign - int nIn = 0; - BOOST_FOREACH(const PAIRTYPE(CWalletTx*,unsigned int)& coin, setCoins) - if (!SignSignature(*coin.first, wtxNew, nIn++)) - return false; - - // Limit size - unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK); - if (nBytes >= MAX_BLOCK_SIZE_GEN/5) - return false; - dPriority /= nBytes; - - // Check that enough fee is included - int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000); - bool fAllowFree = CTransaction::AllowFree(dPriority); - int64 nMinFee = wtxNew.GetMinFee(1, fAllowFree); - if (nFeeRet < max(nPayFee, nMinFee)) - { - nFeeRet = max(nPayFee, nMinFee); - continue; - } - - // Fill vtxPrev by copying from previous transactions vtxPrev - wtxNew.AddSupportingTransactions(txdb); - wtxNew.fTimeReceivedIsTxTime = true; - - break; - } - } + fGenerateBitcoins = fGenerate; + CWalletDB().WriteSetting("fGenerateBitcoins", fGenerateBitcoins); + MainFrameRepaint(); } - return true; -} - -bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) -{ - vector< pair > vecSend; - vecSend.push_back(make_pair(scriptPubKey, nValue)); - return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet); -} - -// Call after CreateTransaction unless you want to abort -bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) -{ - CRITICAL_BLOCK(cs_main) + if (fGenerateBitcoins) { - printf("CommitTransaction:\n%s", wtxNew.ToString().c_str()); - CRITICAL_BLOCK(cs_mapWallet) - { - // 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 - // maybe makes sense; please don't do it anywhere else. - CWalletDB walletdb("r"); - - // Take key pair from key pool so it won't be used again - reservekey.KeepKey(); - - // Add tx to wallet, because if it has change it's also ours, - // otherwise just for transaction history. - AddToWallet(wtxNew); - - // Mark old coins as spent - set setCoins; - BOOST_FOREACH(const CTxIn& txin, wtxNew.vin) - { - CWalletTx &pcoin = mapWallet[txin.prevout.hash]; - pcoin.MarkSpent(txin.prevout.n); - pcoin.WriteToDisk(); - vWalletUpdated.push_back(pcoin.GetHash()); - } - } - - // Track how many getdata requests our transaction gets - CRITICAL_BLOCK(cs_mapRequestCount) - mapRequestCount[wtxNew.GetHash()] = 0; - - // Broadcast - if (!wtxNew.AcceptToMemoryPool()) + int nProcessors = boost::thread::hardware_concurrency(); + printf("%d processors\n", nProcessors); + if (nProcessors < 1) + nProcessors = 1; + if (fLimitProcessors && nProcessors > nLimitProcessors) + nProcessors = nLimitProcessors; + int nAddThreads = nProcessors - vnThreadsRunning[3]; + printf("Starting %d BitcoinMiner threads\n", nAddThreads); + for (int i = 0; i < nAddThreads; i++) { - // This must not fail. The transaction has already been signed and recorded. - printf("CommitTransaction() : Error: Transaction not valid"); - return false; + if (!CreateThread(ThreadBitcoinMiner, NULL)) + printf("Error: CreateThread(ThreadBitcoinMiner) failed\n"); + Sleep(10); } - wtxNew.RelayWalletTransaction(); - } - MainFrameRepaint(); - return true; -} - - - - -// requires cs_main lock -string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee) -{ - CReserveKey reservekey; - int64 nFeeRequired; - if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired)) - { - string 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()); - else - strError = _("Error: Transaction creation failed "); - printf("SendMoney() : %s", strError.c_str()); - return strError; } - - if (fAskFee && !ThreadSafeAskFee(nFeeRequired, _("Sending..."), NULL)) - 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 ""; -} - - - -// requires cs_main lock -string SendMoneyToBitcoinAddress(string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee) -{ - // Check amount - if (nValue <= 0) - return _("Invalid amount"); - if (nValue + nTransactionFee > GetBalance()) - return _("Insufficient funds"); - - // Parse bitcoin address - CScript scriptPubKey; - if (!scriptPubKey.SetBitcoinAddress(strAddress)) - return _("Invalid bitcoin address"); - - return SendMoney(scriptPubKey, nValue, wtxNew, fAskFee); } diff --git a/src/main.h b/src/main.h index 7aa6d41c..2ebb8b86 100644 --- a/src/main.h +++ b/src/main.h @@ -7,7 +7,6 @@ #include "bignum.h" #include "net.h" #include "key.h" -#include "db.h" #include "script.h" #include @@ -22,6 +21,7 @@ class CTransaction; class CBlock; class CBlockIndex; class CWalletTx; +class CWallet; class CKeyItem; class CMessageHeader; @@ -62,14 +62,15 @@ extern CBigNum bnBestChainWork; extern CBigNum bnBestInvalidWork; extern uint256 hashBestChain; extern CBlockIndex* pindexBest; +extern std::set setKeyPool; +extern CCriticalSection cs_setKeyPool; extern unsigned int nTransactionsUpdated; -extern std::map mapRequestCount; -extern CCriticalSection cs_mapRequestCount; -extern std::map mapAddressBook; -extern CCriticalSection cs_mapAddressBook; -extern std::vector vchDefaultKey; extern double dHashesPerSec; extern int64 nHPSTimerStart; +extern int64 nTimeBestReceived; +extern std::map mapAddressBook; +extern CCriticalSection cs_mapAddressBook; + // Settings extern int fGenerateBitcoins; @@ -92,24 +93,11 @@ class CTxIndex; bool CheckDiskSpace(uint64 nAdditionalBytes=0); FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode="rb"); FILE* AppendBlockFile(unsigned int& nFileRet); -bool AddKey(const CKey& key); -std::vector GenerateNewKey(); -bool AddToWallet(const CWalletTx& wtxIn); -void WalletUpdateSpent(const COutPoint& prevout); -int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); -void ReacceptWalletTransactions(); bool LoadBlockIndex(bool fAllowNew=true); void PrintBlockTree(); bool ProcessMessages(CNode* pfrom); bool ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vRecv); bool SendMessages(CNode* pto, bool fSendTrickle); -int64 GetBalance(); -bool CreateTransaction(const std::vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); -bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); -bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); -bool BroadcastTransaction(CWalletTx& wtxNew); -std::string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); -std::string SendMoneyToBitcoinAddress(std::string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); void GenerateBitcoins(bool fGenerate); void ThreadBitcoinMiner(void* parg); CBlock* CreateNewBlock(CReserveKey& reservekey); @@ -721,11 +709,7 @@ public: bool ClientConnectInputs(); bool CheckTransaction() const; bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL); - bool AcceptToMemoryPool(bool fCheckInputs=true, bool* pfMissingInputs=NULL) - { - CTxDB txdb("r"); - return AcceptToMemoryPool(txdb, fCheckInputs, pfMissingInputs); - } + bool AcceptToMemoryPool(bool fCheckInputs=true, bool* pfMissingInputs=NULL); protected: bool AddToMemoryPoolUnchecked(); public: @@ -784,307 +768,7 @@ public: bool IsInMainChain() const { return GetDepthInMainChain() > 0; } int GetBlocksToMaturity() const; bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true); - bool AcceptToMemoryPool() { CTxDB txdb("r"); return AcceptToMemoryPool(txdb); } -}; - - - - -// -// A transaction with a bunch of additional info that only the owner cares -// about. It includes any unrecorded transactions needed to link it back -// to the block chain. -// -class CWalletTx : public CMerkleTx -{ -public: - std::vector vtxPrev; - std::map mapValue; - std::vector > vOrderForm; - unsigned int fTimeReceivedIsTxTime; - unsigned int nTimeReceived; // time received by this node - char fFromMe; - std::string strFromAccount; - std::vector vfSpent; - - // memory only - mutable char fDebitCached; - mutable char fCreditCached; - mutable char fAvailableCreditCached; - mutable char fChangeCached; - mutable int64 nDebitCached; - mutable int64 nCreditCached; - mutable int64 nAvailableCreditCached; - mutable int64 nChangeCached; - - // memory only UI hints - mutable unsigned int nTimeDisplayed; - mutable int nLinesDisplayed; - mutable char fConfirmedDisplayed; - - - CWalletTx() - { - Init(); - } - - CWalletTx(const CMerkleTx& txIn) : CMerkleTx(txIn) - { - Init(); - } - - CWalletTx(const CTransaction& txIn) : CMerkleTx(txIn) - { - Init(); - } - - void Init() - { - vtxPrev.clear(); - mapValue.clear(); - vOrderForm.clear(); - fTimeReceivedIsTxTime = false; - nTimeReceived = 0; - fFromMe = false; - strFromAccount.clear(); - vfSpent.clear(); - fDebitCached = false; - fCreditCached = false; - fAvailableCreditCached = false; - fChangeCached = false; - nDebitCached = 0; - nCreditCached = 0; - nAvailableCreditCached = 0; - nChangeCached = 0; - nTimeDisplayed = 0; - nLinesDisplayed = 0; - fConfirmedDisplayed = false; - } - - IMPLEMENT_SERIALIZE - ( - CWalletTx* pthis = const_cast(this); - if (fRead) - pthis->Init(); - char fSpent = false; - - if (!fRead) - { - pthis->mapValue["fromaccount"] = pthis->strFromAccount; - - std::string str; - BOOST_FOREACH(char f, vfSpent) - { - str += (f ? '1' : '0'); - if (f) - fSpent = true; - } - pthis->mapValue["spent"] = str; - } - - nSerSize += SerReadWrite(s, *(CMerkleTx*)this, nType, nVersion,ser_action); - READWRITE(vtxPrev); - READWRITE(mapValue); - READWRITE(vOrderForm); - READWRITE(fTimeReceivedIsTxTime); - READWRITE(nTimeReceived); - READWRITE(fFromMe); - READWRITE(fSpent); - - if (fRead) - { - pthis->strFromAccount = pthis->mapValue["fromaccount"]; - - if (mapValue.count("spent")) - BOOST_FOREACH(char c, pthis->mapValue["spent"]) - pthis->vfSpent.push_back(c != '0'); - else - pthis->vfSpent.assign(vout.size(), fSpent); - } - - pthis->mapValue.erase("fromaccount"); - pthis->mapValue.erase("version"); - pthis->mapValue.erase("spent"); - ) - - // marks certain txout's as spent - // returns true if any update took place - bool UpdateSpent(const std::vector& vfNewSpent) - { - bool fReturn = false; - for (int i=0; i < vfNewSpent.size(); i++) - { - if (i == vfSpent.size()) - break; - - if (vfNewSpent[i] && !vfSpent[i]) - { - vfSpent[i] = true; - fReturn = true; - fAvailableCreditCached = false; - } - } - return fReturn; - } - - void MarkDirty() - { - fCreditCached = false; - fAvailableCreditCached = false; - fDebitCached = false; - fChangeCached = false; - } - - void MarkSpent(unsigned int nOut) - { - if (nOut >= vout.size()) - throw std::runtime_error("CWalletTx::MarkSpent() : nOut out of range"); - vfSpent.resize(vout.size()); - if (!vfSpent[nOut]) - { - vfSpent[nOut] = true; - fAvailableCreditCached = false; - } - } - - bool IsSpent(unsigned int nOut) const - { - if (nOut >= vout.size()) - throw std::runtime_error("CWalletTx::IsSpent() : nOut out of range"); - if (nOut >= vfSpent.size()) - return false; - return (!!vfSpent[nOut]); - } - - int64 GetDebit() const - { - if (vin.empty()) - return 0; - if (fDebitCached) - return nDebitCached; - nDebitCached = CTransaction::GetDebit(); - fDebitCached = true; - return nDebitCached; - } - - int64 GetCredit(bool fUseCache=true) const - { - // Must wait until coinbase is safely deep enough in the chain before valuing it - if (IsCoinBase() && GetBlocksToMaturity() > 0) - return 0; - - // GetBalance can assume transactions in mapWallet won't change - if (fUseCache && fCreditCached) - return nCreditCached; - nCreditCached = CTransaction::GetCredit(); - fCreditCached = true; - return nCreditCached; - } - - int64 GetAvailableCredit(bool fUseCache=true) const - { - // Must wait until coinbase is safely deep enough in the chain before valuing it - if (IsCoinBase() && GetBlocksToMaturity() > 0) - return 0; - - if (fUseCache && fAvailableCreditCached) - return nAvailableCreditCached; - - int64 nCredit = 0; - for (int i = 0; i < vout.size(); i++) - { - if (!IsSpent(i)) - { - const CTxOut &txout = vout[i]; - nCredit += txout.GetCredit(); - if (!MoneyRange(nCredit)) - throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); - } - } - - nAvailableCreditCached = nCredit; - fAvailableCreditCached = true; - return nCredit; - } - - - int64 GetChange() const - { - if (fChangeCached) - return nChangeCached; - nChangeCached = CTransaction::GetChange(); - fChangeCached = true; - return nChangeCached; - } - - void GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, std::list >& listReceived, - std::list >& listSent, int64& nFee, std::string& strSentAccount) const; - - void GetAccountAmounts(const std::string& strAccount, int64& nGenerated, int64& nReceived, - int64& nSent, int64& nFee) const; - - bool IsFromMe() const - { - return (GetDebit() > 0); - } - - bool IsConfirmed() const - { - // Quick answer in most cases - if (!IsFinal()) - return false; - if (GetDepthInMainChain() >= 1) - return true; - if (!IsFromMe()) // using wtx's cached debit - return false; - - // If no confirmations but it's from us, we can still - // consider it confirmed if all dependencies are confirmed - std::map mapPrev; - std::vector vWorkQueue; - vWorkQueue.reserve(vtxPrev.size()+1); - vWorkQueue.push_back(this); - for (int i = 0; i < vWorkQueue.size(); i++) - { - const CMerkleTx* ptx = vWorkQueue[i]; - - if (!ptx->IsFinal()) - return false; - if (ptx->GetDepthInMainChain() >= 1) - continue; - if (!ptx->IsFromMe()) - return false; - - if (mapPrev.empty()) - BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) - mapPrev[tx.GetHash()] = &tx; - - BOOST_FOREACH(const CTxIn& txin, ptx->vin) - { - if (!mapPrev.count(txin.prevout.hash)) - return false; - vWorkQueue.push_back(mapPrev[txin.prevout.hash]); - } - } - return true; - } - - bool WriteToDisk() - { - return CWalletDB().WriteTx(GetHash(), *this); - } - - - int64 GetTxTime() const; - int GetRequestCount() const; - - void AddSupportingTransactions(CTxDB& txdb); - - bool AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs=true); - bool AcceptWalletTransaction() { CTxDB txdb("r"); return AcceptWalletTransaction(txdb); } - - void RelayWalletTransaction(CTxDB& txdb); - void RelayWalletTransaction() { CTxDB txdb("r"); RelayWalletTransaction(txdb); } + bool AcceptToMemoryPool(); }; @@ -1744,114 +1428,6 @@ public: -// -// Private key that includes an expiration date in case it never gets used. -// -class CWalletKey -{ -public: - CPrivKey vchPrivKey; - int64 nTimeCreated; - int64 nTimeExpires; - std::string strComment; - //// todo: add something to note what created it (user, getnewaddress, change) - //// maybe should have a map property map - - CWalletKey(int64 nExpires=0) - { - nTimeCreated = (nExpires ? GetTime() : 0); - nTimeExpires = nExpires; - } - - IMPLEMENT_SERIALIZE - ( - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - READWRITE(vchPrivKey); - READWRITE(nTimeCreated); - READWRITE(nTimeExpires); - READWRITE(strComment); - ) -}; - - - - - - -// -// Account information. -// Stored in wallet with key "acc"+string account name -// -class CAccount -{ -public: - std::vector vchPubKey; - - CAccount() - { - SetNull(); - } - - void SetNull() - { - vchPubKey.clear(); - } - - IMPLEMENT_SERIALIZE - ( - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - READWRITE(vchPubKey); - ) -}; - - - -// -// Internal transfers. -// Database key is acentry -// -class CAccountingEntry -{ -public: - std::string strAccount; - int64 nCreditDebit; - int64 nTime; - std::string strOtherAccount; - std::string strComment; - - CAccountingEntry() - { - SetNull(); - } - - void SetNull() - { - nCreditDebit = 0; - nTime = 0; - strAccount.clear(); - strOtherAccount.clear(); - strComment.clear(); - } - - IMPLEMENT_SERIALIZE - ( - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - // Note: strAccount is serialized as part of the key, not here. - READWRITE(nCreditDebit); - READWRITE(nTime); - READWRITE(strOtherAccount); - READWRITE(strComment); - ) -}; - - - - - - @@ -2064,12 +1640,8 @@ public: extern std::map mapTransactions; -extern std::map mapWallet; -extern std::vector vWalletUpdated; -extern CCriticalSection cs_mapWallet; extern std::map, CPrivKey> mapKeys; extern std::map > mapPubKeys; extern CCriticalSection cs_mapKeys; -extern CKey keyUser; #endif diff --git a/src/makefile.mingw b/src/makefile.mingw index c3a964e8..fccc0e36 100644 --- a/src/makefile.mingw +++ b/src/makefile.mingw @@ -33,7 +33,7 @@ DEFS=-DWIN32 -D__WXMSW__ -D_WINDOWS -DNOPCH -DUSE_SSL DEBUGFLAGS=-g -D__WXDEBUG__ CFLAGS=-mthreads -O2 -w -Wno-invalid-offsetof -Wformat $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS) HEADERS=headers.h strlcpy.h serialize.h uint256.h util.h key.h bignum.h base58.h \ - script.h db.h net.h irc.h main.h rpc.h uibase.h ui.h noui.h init.h + script.h db.h net.h irc.h keystore.h main.h wallet.h rpc.h uibase.h ui.h noui.h init.h ifdef USE_UPNP INCLUDEPATHS += -I"C:\upnpc-exe-win32-20110215" @@ -50,7 +50,9 @@ OBJS= \ obj/db.o \ obj/net.o \ obj/irc.o \ + obj/keystore.o \ obj/main.o \ + obj/wallet.o \ obj/rpc.o \ obj/init.o \ cryptopp/obj/sha.o \ diff --git a/src/makefile.osx b/src/makefile.osx index 4836ea3f..4e173a9d 100644 --- a/src/makefile.osx +++ b/src/makefile.osx @@ -33,7 +33,7 @@ DEBUGFLAGS=-g -DwxDEBUG_LEVEL=0 # ppc doesn't work because we don't support big-endian CFLAGS=-mmacosx-version-min=10.5 -arch i386 -arch x86_64 -O3 -Wno-invalid-offsetof -Wformat $(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS) HEADERS=headers.h strlcpy.h serialize.h uint256.h util.h key.h bignum.h base58.h \ - script.h db.h net.h irc.h main.h rpc.h uibase.h ui.h noui.h init.h + script.h db.h net.h irc.h keystore.h main.h wallet.h rpc.h uibase.h ui.h noui.h init.h OBJS= \ obj/util.o \ @@ -41,7 +41,9 @@ OBJS= \ obj/db.o \ obj/net.o \ obj/irc.o \ + obj/keystore.o \ obj/main.o \ + obj/wallet.o \ obj/rpc.o \ obj/init.o \ cryptopp/obj/sha.o \ diff --git a/src/makefile.unix b/src/makefile.unix index 4f2da378..f2d85b9d 100644 --- a/src/makefile.unix +++ b/src/makefile.unix @@ -39,7 +39,7 @@ LIBS+= \ DEBUGFLAGS=-g -D__WXDEBUG__ CXXFLAGS=-O2 -Wno-invalid-offsetof -Wformat $(DEBUGFLAGS) $(DEFS) HEADERS=headers.h strlcpy.h serialize.h uint256.h util.h key.h bignum.h base58.h \ - script.h db.h net.h irc.h main.h rpc.h uibase.h ui.h noui.h init.h + script.h db.h net.h irc.h keystore.h main.h wallet.h rpc.h uibase.h ui.h noui.h init.h OBJS= \ obj/util.o \ @@ -47,7 +47,9 @@ OBJS= \ obj/db.o \ obj/net.o \ obj/irc.o \ + obj/keystore.o \ obj/main.o \ + obj/wallet.o \ obj/rpc.o \ obj/init.o \ cryptopp/obj/sha.o \ diff --git a/src/noui.h b/src/noui.h index afb19526..d0072df7 100644 --- a/src/noui.h +++ b/src/noui.h @@ -5,6 +5,8 @@ #define BITCOIN_NOUI_H #include +#include +#include "wallet.h" typedef void wxWindow; #define wxYES 0x00000002 diff --git a/src/script.h b/src/script.h index 22a6020d..efafec44 100644 --- a/src/script.h +++ b/src/script.h @@ -5,6 +5,7 @@ #define H_BITCOIN_SCRIPT #include "base58.h" +#include "keystore.h" #include #include diff --git a/src/ui.cpp b/src/ui.cpp index 0a7d4557..72e8fe2e 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -3,6 +3,7 @@ // file license.txt or http://www.opensource.org/licenses/mit-license.php. #include "headers.h" +#include "db.h" #include "init.h" #include "strlcpy.h" #include diff --git a/src/ui.h b/src/ui.h index 16643db4..3f06ad90 100644 --- a/src/ui.h +++ b/src/ui.h @@ -4,6 +4,9 @@ #ifndef BITCOIN_UI_H #define BITCOIN_UI_H +#include +#include "wallet.h" + DECLARE_EVENT_TYPE(wxEVT_UITHREADCALL, -1) diff --git a/src/wallet.cpp b/src/wallet.cpp new file mode 100644 index 00000000..a9fc92fd --- /dev/null +++ b/src/wallet.cpp @@ -0,0 +1,1056 @@ +// Copyright (c) 2009-2011 Satoshi Nakamoto & Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include "headers.h" +#include "db.h" +#include "cryptopp/sha.h" + +using namespace std; + + + +////////////////////////////////////////////////////////////////////////////// +// +// mapWallet +// + +void WalletUpdateSpent(const COutPoint& prevout) +{ + // Anytime a signature is successfully verified, it's proof the outpoint is spent. + // Update the wallet spent flag if it doesn't know due to wallet.dat being + // restored from backup or the user making copies of wallet.dat. + CRITICAL_BLOCK(cs_mapWallet) + { + map::iterator mi = mapWallet.find(prevout.hash); + if (mi != mapWallet.end()) + { + CWalletTx& wtx = (*mi).second; + if (!wtx.IsSpent(prevout.n) && wtx.vout[prevout.n].IsMine()) + { + printf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); + wtx.MarkSpent(prevout.n); + wtx.WriteToDisk(); + vWalletUpdated.push_back(prevout.hash); + } + } + } +} + +bool AddToWallet(const CWalletTx& wtxIn) +{ + uint256 hash = wtxIn.GetHash(); + CRITICAL_BLOCK(cs_mapWallet) + { + // Inserts only if not already there, returns tx inserted or tx found + pair::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn)); + CWalletTx& wtx = (*ret.first).second; + bool fInsertedNew = ret.second; + if (fInsertedNew) + wtx.nTimeReceived = GetAdjustedTime(); + + bool fUpdated = false; + if (!fInsertedNew) + { + // Merge + if (wtxIn.hashBlock != 0 && wtxIn.hashBlock != wtx.hashBlock) + { + wtx.hashBlock = wtxIn.hashBlock; + fUpdated = true; + } + if (wtxIn.nIndex != -1 && (wtxIn.vMerkleBranch != wtx.vMerkleBranch || wtxIn.nIndex != wtx.nIndex)) + { + wtx.vMerkleBranch = wtxIn.vMerkleBranch; + wtx.nIndex = wtxIn.nIndex; + fUpdated = true; + } + if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) + { + wtx.fFromMe = wtxIn.fFromMe; + fUpdated = true; + } + fUpdated |= wtx.UpdateSpent(wtxIn.vfSpent); + } + + //// debug print + printf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString().substr(0,10).c_str(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); + + // Write to disk + if (fInsertedNew || fUpdated) + if (!wtx.WriteToDisk()) + return false; + + // If default receiving address gets used, replace it with a new one + CScript scriptDefaultKey; + scriptDefaultKey.SetBitcoinAddress(vchDefaultKey); + BOOST_FOREACH(const CTxOut& txout, wtx.vout) + { + if (txout.scriptPubKey == scriptDefaultKey) + { + CWalletDB walletdb; + vchDefaultKey = GetKeyFromKeyPool(); + walletdb.WriteDefaultKey(vchDefaultKey); + walletdb.WriteName(PubKeyToAddress(vchDefaultKey), ""); + } + } + + // Notify UI + vWalletUpdated.push_back(hash); + } + + // Refresh UI + MainFrameRepaint(); + return true; +} + +bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate) +{ + uint256 hash = tx.GetHash(); + bool fExisted = mapWallet.count(hash); + if (fExisted && !fUpdate) return false; + if (fExisted || tx.IsMine() || tx.IsFromMe()) + { + CWalletTx wtx(tx); + // Get merkle branch if transaction was found in a block + if (pblock) + wtx.SetMerkleBranch(pblock); + return AddToWallet(wtx); + } + return false; +} + +bool EraseFromWallet(uint256 hash) +{ + CRITICAL_BLOCK(cs_mapWallet) + { + if (mapWallet.erase(hash)) + CWalletDB().EraseTx(hash); + } + return true; +} + + +bool CTxIn::IsMine() const +{ + CRITICAL_BLOCK(cs_mapWallet) + { + map::iterator mi = mapWallet.find(prevout.hash); + if (mi != mapWallet.end()) + { + const CWalletTx& prev = (*mi).second; + if (prevout.n < prev.vout.size()) + if (prev.vout[prevout.n].IsMine()) + return true; + } + } + return false; +} + +int64 CTxIn::GetDebit() const +{ + CRITICAL_BLOCK(cs_mapWallet) + { + map::iterator mi = mapWallet.find(prevout.hash); + if (mi != mapWallet.end()) + { + const CWalletTx& prev = (*mi).second; + if (prevout.n < prev.vout.size()) + if (prev.vout[prevout.n].IsMine()) + return prev.vout[prevout.n].nValue; + } + } + return 0; +} + +int64 CWalletTx::GetTxTime() const +{ + if (!fTimeReceivedIsTxTime && hashBlock != 0) + { + // If we did not receive the transaction directly, we rely on the block's + // time to figure out when it happened. We use the median over a range + // of blocks to try to filter out inaccurate block times. + map::iterator mi = mapBlockIndex.find(hashBlock); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (pindex) + return pindex->GetMedianTime(); + } + } + return nTimeReceived; +} + +int CWalletTx::GetRequestCount() const +{ + // Returns -1 if it wasn't being tracked + int nRequests = -1; + CRITICAL_BLOCK(cs_mapRequestCount) + { + if (IsCoinBase()) + { + // Generated block + if (hashBlock != 0) + { + map::iterator mi = mapRequestCount.find(hashBlock); + if (mi != mapRequestCount.end()) + nRequests = (*mi).second; + } + } + else + { + // Did anyone request this transaction? + map::iterator mi = mapRequestCount.find(GetHash()); + if (mi != mapRequestCount.end()) + { + nRequests = (*mi).second; + + // How about the block it's in? + if (nRequests == 0 && hashBlock != 0) + { + map::iterator mi = mapRequestCount.find(hashBlock); + if (mi != mapRequestCount.end()) + nRequests = (*mi).second; + else + nRequests = 1; // If it's in someone else's block it must have got out + } + } + } + } + return nRequests; +} + +void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, list >& listReceived, + list >& listSent, int64& nFee, string& strSentAccount) const +{ + nGeneratedImmature = nGeneratedMature = nFee = 0; + listReceived.clear(); + listSent.clear(); + strSentAccount = strFromAccount; + + if (IsCoinBase()) + { + if (GetBlocksToMaturity() > 0) + nGeneratedImmature = CTransaction::GetCredit(); + else + nGeneratedMature = GetCredit(); + return; + } + + // Compute fee: + int64 nDebit = GetDebit(); + if (nDebit > 0) // debit>0 means we signed/sent this transaction + { + int64 nValueOut = GetValueOut(); + nFee = nDebit - nValueOut; + } + + // Sent/received. Standard client will never generate a send-to-multiple-recipients, + // but non-standard clients might (so return a list of address/amount pairs) + BOOST_FOREACH(const CTxOut& txout, vout) + { + string address; + uint160 hash160; + vector vchPubKey; + if (ExtractHash160(txout.scriptPubKey, hash160)) + address = Hash160ToAddress(hash160); + else if (ExtractPubKey(txout.scriptPubKey, false, vchPubKey)) + address = PubKeyToAddress(vchPubKey); + else + { + printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", + this->GetHash().ToString().c_str()); + address = " unknown "; + } + + // Don't report 'change' txouts + if (nDebit > 0 && txout.IsChange()) + continue; + + if (nDebit > 0) + listSent.push_back(make_pair(address, txout.nValue)); + + if (txout.IsMine()) + listReceived.push_back(make_pair(address, txout.nValue)); + } + +} + +void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nGenerated, int64& nReceived, + int64& nSent, int64& nFee) const +{ + nGenerated = nReceived = nSent = nFee = 0; + + int64 allGeneratedImmature, allGeneratedMature, allFee; + allGeneratedImmature = allGeneratedMature = allFee = 0; + string strSentAccount; + list > listReceived; + list > listSent; + GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount); + + if (strAccount == "") + nGenerated = allGeneratedMature; + if (strAccount == strSentAccount) + { + BOOST_FOREACH(const PAIRTYPE(string,int64)& s, listSent) + nSent += s.second; + nFee = allFee; + } + CRITICAL_BLOCK(cs_mapAddressBook) + { + BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listReceived) + { + if (mapAddressBook.count(r.first)) + { + if (mapAddressBook[r.first] == strAccount) + { + nReceived += r.second; + } + } + else if (strAccount.empty()) + { + nReceived += r.second; + } + } + } +} + +void CWalletTx::AddSupportingTransactions(CTxDB& txdb) +{ + vtxPrev.clear(); + + const int COPY_DEPTH = 3; + if (SetMerkleBranch() < COPY_DEPTH) + { + vector vWorkQueue; + BOOST_FOREACH(const CTxIn& txin, vin) + vWorkQueue.push_back(txin.prevout.hash); + + // This critsect is OK because txdb is already open + CRITICAL_BLOCK(cs_mapWallet) + { + map mapWalletPrev; + set setAlreadyDone; + for (int i = 0; i < vWorkQueue.size(); i++) + { + uint256 hash = vWorkQueue[i]; + if (setAlreadyDone.count(hash)) + continue; + setAlreadyDone.insert(hash); + + CMerkleTx tx; + if (mapWallet.count(hash)) + { + tx = mapWallet[hash]; + BOOST_FOREACH(const CMerkleTx& txWalletPrev, mapWallet[hash].vtxPrev) + mapWalletPrev[txWalletPrev.GetHash()] = &txWalletPrev; + } + else if (mapWalletPrev.count(hash)) + { + tx = *mapWalletPrev[hash]; + } + else if (!fClient && txdb.ReadDiskTx(hash, tx)) + { + ; + } + else + { + printf("ERROR: AddSupportingTransactions() : unsupported transaction\n"); + continue; + } + + int nDepth = tx.SetMerkleBranch(); + vtxPrev.push_back(tx); + + if (nDepth < COPY_DEPTH) + BOOST_FOREACH(const CTxIn& txin, tx.vin) + vWorkQueue.push_back(txin.prevout.hash); + } + } + } + + reverse(vtxPrev.begin(), vtxPrev.end()); +} + +bool CWalletTx::WriteToDisk() +{ + return CWalletDB().WriteTx(GetHash(), *this); +} + +int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) +{ + int ret = 0; + + CBlockIndex* pindex = pindexStart; + CRITICAL_BLOCK(cs_mapWallet) + { + while (pindex) + { + CBlock block; + block.ReadFromDisk(pindex, true); + BOOST_FOREACH(CTransaction& tx, block.vtx) + { + if (AddToWalletIfInvolvingMe(tx, &block, fUpdate)) + ret++; + } + pindex = pindex->pnext; + } + } + return ret; +} + +void ReacceptWalletTransactions() +{ + CTxDB txdb("r"); + bool fRepeat = true; + while (fRepeat) CRITICAL_BLOCK(cs_mapWallet) + { + fRepeat = false; + vector vMissingTx; + BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) + { + CWalletTx& wtx = item.second; + if (wtx.IsCoinBase() && wtx.IsSpent(0)) + continue; + + CTxIndex txindex; + bool fUpdated = false; + if (txdb.ReadTxIndex(wtx.GetHash(), txindex)) + { + // 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()); + continue; + } + for (int i = 0; i < txindex.vSpent.size(); i++) + { + if (wtx.IsSpent(i)) + continue; + if (!txindex.vSpent[i].IsNull() && wtx.vout[i].IsMine()) + { + wtx.MarkSpent(i); + fUpdated = true; + vMissingTx.push_back(txindex.vSpent[i]); + } + } + if (fUpdated) + { + printf("ReacceptWalletTransactions found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); + wtx.MarkDirty(); + wtx.WriteToDisk(); + } + } + else + { + // Reaccept any txes of ours that aren't already in a block + if (!wtx.IsCoinBase()) + wtx.AcceptWalletTransaction(txdb, false); + } + } + if (!vMissingTx.empty()) + { + // TODO: optimize this to scan just part of the block chain? + if (ScanForWalletTransactions(pindexGenesisBlock)) + fRepeat = true; // Found missing transactions: re-do Reaccept. + } + } +} + +void CWalletTx::RelayWalletTransaction(CTxDB& txdb) +{ + BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) + { + if (!tx.IsCoinBase()) + { + uint256 hash = tx.GetHash(); + if (!txdb.ContainsTx(hash)) + RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx); + } + } + if (!IsCoinBase()) + { + uint256 hash = GetHash(); + if (!txdb.ContainsTx(hash)) + { + printf("Relaying wtx %s\n", hash.ToString().substr(0,10).c_str()); + RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this); + } + } +} + +void CWalletTx::RelayWalletTransaction() +{ + CTxDB txdb("r"); + RelayWalletTransaction(txdb); +} + +void ResendWalletTransactions() +{ + // Do this infrequently and randomly to avoid giving away + // that these are our transactions. + static int64 nNextTime; + if (GetTime() < nNextTime) + return; + bool fFirst = (nNextTime == 0); + nNextTime = GetTime() + GetRand(30 * 60); + if (fFirst) + return; + + // Only do it if there's been a new block since last time + static int64 nLastTime; + if (nTimeBestReceived < nLastTime) + return; + nLastTime = GetTime(); + + // Rebroadcast any of our txes that aren't in a block yet + printf("ResendWalletTransactions()\n"); + CTxDB txdb("r"); + CRITICAL_BLOCK(cs_mapWallet) + { + // Sort them in chronological order + multimap mapSorted; + BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) + { + CWalletTx& wtx = item.second; + // Don't rebroadcast until it's had plenty of time that + // it should have gotten in already by now. + if (nTimeBestReceived - (int64)wtx.nTimeReceived > 5 * 60) + mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx)); + } + BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) + { + CWalletTx& wtx = *item.second; + wtx.RelayWalletTransaction(txdb); + } + } +} + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// Actions +// + + +int64 GetBalance() +{ + int64 nStart = GetTimeMillis(); + + int64 nTotal = 0; + CRITICAL_BLOCK(cs_mapWallet) + { + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + CWalletTx* pcoin = &(*it).second; + if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) + continue; + nTotal += pcoin->GetAvailableCredit(); + } + } + + //printf("GetBalance() %"PRI64d"ms\n", GetTimeMillis() - nStart); + return nTotal; +} + + +bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set >& setCoinsRet, int64& nValueRet) +{ + setCoinsRet.clear(); + nValueRet = 0; + + // List of values less than target + pair > coinLowestLarger; + coinLowestLarger.first = INT64_MAX; + coinLowestLarger.second.first = NULL; + vector > > vValue; + int64 nTotalLower = 0; + + CRITICAL_BLOCK(cs_mapWallet) + { + vector vCoins; + vCoins.reserve(mapWallet.size()); + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + vCoins.push_back(&(*it).second); + random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); + + BOOST_FOREACH(CWalletTx* pcoin, vCoins) + { + if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) + continue; + + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + continue; + + int nDepth = pcoin->GetDepthInMainChain(); + if (nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs)) + continue; + + for (int i = 0; i < pcoin->vout.size(); i++) + { + if (pcoin->IsSpent(i) || !pcoin->vout[i].IsMine()) + continue; + + int64 n = pcoin->vout[i].nValue; + + if (n <= 0) + continue; + + pair > 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 (int i = 0; i < vValue.size(); ++i) + { + setCoinsRet.insert(vValue[i].second); + nValueRet += vValue[i].first; + } + 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()); + vector vfIncluded; + vector vfBest(vValue.size(), true); + int64 nBest = nTotalLower; + + for (int nRep = 0; nRep < 1000 && nBest != nTargetValue; nRep++) + { + vfIncluded.assign(vValue.size(), false); + int64 nTotal = 0; + bool fReachedTarget = false; + for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) + { + for (int i = 0; i < vValue.size(); i++) + { + if (nPass == 0 ? rand() % 2 : !vfIncluded[i]) + { + nTotal += vValue[i].first; + vfIncluded[i] = true; + if (nTotal >= nTargetValue) + { + fReachedTarget = true; + if (nTotal < nBest) + { + nBest = nTotal; + vfBest = vfIncluded; + } + nTotal -= vValue[i].first; + vfIncluded[i] = false; + } + } + } + } + } + + // If the next larger is still closer, return it + if (coinLowestLarger.second.first && coinLowestLarger.first - nTargetValue <= nBest - nTargetValue) + { + setCoinsRet.insert(coinLowestLarger.second); + nValueRet += coinLowestLarger.first; + } + else { + for (int i = 0; i < vValue.size(); i++) + if (vfBest[i]) + { + setCoinsRet.insert(vValue[i].second); + nValueRet += vValue[i].first; + } + + //// debug print + printf("SelectCoins() best subset: "); + for (int i = 0; i < vValue.size(); i++) + if (vfBest[i]) + printf("%s ", FormatMoney(vValue[i].first).c_str()); + printf("total %s\n", FormatMoney(nBest).c_str()); + } + + return true; +} + +bool SelectCoins(int64 nTargetValue, set >& setCoinsRet, int64& nValueRet) +{ + return (SelectCoinsMinConf(nTargetValue, 1, 6, setCoinsRet, nValueRet) || + SelectCoinsMinConf(nTargetValue, 1, 1, setCoinsRet, nValueRet) || + SelectCoinsMinConf(nTargetValue, 0, 1, setCoinsRet, nValueRet)); +} + + + + +bool CreateTransaction(const vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) +{ + int64 nValue = 0; + BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) + { + if (nValue < 0) + return false; + nValue += s.second; + } + if (vecSend.empty() || nValue < 0) + return false; + + CRITICAL_BLOCK(cs_main) + { + // txdb must be opened before the mapWallet lock + CTxDB txdb("r"); + CRITICAL_BLOCK(cs_mapWallet) + { + nFeeRet = nTransactionFee; + loop + { + wtxNew.vin.clear(); + wtxNew.vout.clear(); + wtxNew.fFromMe = true; + + int64 nTotalValue = nValue + nFeeRet; + double dPriority = 0; + // vouts to the payees + BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) + wtxNew.vout.push_back(CTxOut(s.second, s.first)); + + // Choose coins to use + set > setCoins; + int64 nValueIn = 0; + if (!SelectCoins(nTotalValue, setCoins, nValueIn)) + return false; + BOOST_FOREACH(PAIRTYPE(CWalletTx*, unsigned int) pcoin, setCoins) + { + int64 nCredit = pcoin.first->vout[pcoin.second].nValue; + dPriority += (double)nCredit * pcoin.first->GetDepthInMainChain(); + } + + // Fill a vout back to self with any change + int64 nChange = nValueIn - nTotalValue; + if (nChange >= CENT) + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + vector vchPubKey = reservekey.GetReservedKey(); + assert(mapKeys.count(vchPubKey)); + + // Fill a vout to ourself, using same address type as the payment + CScript scriptChange; + if (vecSend[0].first.GetBitcoinAddressHash160() != 0) + scriptChange.SetBitcoinAddress(vchPubKey); + else + scriptChange << vchPubKey << OP_CHECKSIG; + + // Insert change txn at random position: + vector::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()); + wtxNew.vout.insert(position, CTxOut(nChange, scriptChange)); + } + else + reservekey.ReturnKey(); + + // Fill vin + BOOST_FOREACH(const PAIRTYPE(CWalletTx*,unsigned int)& coin, setCoins) + wtxNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second)); + + // Sign + int nIn = 0; + BOOST_FOREACH(const PAIRTYPE(CWalletTx*,unsigned int)& coin, setCoins) + if (!SignSignature(*coin.first, wtxNew, nIn++)) + return false; + + // Limit size + unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK); + if (nBytes >= MAX_BLOCK_SIZE_GEN/5) + return false; + dPriority /= nBytes; + + // Check that enough fee is included + int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000); + bool fAllowFree = CTransaction::AllowFree(dPriority); + int64 nMinFee = wtxNew.GetMinFee(1, fAllowFree); + if (nFeeRet < max(nPayFee, nMinFee)) + { + nFeeRet = max(nPayFee, nMinFee); + continue; + } + + // Fill vtxPrev by copying from previous transactions vtxPrev + wtxNew.AddSupportingTransactions(txdb); + wtxNew.fTimeReceivedIsTxTime = true; + + break; + } + } + } + return true; +} + +bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) +{ + vector< pair > vecSend; + vecSend.push_back(make_pair(scriptPubKey, nValue)); + return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet); +} + +// Call after CreateTransaction unless you want to abort +bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) +{ + CRITICAL_BLOCK(cs_main) + { + printf("CommitTransaction:\n%s", wtxNew.ToString().c_str()); + CRITICAL_BLOCK(cs_mapWallet) + { + // 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 + // maybe makes sense; please don't do it anywhere else. + CWalletDB walletdb("r"); + + // Take key pair from key pool so it won't be used again + reservekey.KeepKey(); + + // Add tx to wallet, because if it has change it's also ours, + // otherwise just for transaction history. + AddToWallet(wtxNew); + + // Mark old coins as spent + set setCoins; + BOOST_FOREACH(const CTxIn& txin, wtxNew.vin) + { + CWalletTx &pcoin = mapWallet[txin.prevout.hash]; + pcoin.MarkSpent(txin.prevout.n); + pcoin.WriteToDisk(); + vWalletUpdated.push_back(pcoin.GetHash()); + } + } + + // Track how many getdata requests our transaction gets + CRITICAL_BLOCK(cs_mapRequestCount) + mapRequestCount[wtxNew.GetHash()] = 0; + + // Broadcast + if (!wtxNew.AcceptToMemoryPool()) + { + // This must not fail. The transaction has already been signed and recorded. + printf("CommitTransaction() : Error: Transaction not valid"); + return false; + } + wtxNew.RelayWalletTransaction(); + } + MainFrameRepaint(); + return true; +} + + + + +// requires cs_main lock +string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee) +{ + CReserveKey reservekey; + int64 nFeeRequired; + if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired)) + { + string 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()); + else + strError = _("Error: Transaction creation failed "); + printf("SendMoney() : %s", strError.c_str()); + return strError; + } + + if (fAskFee && !ThreadSafeAskFee(nFeeRequired, _("Sending..."), NULL)) + 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 ""; +} + + + +// requires cs_main lock +string SendMoneyToBitcoinAddress(string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee) +{ + // Check amount + if (nValue <= 0) + return _("Invalid amount"); + if (nValue + nTransactionFee > GetBalance()) + return _("Insufficient funds"); + + // Parse bitcoin address + CScript scriptPubKey; + if (!scriptPubKey.SetBitcoinAddress(strAddress)) + return _("Invalid bitcoin address"); + + return SendMoney(scriptPubKey, nValue, wtxNew, fAskFee); +} + + + + +bool LoadWallet(bool& fFirstRunRet) +{ + fFirstRunRet = false; + if (!CWalletDB("cr+").LoadWallet()) + return false; + fFirstRunRet = vchDefaultKey.empty(); + + if (mapKeys.count(vchDefaultKey)) + { + // Set keyUser + keyUser.SetPubKey(vchDefaultKey); + keyUser.SetPrivKey(mapKeys[vchDefaultKey]); + } + else + { + // Create new keyUser and set as default key + RandAddSeedPerfmon(); + + CWalletDB walletdb; + vchDefaultKey = GetKeyFromKeyPool(); + walletdb.WriteDefaultKey(vchDefaultKey); + walletdb.WriteName(PubKeyToAddress(vchDefaultKey), ""); + } + + CreateThread(ThreadFlushWalletDB, NULL); + return true; +} + +void CWalletDB::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool) +{ + nIndex = -1; + keypool.vchPubKey.clear(); + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(cs_setKeyPool) + { + // Top up key pool + int64 nTargetSize = max(GetArg("-keypool", 100), (int64)0); + while (setKeyPool.size() < nTargetSize+1) + { + int64 nEnd = 1; + if (!setKeyPool.empty()) + nEnd = *(--setKeyPool.end()) + 1; + if (!Write(make_pair(string("pool"), nEnd), CKeyPool(GenerateNewKey()))) + throw runtime_error("ReserveKeyFromKeyPool() : writing generated key failed"); + setKeyPool.insert(nEnd); + printf("keypool added key %"PRI64d", size=%d\n", nEnd, setKeyPool.size()); + } + + // Get the oldest key + assert(!setKeyPool.empty()); + nIndex = *(setKeyPool.begin()); + setKeyPool.erase(setKeyPool.begin()); + if (!Read(make_pair(string("pool"), nIndex), keypool)) + throw runtime_error("ReserveKeyFromKeyPool() : read failed"); + if (!mapKeys.count(keypool.vchPubKey)) + throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool"); + assert(!keypool.vchPubKey.empty()); + printf("keypool reserve %"PRI64d"\n", nIndex); + } +} + +void CWalletDB::KeepKey(int64 nIndex) +{ + // Remove from key pool + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(cs_mapWallet) + { + Erase(make_pair(string("pool"), nIndex)); + } + printf("keypool keep %"PRI64d"\n", nIndex); +} + +void CWalletDB::ReturnKey(int64 nIndex) +{ + // Return to key pool + CRITICAL_BLOCK(cs_setKeyPool) + setKeyPool.insert(nIndex); + printf("keypool return %"PRI64d"\n", nIndex); +} + +vector GetKeyFromKeyPool() +{ + CWalletDB walletdb; + int64 nIndex = 0; + CKeyPool keypool; + walletdb.ReserveKeyFromKeyPool(nIndex, keypool); + walletdb.KeepKey(nIndex); + return keypool.vchPubKey; +} + +int64 GetOldestKeyPoolTime() +{ + CWalletDB walletdb; + int64 nIndex = 0; + CKeyPool keypool; + walletdb.ReserveKeyFromKeyPool(nIndex, keypool); + walletdb.ReturnKey(nIndex); + return keypool.nTime; +} + +std::vector CReserveKey::GetReservedKey() +{ + if (nIndex == -1) + { + CKeyPool keypool; + CWalletDB().ReserveKeyFromKeyPool(nIndex, keypool); + vchPubKey = keypool.vchPubKey; + } + assert(!vchPubKey.empty()); + return vchPubKey; +} + +void CReserveKey::KeepKey() +{ + if (nIndex != -1) + CWalletDB().KeepKey(nIndex); + nIndex = -1; + vchPubKey.clear(); +} + +void CReserveKey::ReturnKey() +{ + if (nIndex != -1) + CWalletDB::ReturnKey(nIndex); + nIndex = -1; + vchPubKey.clear(); +} diff --git a/src/wallet.h b/src/wallet.h new file mode 100644 index 00000000..f9d2ea09 --- /dev/null +++ b/src/wallet.h @@ -0,0 +1,441 @@ +// Copyright (c) 2009-2011 Satoshi Nakamoto & Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_WALLET_H +#define BITCOIN_WALLET_H + +#include "bignum.h" +#include "script.h" + +class CWalletTx; +class CReserveKey; +class CWalletDB; + +extern std::map mapWallet; +extern std::vector vWalletUpdated; +extern CCriticalSection cs_mapWallet; + +extern std::map mapRequestCount; +extern CCriticalSection cs_mapRequestCount; + +extern std::map mapAddressBook; +extern CCriticalSection cs_mapAddressBook; + +extern std::vector vchDefaultKey; +extern CKey keyUser; + +bool AddToWallet(const CWalletTx& wtxIn); +bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate = false); +bool EraseFromWallet(uint256 hash); +void WalletUpdateSpent(const COutPoint& prevout); +int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); +void ReacceptWalletTransactions(); +void ResendWalletTransactions(); +int64 GetBalance(); +bool CreateTransaction(const std::vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); +bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); +bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); +bool BroadcastTransaction(CWalletTx& wtxNew); +std::string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); +std::string SendMoneyToBitcoinAddress(std::string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); + + + +// +// A transaction with a bunch of additional info that only the owner cares +// about. It includes any unrecorded transactions needed to link it back +// to the block chain. +// +class CWalletTx : public CMerkleTx +{ +public: + std::vector vtxPrev; + std::map mapValue; + std::vector > vOrderForm; + unsigned int fTimeReceivedIsTxTime; + unsigned int nTimeReceived; // time received by this node + char fFromMe; + std::string strFromAccount; + std::vector vfSpent; + + // memory only + mutable char fDebitCached; + mutable char fCreditCached; + mutable char fAvailableCreditCached; + mutable char fChangeCached; + mutable int64 nDebitCached; + mutable int64 nCreditCached; + mutable int64 nAvailableCreditCached; + mutable int64 nChangeCached; + + // memory only UI hints + mutable unsigned int nTimeDisplayed; + mutable int nLinesDisplayed; + mutable char fConfirmedDisplayed; + + + CWalletTx() + { + Init(); + } + + CWalletTx(const CMerkleTx& txIn) : CMerkleTx(txIn) + { + Init(); + } + + CWalletTx(const CTransaction& txIn) : CMerkleTx(txIn) + { + Init(); + } + + void Init() + { + vtxPrev.clear(); + mapValue.clear(); + vOrderForm.clear(); + fTimeReceivedIsTxTime = false; + nTimeReceived = 0; + fFromMe = false; + strFromAccount.clear(); + vfSpent.clear(); + fDebitCached = false; + fCreditCached = false; + fAvailableCreditCached = false; + fChangeCached = false; + nDebitCached = 0; + nCreditCached = 0; + nAvailableCreditCached = 0; + nChangeCached = 0; + nTimeDisplayed = 0; + nLinesDisplayed = 0; + fConfirmedDisplayed = false; + } + + IMPLEMENT_SERIALIZE + ( + CWalletTx* pthis = const_cast(this); + if (fRead) + pthis->Init(); + char fSpent = false; + + if (!fRead) + { + pthis->mapValue["fromaccount"] = pthis->strFromAccount; + + std::string str; + BOOST_FOREACH(char f, vfSpent) + { + str += (f ? '1' : '0'); + if (f) + fSpent = true; + } + pthis->mapValue["spent"] = str; + } + + nSerSize += SerReadWrite(s, *(CMerkleTx*)this, nType, nVersion,ser_action); + READWRITE(vtxPrev); + READWRITE(mapValue); + READWRITE(vOrderForm); + READWRITE(fTimeReceivedIsTxTime); + READWRITE(nTimeReceived); + READWRITE(fFromMe); + READWRITE(fSpent); + + if (fRead) + { + pthis->strFromAccount = pthis->mapValue["fromaccount"]; + + if (mapValue.count("spent")) + BOOST_FOREACH(char c, pthis->mapValue["spent"]) + pthis->vfSpent.push_back(c != '0'); + else + pthis->vfSpent.assign(vout.size(), fSpent); + } + + pthis->mapValue.erase("fromaccount"); + pthis->mapValue.erase("version"); + pthis->mapValue.erase("spent"); + ) + + // marks certain txout's as spent + // returns true if any update took place + bool UpdateSpent(const std::vector& vfNewSpent) + { + bool fReturn = false; + for (int i=0; i < vfNewSpent.size(); i++) + { + if (i == vfSpent.size()) + break; + + if (vfNewSpent[i] && !vfSpent[i]) + { + vfSpent[i] = true; + fReturn = true; + fAvailableCreditCached = false; + } + } + return fReturn; + } + + void MarkDirty() + { + fCreditCached = false; + fAvailableCreditCached = false; + fDebitCached = false; + fChangeCached = false; + } + + void MarkSpent(unsigned int nOut) + { + if (nOut >= vout.size()) + throw std::runtime_error("CWalletTx::MarkSpent() : nOut out of range"); + vfSpent.resize(vout.size()); + if (!vfSpent[nOut]) + { + vfSpent[nOut] = true; + fAvailableCreditCached = false; + } + } + + bool IsSpent(unsigned int nOut) const + { + if (nOut >= vout.size()) + throw std::runtime_error("CWalletTx::IsSpent() : nOut out of range"); + if (nOut >= vfSpent.size()) + return false; + return (!!vfSpent[nOut]); + } + + int64 GetDebit() const + { + if (vin.empty()) + return 0; + if (fDebitCached) + return nDebitCached; + nDebitCached = CTransaction::GetDebit(); + fDebitCached = true; + return nDebitCached; + } + + int64 GetCredit(bool fUseCache=true) const + { + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + // GetBalance can assume transactions in mapWallet won't change + if (fUseCache && fCreditCached) + return nCreditCached; + nCreditCached = CTransaction::GetCredit(); + fCreditCached = true; + return nCreditCached; + } + + int64 GetAvailableCredit(bool fUseCache=true) const + { + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + if (fUseCache && fAvailableCreditCached) + return nAvailableCreditCached; + + int64 nCredit = 0; + for (int i = 0; i < vout.size(); i++) + { + if (!IsSpent(i)) + { + const CTxOut &txout = vout[i]; + nCredit += txout.GetCredit(); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); + } + } + + nAvailableCreditCached = nCredit; + fAvailableCreditCached = true; + return nCredit; + } + + + int64 GetChange() const + { + if (fChangeCached) + return nChangeCached; + nChangeCached = CTransaction::GetChange(); + fChangeCached = true; + return nChangeCached; + } + + void GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, std::list >& listReceived, + std::list >& listSent, int64& nFee, std::string& strSentAccount) const; + + void GetAccountAmounts(const std::string& strAccount, int64& nGenerated, int64& nReceived, + int64& nSent, int64& nFee) const; + + bool IsFromMe() const + { + return (GetDebit() > 0); + } + + bool IsConfirmed() const + { + // Quick answer in most cases + if (!IsFinal()) + return false; + if (GetDepthInMainChain() >= 1) + return true; + if (!IsFromMe()) // using wtx's cached debit + return false; + + // If no confirmations but it's from us, we can still + // consider it confirmed if all dependencies are confirmed + std::map mapPrev; + std::vector vWorkQueue; + vWorkQueue.reserve(vtxPrev.size()+1); + vWorkQueue.push_back(this); + for (int i = 0; i < vWorkQueue.size(); i++) + { + const CMerkleTx* ptx = vWorkQueue[i]; + + if (!ptx->IsFinal()) + return false; + if (ptx->GetDepthInMainChain() >= 1) + continue; + if (!ptx->IsFromMe()) + return false; + + if (mapPrev.empty()) + BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) + mapPrev[tx.GetHash()] = &tx; + + BOOST_FOREACH(const CTxIn& txin, ptx->vin) + { + if (!mapPrev.count(txin.prevout.hash)) + return false; + vWorkQueue.push_back(mapPrev[txin.prevout.hash]); + } + } + return true; + } + + bool WriteToDisk(); + + int64 GetTxTime() const; + int GetRequestCount() const; + + void AddSupportingTransactions(CTxDB& txdb); + + bool AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs=true); + bool AcceptWalletTransaction(); + + void RelayWalletTransaction(CTxDB& txdb); + void RelayWalletTransaction(); +}; + + +// +// Private key that includes an expiration date in case it never gets used. +// +class CWalletKey +{ +public: + CPrivKey vchPrivKey; + int64 nTimeCreated; + int64 nTimeExpires; + std::string strComment; + //// todo: add something to note what created it (user, getnewaddress, change) + //// maybe should have a map property map + + CWalletKey(int64 nExpires=0) + { + nTimeCreated = (nExpires ? GetTime() : 0); + nTimeExpires = nExpires; + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vchPrivKey); + READWRITE(nTimeCreated); + READWRITE(nTimeExpires); + READWRITE(strComment); + ) +}; + + + + + + +// +// Account information. +// Stored in wallet with key "acc"+string account name +// +class CAccount +{ +public: + std::vector vchPubKey; + + CAccount() + { + SetNull(); + } + + void SetNull() + { + vchPubKey.clear(); + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vchPubKey); + ) +}; + + + +// +// Internal transfers. +// Database key is acentry +// +class CAccountingEntry +{ +public: + std::string strAccount; + int64 nCreditDebit; + int64 nTime; + std::string strOtherAccount; + std::string strComment; + + CAccountingEntry() + { + SetNull(); + } + + void SetNull() + { + nCreditDebit = 0; + nTime = 0; + strAccount.clear(); + strOtherAccount.clear(); + strComment.clear(); + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + // Note: strAccount is serialized as part of the key, not here. + READWRITE(nCreditDebit); + READWRITE(nTime); + READWRITE(strOtherAccount); + READWRITE(strComment); + ) +}; + +#endif