aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPieter Wuille <pieter.wuille@gmail.com>2011-06-01 18:27:05 +0200
committerPieter Wuille <pieter.wuille@gmail.com>2011-06-15 11:05:55 +0200
commite89b9f6a2abaa120ff0fc3cea9ae364e8cbd25e4 (patch)
treeef0789aeadd8ff3e53c2705ea9153f0e612f689c
parent19ea44208f7c2cf335c654126deb81406036e328 (diff)
move wallet code to separate file
This introduces two new source files, keystore.cpp and wallet.cpp with corresponding headers. Code is moved from main and db, in a preparation for a follow-up commit which introduces the classes CWallet and CKeyStore.
-rw-r--r--src/db.cpp103
-rw-r--r--src/db.h34
-rw-r--r--src/headers.h2
-rw-r--r--src/keystore.cpp33
-rw-r--r--src/keystore.h10
-rw-r--r--src/main.cpp1281
-rw-r--r--src/main.h446
-rw-r--r--src/makefile.mingw4
-rw-r--r--src/makefile.osx4
-rw-r--r--src/makefile.unix4
-rw-r--r--src/noui.h2
-rw-r--r--src/script.h1
-rw-r--r--src/ui.cpp1
-rw-r--r--src/ui.h3
-rw-r--r--src/wallet.cpp1056
-rw-r--r--src/wallet.h441
16 files changed, 1735 insertions, 1690 deletions
diff --git a/src/db.cpp b/src/db.cpp
index b67e2a6452..30e4bb0d8b 100644
--- a/src/db.cpp
+++ b/src/db.cpp
@@ -584,9 +584,6 @@ bool LoadAddresses()
// CWalletDB
//
-static set<int64> 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<unsigned char> 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 9826194ed0..577983725b 100644
--- a/src/db.h
+++ b/src/db.h
@@ -25,8 +25,6 @@ class CAccount;
class CAccountingEntry;
class CBlockLocator;
-extern std::map<std::string, std::string> mapAddressBook;
-extern CCriticalSection cs_mapAddressBook;
extern std::vector<unsigned char> vchDefaultKey;
extern bool fClient;
extern int nBestHeight;
@@ -39,6 +37,8 @@ extern DbEnv dbenv;
extern void DBFlush(bool fShutdown);
extern std::vector<unsigned char> GetKeyFromKeyPool();
extern int64 GetOldestKeyPoolTime();
+extern void ThreadFlushWalletDB(void* parg);
+
@@ -494,33 +494,9 @@ public:
ReturnKey();
}
- std::vector<unsigned char> 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<unsigned char> GetReservedKey();
+ void KeepKey();
+ void ReturnKey();
};
#endif
diff --git a/src/headers.h b/src/headers.h
index 9e81e27d1d..d1844eb24e 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 0000000000..51f39a5251
--- /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<unsigned char> 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 0000000000..2f37ec5078
--- /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<unsigned char> GenerateNewKey();
+
+#endif
diff --git a/src/main.cpp b/src/main.cpp
index 108842f3a1..8949b97be6 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -54,6 +54,9 @@ CCriticalSection cs_mapRequestCount;
map<string, string> mapAddressBook;
CCriticalSection cs_mapAddressBook;
+set<int64> setKeyPool;
+CCriticalSection cs_setKeyPool;
+
vector<unsigned char> vchDefaultKey;
double dHashesPerSec;
@@ -81,160 +84,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<unsigned char> 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<map<uint256, CWalletTx>::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<uint256, CWalletTx>::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<uint256, CWalletTx>::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<uint256, CWalletTx>::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<uint256, CBlockIndex*>::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<uint256, int>::iterator mi = mapRequestCount.find(hashBlock);
- if (mi != mapRequestCount.end())
- nRequests = (*mi).second;
- }
- }
- else
- {
- // Did anyone request this transaction?
- map<uint256, int>::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<uint256, int>::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<pair<string, int64> >& listReceived,
- list<pair<string, int64> >& 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<unsigned char> 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<pair<string, int64> > listReceived;
- list<pair<string, int64> > 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<uint256> 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<uint256, const CMerkleTx*> mapWalletPrev;
- set<uint256> 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()
+bool CWalletTx::AcceptWalletTransaction()
{
CTxDB txdb("r");
- bool fRepeat = true;
- while (fRepeat) CRITICAL_BLOCK(cs_mapWallet)
- {
- fRepeat = false;
- vector<CDiskTxPos> 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()
-{
- // 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<unsigned int, CWalletTx*> 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<char> 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<unsigned int, vector<unsigned char> > 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<char> 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<uint256, CWalletTx>::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<pair<CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet)
+void ThreadBitcoinMiner(void* parg)
{
- setCoinsRet.clear();
- nValueRet = 0;
-
- // List of values less than target
- pair<int64, pair<CWalletTx*,unsigned int> > coinLowestLarger;
- coinLowestLarger.first = INT64_MAX;
- coinLowestLarger.second.first = NULL;
- vector<pair<int64, pair<CWalletTx*,unsigned int> > > vValue;
- int64 nTotalLower = 0;
-
- CRITICAL_BLOCK(cs_mapWallet)
- {
- vector<CWalletTx*> vCoins;
- vCoins.reserve(mapWallet.size());
- for (map<uint256, CWalletTx>::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<int64,pair<CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin,i));
-
- if (n == nTargetValue)
- {
- setCoinsRet.insert(coin.second);
- nValueRet += coin.first;
- return true;
- }
- else if (n < nTargetValue + CENT)
- {
- vValue.push_back(coin);
- nTotalLower += n;
- }
- else if (n < coinLowestLarger.first)
- {
- coinLowestLarger = coin;
- }
- }
- }
- }
-
- if (nTotalLower == nTargetValue || nTotalLower == nTargetValue + CENT)
- {
- for (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<char> vfIncluded;
- vector<char> 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<pair<CWalletTx*,unsigned int> >& 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<pair<CScript, int64> >& 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<pair<CWalletTx*,unsigned int> > 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<unsigned char> 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<CTxOut>::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<CScript, int64> > 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<CWalletTx*> 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 7aa6d41c30..2ebb8b867e 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 <list>
@@ -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<int64> setKeyPool;
+extern CCriticalSection cs_setKeyPool;
extern unsigned int nTransactionsUpdated;
-extern std::map<uint256, int> mapRequestCount;
-extern CCriticalSection cs_mapRequestCount;
-extern std::map<std::string, std::string> mapAddressBook;
-extern CCriticalSection cs_mapAddressBook;
-extern std::vector<unsigned char> vchDefaultKey;
extern double dHashesPerSec;
extern int64 nHPSTimerStart;
+extern int64 nTimeBestReceived;
+extern std::map<std::string, std::string> 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<unsigned char> 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<std::pair<CScript, int64> >& 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<CMerkleTx> vtxPrev;
- std::map<std::string, std::string> mapValue;
- std::vector<std::pair<std::string, std::string> > vOrderForm;
- unsigned int fTimeReceivedIsTxTime;
- unsigned int nTimeReceived; // time received by this node
- char fFromMe;
- std::string strFromAccount;
- std::vector<char> 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<CWalletTx*>(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<char>& 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<std::pair<std::string /* address */, int64> >& listReceived,
- std::list<std::pair<std::string /* address */, int64> >& 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<uint256, const CMerkleTx*> mapPrev;
- std::vector<const CMerkleTx*> 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<string, string> 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<unsigned char> vchPubKey;
-
- CAccount()
- {
- SetNull();
- }
-
- void SetNull()
- {
- vchPubKey.clear();
- }
-
- IMPLEMENT_SERIALIZE
- (
- if (!(nType & SER_GETHASH))
- READWRITE(nVersion);
- READWRITE(vchPubKey);
- )
-};
-
-
-
-//
-// Internal transfers.
-// Database key is acentry<account><counter>
-//
-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<uint256, CTransaction> mapTransactions;
-extern std::map<uint256, CWalletTx> mapWallet;
-extern std::vector<uint256> vWalletUpdated;
-extern CCriticalSection cs_mapWallet;
extern std::map<std::vector<unsigned char>, CPrivKey> mapKeys;
extern std::map<uint160, std::vector<unsigned char> > mapPubKeys;
extern CCriticalSection cs_mapKeys;
-extern CKey keyUser;
#endif
diff --git a/src/makefile.mingw b/src/makefile.mingw
index c3a964e8fc..fccc0e36af 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 4836ea3f4f..4e173a9dfa 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 4f2da37894..f2d85b9dd3 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 afb19526c1..d0072df7f2 100644
--- a/src/noui.h
+++ b/src/noui.h
@@ -5,6 +5,8 @@
#define BITCOIN_NOUI_H
#include <string>
+#include <boost/function.hpp>
+#include "wallet.h"
typedef void wxWindow;
#define wxYES 0x00000002
diff --git a/src/script.h b/src/script.h
index 22a6020dce..efafec4470 100644
--- a/src/script.h
+++ b/src/script.h
@@ -5,6 +5,7 @@
#define H_BITCOIN_SCRIPT
#include "base58.h"
+#include "keystore.h"
#include <string>
#include <vector>
diff --git a/src/ui.cpp b/src/ui.cpp
index 0a7d45578f..72e8fe2ec8 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 <boost/filesystem/fstream.hpp>
diff --git a/src/ui.h b/src/ui.h
index 16643db421..3f06ad90cb 100644
--- a/src/ui.h
+++ b/src/ui.h
@@ -4,6 +4,9 @@
#ifndef BITCOIN_UI_H
#define BITCOIN_UI_H
+#include <boost/function.hpp>
+#include "wallet.h"
+
DECLARE_EVENT_TYPE(wxEVT_UITHREADCALL, -1)
diff --git a/src/wallet.cpp b/src/wallet.cpp
new file mode 100644
index 0000000000..a9fc92fd78
--- /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<uint256, CWalletTx>::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<map<uint256, CWalletTx>::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<uint256, CWalletTx>::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<uint256, CWalletTx>::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<uint256, CBlockIndex*>::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<uint256, int>::iterator mi = mapRequestCount.find(hashBlock);
+ if (mi != mapRequestCount.end())
+ nRequests = (*mi).second;
+ }
+ }
+ else
+ {
+ // Did anyone request this transaction?
+ map<uint256, int>::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<uint256, int>::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<pair<string, int64> >& listReceived,
+ list<pair<string, int64> >& 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<unsigned char> 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<pair<string, int64> > listReceived;
+ list<pair<string, int64> > 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<uint256> 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<uint256, const CMerkleTx*> mapWalletPrev;
+ set<uint256> 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<CDiskTxPos> 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<unsigned int, CWalletTx*> 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<uint256, CWalletTx>::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<pair<CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet)
+{
+ setCoinsRet.clear();
+ nValueRet = 0;
+
+ // List of values less than target
+ pair<int64, pair<CWalletTx*,unsigned int> > coinLowestLarger;
+ coinLowestLarger.first = INT64_MAX;
+ coinLowestLarger.second.first = NULL;
+ vector<pair<int64, pair<CWalletTx*,unsigned int> > > vValue;
+ int64 nTotalLower = 0;
+
+ CRITICAL_BLOCK(cs_mapWallet)
+ {
+ vector<CWalletTx*> vCoins;
+ vCoins.reserve(mapWallet.size());
+ for (map<uint256, CWalletTx>::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<int64,pair<CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin,i));
+
+ if (n == nTargetValue)
+ {
+ setCoinsRet.insert(coin.second);
+ nValueRet += coin.first;
+ return true;
+ }
+ else if (n < nTargetValue + CENT)
+ {
+ vValue.push_back(coin);
+ nTotalLower += n;
+ }
+ else if (n < coinLowestLarger.first)
+ {
+ coinLowestLarger = coin;
+ }
+ }
+ }
+ }
+
+ if (nTotalLower == nTargetValue || nTotalLower == nTargetValue + CENT)
+ {
+ for (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<char> vfIncluded;
+ vector<char> 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<pair<CWalletTx*,unsigned int> >& 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<pair<CScript, int64> >& 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<pair<CWalletTx*,unsigned int> > 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<unsigned char> 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<CTxOut>::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<CScript, int64> > 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<CWalletTx*> 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<unsigned char> 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<unsigned char> 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 0000000000..f9d2ea0989
--- /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<uint256, CWalletTx> mapWallet;
+extern std::vector<uint256> vWalletUpdated;
+extern CCriticalSection cs_mapWallet;
+
+extern std::map<uint256, int> mapRequestCount;
+extern CCriticalSection cs_mapRequestCount;
+
+extern std::map<std::string, std::string> mapAddressBook;
+extern CCriticalSection cs_mapAddressBook;
+
+extern std::vector<unsigned char> 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<std::pair<CScript, int64> >& 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<CMerkleTx> vtxPrev;
+ std::map<std::string, std::string> mapValue;
+ std::vector<std::pair<std::string, std::string> > vOrderForm;
+ unsigned int fTimeReceivedIsTxTime;
+ unsigned int nTimeReceived; // time received by this node
+ char fFromMe;
+ std::string strFromAccount;
+ std::vector<char> 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<CWalletTx*>(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<char>& 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<std::pair<std::string /* address */, int64> >& listReceived,
+ std::list<std::pair<std::string /* address */, int64> >& 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<uint256, const CMerkleTx*> mapPrev;
+ std::vector<const CMerkleTx*> 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<string, string> 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<unsigned char> vchPubKey;
+
+ CAccount()
+ {
+ SetNull();
+ }
+
+ void SetNull()
+ {
+ vchPubKey.clear();
+ }
+
+ IMPLEMENT_SERIALIZE
+ (
+ if (!(nType & SER_GETHASH))
+ READWRITE(nVersion);
+ READWRITE(vchPubKey);
+ )
+};
+
+
+
+//
+// Internal transfers.
+// Database key is acentry<account><counter>
+//
+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