-// Copyright (c) 2009-2011 Satoshi Nakamoto & Bitcoin developers
+// Copyright (c) 2009-2011 Satoshi Nakamoto
+// Copyright (c) 2011 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin 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"
+#include "crypter.h"
+#include "checkpoints.h"
using namespace std;
-
//////////////////////////////////////////////////////////////////////////////
//
// mapWallet
//
-void WalletUpdateSpent(const COutPoint& prevout)
+bool CWallet::AddKey(const CKey& key)
+{
+ if (!CCryptoKeyStore::AddKey(key))
+ return false;
+ if (!fFileBacked)
+ return true;
+ if (!IsCrypted())
+ return CWalletDB(strWalletFile).WriteKey(key.GetPubKey(), key.GetPrivKey());
+ return true;
+}
+
+bool CWallet::AddCryptedKey(const vector<unsigned char> &vchPubKey, const vector<unsigned char> &vchCryptedSecret)
+{
+ if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret))
+ return false;
+ if (!fFileBacked)
+ return true;
+ CRITICAL_BLOCK(cs_wallet)
+ {
+ if (pwalletdbEncryption)
+ return pwalletdbEncryption->WriteCryptedKey(vchPubKey, vchCryptedSecret);
+ else
+ return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret);
+ }
+ return false;
+}
+
+// ppcoin: optional setting to create coinstake only when unlocked;
+// serves to disable the trivial sendmoney when OS account compromised
+bool fWalletUnlockStakeOnly = false;
+
+bool CWallet::Unlock(const SecureString& strWalletPassphrase)
+{
+ if (!IsLocked())
+ return false;
+
+ CCrypter crypter;
+ CKeyingMaterial vMasterKey;
+
+ CRITICAL_BLOCK(cs_wallet)
+ BOOST_FOREACH(const MasterKeyMap::value_type& pMasterKey, mapMasterKeys)
+ {
+ if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
+ return false;
+ if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey))
+ return false;
+ if (CCryptoKeyStore::Unlock(vMasterKey))
+ return true;
+ }
+ return false;
+}
+
+bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase)
+{
+ bool fWasLocked = IsLocked();
+
+ CRITICAL_BLOCK(cs_wallet)
+ {
+ Lock();
+
+ CCrypter crypter;
+ CKeyingMaterial vMasterKey;
+ BOOST_FOREACH(MasterKeyMap::value_type& pMasterKey, mapMasterKeys)
+ {
+ if(!crypter.SetKeyFromPassphrase(strOldWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
+ return false;
+ if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey))
+ return false;
+ if (CCryptoKeyStore::Unlock(vMasterKey))
+ {
+ int64 nStartTime = GetTimeMillis();
+ crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);
+ pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime)));
+
+ nStartTime = GetTimeMillis();
+ crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);
+ pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + pMasterKey.second.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2;
+
+ if (pMasterKey.second.nDeriveIterations < 25000)
+ pMasterKey.second.nDeriveIterations = 25000;
+
+ printf("Wallet passphrase changed to an nDeriveIterations of %i\n", pMasterKey.second.nDeriveIterations);
+
+ if (!crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
+ return false;
+ if (!crypter.Encrypt(vMasterKey, pMasterKey.second.vchCryptedKey))
+ return false;
+ CWalletDB(strWalletFile).WriteMasterKey(pMasterKey.first, pMasterKey.second);
+ if (fWasLocked)
+ Lock();
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+// This class implements an addrIncoming entry that causes pre-0.4
+// clients to crash on startup if reading a private-key-encrypted wallet.
+class CCorruptAddress
+{
+public:
+ IMPLEMENT_SERIALIZE
+ (
+ if (nType & SER_DISK)
+ READWRITE(nVersion);
+ )
+};
+
+bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
+{
+ if (IsCrypted())
+ return false;
+
+ CKeyingMaterial vMasterKey;
+ RandAddSeedPerfmon();
+
+ vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE);
+ RAND_bytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE);
+
+ CMasterKey kMasterKey;
+
+ RandAddSeedPerfmon();
+ kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE);
+ RAND_bytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE);
+
+ CCrypter crypter;
+ int64 nStartTime = GetTimeMillis();
+ crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod);
+ kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime));
+
+ nStartTime = GetTimeMillis();
+ crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod);
+ kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2;
+
+ if (kMasterKey.nDeriveIterations < 25000)
+ kMasterKey.nDeriveIterations = 25000;
+
+ printf("Encrypting Wallet with an nDeriveIterations of %i\n", kMasterKey.nDeriveIterations);
+
+ if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod))
+ return false;
+ if (!crypter.Encrypt(vMasterKey, kMasterKey.vchCryptedKey))
+ return false;
+
+ CRITICAL_BLOCK(cs_wallet)
+ {
+ mapMasterKeys[++nMasterKeyMaxID] = kMasterKey;
+ if (fFileBacked)
+ {
+ pwalletdbEncryption = new CWalletDB(strWalletFile);
+ pwalletdbEncryption->TxnBegin();
+ pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey);
+ }
+
+ if (!EncryptKeys(vMasterKey))
+ {
+ if (fFileBacked)
+ pwalletdbEncryption->TxnAbort();
+ exit(1); //We now probably have half of our keys encrypted in memory, and half not...die and let the user reload their unencrypted wallet.
+ }
+
+ if (fFileBacked)
+ {
+ CCorruptAddress corruptAddress;
+ pwalletdbEncryption->WriteSetting("addrIncoming", corruptAddress);
+ if (!pwalletdbEncryption->TxnCommit())
+ exit(1); //We now have keys encrypted in memory, but no on disk...die to avoid confusion and let the user reload their unencrypted wallet.
+
+ pwalletdbEncryption->Close();
+ pwalletdbEncryption = NULL;
+ }
+
+ Lock();
+ Unlock(strWalletPassphrase);
+ NewKeyPool();
+ Lock();
+
+ // Need to completely rewrite the wallet file; if we don't, bdb might keep
+ // bits of the unencrypted private key in slack space in the database file.
+ CDB::Rewrite(strWalletFile);
+ }
+
+ return true;
+}
+
+void CWallet::WalletUpdateSpent(const CTransaction &tx)
{
// 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)
+ CRITICAL_BLOCK(cs_wallet)
{
- map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
- if (mi != mapWallet.end())
+ BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
- CWalletTx& wtx = (*mi).second;
- if (!wtx.IsSpent(prevout.n) && wtx.vout[prevout.n].IsMine())
+ map<uint256, CWalletTx>::iterator mi = mapWallet.find(txin.prevout.hash);
+ if (mi != mapWallet.end())
{
- 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);
+ CWalletTx& wtx = (*mi).second;
+ if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n]))
+ {
+ printf("WalletUpdateSpent found spent coin %sppc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
+ wtx.MarkSpent(txin.prevout.n);
+ wtx.WriteToDisk();
+ vWalletUpdated.push_back(txin.prevout.hash);
+ }
}
}
}
}
-bool AddToWallet(const CWalletTx& wtxIn)
+bool CWallet::AddToWallet(const CWalletTx& wtxIn)
{
uint256 hash = wtxIn.GetHash();
- CRITICAL_BLOCK(cs_mapWallet)
+ CRITICAL_BLOCK(cs_wallet)
{
// 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;
+ wtx.pwallet = this;
bool fInsertedNew = ret.second;
if (fInsertedNew)
wtx.nTimeReceived = GetAdjustedTime();
if (fInsertedNew || fUpdated)
if (!wtx.WriteToDisk())
return false;
-
+#ifndef QT_GUI
// If default receiving address gets used, replace it with a new one
CScript scriptDefaultKey;
scriptDefaultKey.SetBitcoinAddress(vchDefaultKey);
{
if (txout.scriptPubKey == scriptDefaultKey)
{
- CWalletDB walletdb;
- vchDefaultKey = GetKeyFromKeyPool();
- walletdb.WriteDefaultKey(vchDefaultKey);
- walletdb.WriteName(PubKeyToAddress(vchDefaultKey), "");
+ std::vector<unsigned char> newDefaultKey;
+ if (GetKeyFromPool(newDefaultKey, false))
+ {
+ SetDefaultKey(newDefaultKey);
+ SetAddressBookName(CBitcoinAddress(vchDefaultKey), "");
+ }
}
}
-
+#endif
// Notify UI
vWalletUpdated.push_back(hash);
+
+ // since AddToWallet is called directly for self-originating transactions, check for consumption of own coins
+ WalletUpdateSpent(wtx);
}
// Refresh UI
return true;
}
-bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate)
+// Add a transaction to the wallet, or update it.
+// pblock is optional, but should be provided if the transaction is known to be in a block.
+// If fUpdate is true, existing transactions will be updated.
+bool CWallet::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())
+ CRITICAL_BLOCK(cs_wallet)
{
- CWalletTx wtx(tx);
- // Get merkle branch if transaction was found in a block
- if (pblock)
- wtx.SetMerkleBranch(pblock);
- return AddToWallet(wtx);
+ bool fExisted = mapWallet.count(hash);
+ if (fExisted && !fUpdate) return false;
+ if (fExisted || IsMine(tx) || IsFromMe(tx))
+ {
+ CWalletTx wtx(this,tx);
+ // Get merkle branch if transaction was found in a block
+ if (pblock)
+ wtx.SetMerkleBranch(pblock);
+ return AddToWallet(wtx);
+ }
+ else
+ WalletUpdateSpent(tx);
}
return false;
}
-bool EraseFromWallet(uint256 hash)
+bool CWallet::EraseFromWallet(uint256 hash)
{
- CRITICAL_BLOCK(cs_mapWallet)
+ if (!fFileBacked)
+ return false;
+ CRITICAL_BLOCK(cs_wallet)
{
if (mapWallet.erase(hash))
- CWalletDB().EraseTx(hash);
+ CWalletDB(strWalletFile).EraseTx(hash);
}
return true;
}
-bool CTxIn::IsMine() const
+bool CWallet::IsMine(const CTxIn &txin) const
{
- CRITICAL_BLOCK(cs_mapWallet)
+ CRITICAL_BLOCK(cs_wallet)
{
- map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
+ map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);
if (mi != mapWallet.end())
{
const CWalletTx& prev = (*mi).second;
- if (prevout.n < prev.vout.size())
- if (prev.vout[prevout.n].IsMine())
+ if (txin.prevout.n < prev.vout.size())
+ if (IsMine(prev.vout[txin.prevout.n]))
return true;
}
}
return false;
}
-int64 CTxIn::GetDebit() const
+int64 CWallet::GetDebit(const CTxIn &txin) const
{
- CRITICAL_BLOCK(cs_mapWallet)
+ CRITICAL_BLOCK(cs_wallet)
{
- map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
+ map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.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;
+ if (txin.prevout.n < prev.vout.size())
+ if (IsMine(prev.vout[txin.prevout.n]))
+ return prev.vout[txin.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;
}
{
// Returns -1 if it wasn't being tracked
int nRequests = -1;
- CRITICAL_BLOCK(cs_mapRequestCount)
+ CRITICAL_BLOCK(pwallet->cs_wallet)
{
- if (IsCoinBase())
+ if (IsCoinBase() || IsCoinStake())
{
// Generated block
if (hashBlock != 0)
{
- map<uint256, int>::iterator mi = mapRequestCount.find(hashBlock);
- if (mi != mapRequestCount.end())
+ map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock);
+ if (mi != pwallet->mapRequestCount.end())
nRequests = (*mi).second;
}
}
else
{
// Did anyone request this transaction?
- map<uint256, int>::iterator mi = mapRequestCount.find(GetHash());
- if (mi != mapRequestCount.end())
+ map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(GetHash());
+ if (mi != pwallet->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())
+ map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock);
+ if (mi != pwallet->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
+void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, list<pair<CBitcoinAddress, int64> >& listReceived,
+ list<pair<CBitcoinAddress, int64> >& listSent, int64& nFee, string& strSentAccount) const
{
nGeneratedImmature = nGeneratedMature = nFee = 0;
listReceived.clear();
listSent.clear();
strSentAccount = strFromAccount;
- if (IsCoinBase())
+ if (IsCoinBase() || IsCoinStake())
{
if (GetBlocksToMaturity() > 0)
- nGeneratedImmature = CTransaction::GetCredit();
+ nGeneratedImmature = pwallet->GetCredit(*this);
else
nGeneratedMature = GetCredit();
return;
// but non-standard clients might (so return a list of address/amount pairs)
BOOST_FOREACH(const CTxOut& txout, vout)
{
- string address;
- uint160 hash160;
+ CBitcoinAddress address;
vector<unsigned char> vchPubKey;
- if (ExtractHash160(txout.scriptPubKey, hash160))
- address = Hash160ToAddress(hash160);
- else if (ExtractPubKey(txout.scriptPubKey, false, vchPubKey))
- address = PubKeyToAddress(vchPubKey);
- else
+ if (!ExtractAddress(txout.scriptPubKey, NULL, address))
{
printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n",
this->GetHash().ToString().c_str());
}
// Don't report 'change' txouts
- if (nDebit > 0 && txout.IsChange())
+ if (nDebit > 0 && pwallet->IsChange(txout))
continue;
if (nDebit > 0)
listSent.push_back(make_pair(address, txout.nValue));
- if (txout.IsMine())
+ if (pwallet->IsMine(txout))
listReceived.push_back(make_pair(address, txout.nValue));
}
int64 allGeneratedImmature, allGeneratedMature, allFee;
allGeneratedImmature = allGeneratedMature = allFee = 0;
string strSentAccount;
- list<pair<string, int64> > listReceived;
- list<pair<string, int64> > listSent;
+ list<pair<CBitcoinAddress, int64> > listReceived;
+ list<pair<CBitcoinAddress, int64> > listSent;
GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
if (strAccount == "")
nGenerated = allGeneratedMature;
if (strAccount == strSentAccount)
{
- BOOST_FOREACH(const PAIRTYPE(string,int64)& s, listSent)
+ BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& s, listSent)
nSent += s.second;
nFee = allFee;
}
- CRITICAL_BLOCK(cs_mapAddressBook)
+ CRITICAL_BLOCK(pwallet->cs_wallet)
{
- BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listReceived)
+ BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listReceived)
{
- if (mapAddressBook.count(r.first))
+ if (pwallet->mapAddressBook.count(r.first))
{
- if (mapAddressBook[r.first] == strAccount)
- {
+ map<CBitcoinAddress, string>::const_iterator mi = pwallet->mapAddressBook.find(r.first);
+ if (mi != pwallet->mapAddressBook.end() && (*mi).second == strAccount)
nReceived += r.second;
- }
}
else if (strAccount.empty())
{
vWorkQueue.push_back(txin.prevout.hash);
// This critsect is OK because txdb is already open
- CRITICAL_BLOCK(cs_mapWallet)
+ CRITICAL_BLOCK(pwallet->cs_wallet)
{
map<uint256, const CMerkleTx*> mapWalletPrev;
set<uint256> setAlreadyDone;
setAlreadyDone.insert(hash);
CMerkleTx tx;
- if (mapWallet.count(hash))
+ map<uint256, CWalletTx>::const_iterator mi = pwallet->mapWallet.find(hash);
+ if (mi != pwallet->mapWallet.end())
{
- tx = mapWallet[hash];
- BOOST_FOREACH(const CMerkleTx& txWalletPrev, mapWallet[hash].vtxPrev)
+ tx = (*mi).second;
+ BOOST_FOREACH(const CMerkleTx& txWalletPrev, (*mi).second.vtxPrev)
mapWalletPrev[txWalletPrev.GetHash()] = &txWalletPrev;
}
else if (mapWalletPrev.count(hash))
bool CWalletTx::WriteToDisk()
{
- return CWalletDB().WriteTx(GetHash(), *this);
+ return CWalletDB(pwallet->strWalletFile).WriteTx(GetHash(), *this);
}
-int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
+// Scan the block chain (starting in pindexStart) for transactions
+// from or to us. If fUpdate is true, found transactions that already
+// exist in the wallet will be updated.
+int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
{
int ret = 0;
CBlockIndex* pindex = pindexStart;
- CRITICAL_BLOCK(cs_mapWallet)
+ CRITICAL_BLOCK(cs_wallet)
{
while (pindex)
{
return ret;
}
-void ReacceptWalletTransactions()
+void CWallet::ReacceptWalletTransactions()
{
CTxDB txdb("r");
bool fRepeat = true;
- while (fRepeat) CRITICAL_BLOCK(cs_mapWallet)
+ while (fRepeat) CRITICAL_BLOCK(cs_wallet)
{
fRepeat = false;
vector<CDiskTxPos> vMissingTx;
BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
{
CWalletTx& wtx = item.second;
- if (wtx.IsCoinBase() && wtx.IsSpent(0))
+ if ((wtx.IsCoinBase() && wtx.IsSpent(0)) || (wtx.IsCoinStake() && wtx.IsSpent(1)))
continue;
CTxIndex txindex;
{
if (wtx.IsSpent(i))
continue;
- if (!txindex.vSpent[i].IsNull() && wtx.vout[i].IsMine())
+ if (!txindex.vSpent[i].IsNull() && IsMine(wtx.vout[i]))
{
wtx.MarkSpent(i);
fUpdated = true;
}
if (fUpdated)
{
- printf("ReacceptWalletTransactions found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
+ printf("ReacceptWalletTransactions found spent coin %sppc %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())
+ if (!(wtx.IsCoinBase() || wtx.IsCoinStake()))
wtx.AcceptWalletTransaction(txdb, false);
}
}
{
BOOST_FOREACH(const CMerkleTx& tx, vtxPrev)
{
- if (!tx.IsCoinBase())
+ if (!(tx.IsCoinBase() || tx.IsCoinStake()))
{
uint256 hash = tx.GetHash();
if (!txdb.ContainsTx(hash))
RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx);
}
}
- if (!IsCoinBase())
+ if (!(IsCoinBase() || IsCoinStake()))
{
uint256 hash = GetHash();
if (!txdb.ContainsTx(hash))
RelayWalletTransaction(txdb);
}
-void ResendWalletTransactions()
+void CWallet::ResendWalletTransactions()
{
// Do this infrequently and randomly to avoid giving away
// that these are our transactions.
// Rebroadcast any of our txes that aren't in a block yet
printf("ResendWalletTransactions()\n");
CTxDB txdb("r");
- CRITICAL_BLOCK(cs_mapWallet)
+ CRITICAL_BLOCK(cs_wallet)
{
// Sort them in chronological order
multimap<unsigned int, CWalletTx*> mapSorted;
//
-int64 GetBalance()
+int64 CWallet::GetBalance() const
{
- int64 nStart = GetTimeMillis();
-
int64 nTotal = 0;
- CRITICAL_BLOCK(cs_mapWallet)
+ CRITICAL_BLOCK(cs_wallet)
{
- for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
- CWalletTx* pcoin = &(*it).second;
+ const CWalletTx* pcoin = &(*it).second;
if (!pcoin->IsFinal() || !pcoin->IsConfirmed())
continue;
nTotal += pcoin->GetAvailableCredit();
}
}
- //printf("GetBalance() %"PRI64d"ms\n", GetTimeMillis() - nStart);
return nTotal;
}
+int64 CWallet::GetUnconfirmedBalance() const
+{
+ int64 nTotal = 0;
+ CRITICAL_BLOCK(cs_wallet)
+ {
+ for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ {
+ const CWalletTx* pcoin = &(*it).second;
+ if (pcoin->IsFinal() && pcoin->IsConfirmed())
+ continue;
+ nTotal += pcoin->GetAvailableCredit();
+ }
+ }
+ return nTotal;
+}
+
+// ppcoin: total coins staked (non-spendable until maturity)
+int64 CWallet::GetStake() const
+{
+ int64 nTotal = 0;
+ CRITICAL_BLOCK(cs_wallet)
+ {
+ for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ {
+ const CWalletTx* pcoin = &(*it).second;
+ if (pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0)
+ nTotal += CWallet::GetCredit(*pcoin);
+ }
+ }
+ return nTotal;
+}
+
+int64 CWallet::GetNewMint() const
+{
+ int64 nTotal = 0;
+ CRITICAL_BLOCK(cs_wallet)
+ {
+ for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ {
+ const CWalletTx* pcoin = &(*it).second;
+ if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0)
+ nTotal += CWallet::GetCredit(*pcoin);
+ }
+ }
+ return nTotal;
+}
-bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set<pair<CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet)
+
+bool CWallet::SelectCoinsMinConf(int64 nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
{
setCoinsRet.clear();
nValueRet = 0;
// List of values less than target
- pair<int64, pair<CWalletTx*,unsigned int> > coinLowestLarger;
+ pair<int64, pair<const CWalletTx*,unsigned int> > coinLowestLarger;
coinLowestLarger.first = INT64_MAX;
coinLowestLarger.second.first = NULL;
- vector<pair<int64, pair<CWalletTx*,unsigned int> > > vValue;
+ vector<pair<int64, pair<const CWalletTx*,unsigned int> > > vValue;
int64 nTotalLower = 0;
- CRITICAL_BLOCK(cs_mapWallet)
+ CRITICAL_BLOCK(cs_wallet)
{
- vector<CWalletTx*> vCoins;
+ vector<const CWalletTx*> vCoins;
vCoins.reserve(mapWallet.size());
- for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
vCoins.push_back(&(*it).second);
random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt);
- BOOST_FOREACH(CWalletTx* pcoin, vCoins)
+ BOOST_FOREACH(const CWalletTx* pcoin, vCoins)
{
if (!pcoin->IsFinal() || !pcoin->IsConfirmed())
continue;
- if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
+ if ((pcoin->IsCoinBase() || pcoin->IsCoinStake()) && pcoin->GetBlocksToMaturity() > 0)
continue;
int nDepth = pcoin->GetDepthInMainChain();
for (int i = 0; i < pcoin->vout.size(); i++)
{
- if (pcoin->IsSpent(i) || !pcoin->vout[i].IsMine())
+ if (pcoin->IsSpent(i) || !IsMine(pcoin->vout[i]))
continue;
+ if (pcoin->nTime > nSpendTime)
+ continue; // ppcoin: timestamp must not exceed spend time
+
int64 n = pcoin->vout[i].nValue;
if (n <= 0)
continue;
- pair<int64,pair<CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin,i));
+ pair<int64,pair<const CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin,i));
if (n == nTargetValue)
{
return true;
}
-bool SelectCoins(int64 nTargetValue, set<pair<CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet)
+bool CWallet::SelectCoins(int64 nTargetValue, unsigned int nSpendTime, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
{
- return (SelectCoinsMinConf(nTargetValue, 1, 6, setCoinsRet, nValueRet) ||
- SelectCoinsMinConf(nTargetValue, 1, 1, setCoinsRet, nValueRet) ||
- SelectCoinsMinConf(nTargetValue, 0, 1, setCoinsRet, nValueRet));
+ return (SelectCoinsMinConf(nTargetValue, nSpendTime, 1, 6, setCoinsRet, nValueRet) ||
+ SelectCoinsMinConf(nTargetValue, nSpendTime, 1, 1, setCoinsRet, nValueRet) ||
+ SelectCoinsMinConf(nTargetValue, nSpendTime, 0, 1, setCoinsRet, nValueRet));
}
-bool CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet)
+bool CWallet::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 (vecSend.empty() || nValue < 0)
return false;
+ wtxNew.pwallet = this;
+
CRITICAL_BLOCK(cs_main)
+ CRITICAL_BLOCK(cs_wallet)
{
// txdb must be opened before the mapWallet lock
CTxDB txdb("r");
- CRITICAL_BLOCK(cs_mapWallet)
{
nFeeRet = nTransactionFee;
loop
wtxNew.vout.push_back(CTxOut(s.second, s.first));
// Choose coins to use
- set<pair<CWalletTx*,unsigned int> > setCoins;
+ set<pair<const CWalletTx*,unsigned int> > setCoins;
int64 nValueIn = 0;
- if (!SelectCoins(nTotalValue, setCoins, nValueIn))
+ if (!SelectCoins(nTotalValue, wtxNew.nTime, setCoins, nValueIn))
return false;
- BOOST_FOREACH(PAIRTYPE(CWalletTx*, unsigned int) pcoin, setCoins)
+ BOOST_FOREACH(PAIRTYPE(const 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)
+ 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
// Reserve a new key pair from key pool
vector<unsigned char> vchPubKey = reservekey.GetReservedKey();
- assert(mapKeys.count(vchPubKey));
+ // assert(mapKeys.count(vchPubKey));
// Fill a vout to ourself, using same address type as the payment
CScript scriptChange;
- if (vecSend[0].first.GetBitcoinAddressHash160() != 0)
+ if (vecSend[0].first.GetBitcoinAddress().IsValid())
scriptChange.SetBitcoinAddress(vchPubKey);
else
scriptChange << vchPubKey << OP_CHECKSIG;
reservekey.ReturnKey();
// Fill vin
- BOOST_FOREACH(const PAIRTYPE(CWalletTx*,unsigned int)& coin, setCoins)
+ BOOST_FOREACH(const PAIRTYPE(const 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++))
+ BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
+ if (!SignSignature(*this, *coin.first, wtxNew, nIn++))
return false;
// Limit size
// Check that enough fee is included
int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000);
- bool fAllowFree = CTransaction::AllowFree(dPriority);
- int64 nMinFee = wtxNew.GetMinFee(1, fAllowFree);
+ int64 nMinFee = wtxNew.GetMinFee(1, false);
if (nFeeRet < max(nPayFee, nMinFee))
{
nFeeRet = max(nPayFee, nMinFee);
return true;
}
-bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet)
+bool CWallet::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);
}
+// ppcoin: create coin stake transaction
+bool CWallet::CreateCoinStake(unsigned int nBits, CTransaction& txNew)
+{
+ CBigNum bnTargetPerCoinDay;
+ bnTargetPerCoinDay.SetCompact(nBits);
+
+ CRITICAL_BLOCK(cs_main)
+ CRITICAL_BLOCK(cs_wallet)
+ {
+ txNew.vin.clear();
+ txNew.vout.clear();
+ // Mark coin stake transaction
+ CScript scriptEmpty;
+ scriptEmpty.clear();
+ txNew.vout.push_back(CTxOut(0, scriptEmpty));
+ // Choose coins to use
+ int64 nBalance = GetBalance();
+ if (nBalance <= nBalanceReserve)
+ return false;
+ set<pair<const CWalletTx*,unsigned int> > setCoins;
+ vector<const CWalletTx*> vwtxPrev;
+ int64 nValueIn = 0;
+ if (!SelectCoins(nBalance - nBalanceReserve, txNew.nTime, setCoins, nValueIn))
+ return false;
+ if (setCoins.empty())
+ return false;
+ int64 nCredit = 0;
+ BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
+ {
+ CTxDB txdb("r");
+ CTxIndex txindex;
+ if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex))
+ continue;
+
+ // Read block header
+ CBlock block;
+ if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
+ continue;
+ if (block.GetBlockTime() + STAKE_MIN_AGE > txNew.nTime)
+ continue; // only count coins meeting min age requirement
+
+ int64 nValueIn = pcoin.first->vout[pcoin.second].nValue;
+ CBigNum bnCoinDay = CBigNum(nValueIn) * (txNew.nTime-pcoin.first->nTime) / COIN / (24 * 60 * 60);
+ // Calculate hash
+ CDataStream ss(SER_GETHASH, VERSION);
+ ss << nBits << block.nTime << (txindex.pos.nTxPos - txindex.pos.nBlockPos) << pcoin.first->nTime << pcoin.second << txNew.nTime;
+ if (CBigNum(Hash(ss.begin(), ss.end())) <= bnCoinDay * bnTargetPerCoinDay)
+ {
+ txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second));
+ nCredit += pcoin.first->vout[pcoin.second].nValue;
+ vwtxPrev.push_back(pcoin.first);
+ // Set output scriptPubKey
+ txNew.vout.push_back(CTxOut(0, pcoin.first->vout[pcoin.second].scriptPubKey));
+ break;
+ }
+ }
+ if (nCredit == 0 || nCredit > nBalance - nBalanceReserve)
+ return false;
+ BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
+ {
+ if (pcoin.first->vout[pcoin.second].scriptPubKey == txNew.vout[1].scriptPubKey && pcoin.first->GetHash() != txNew.vin[0].prevout.hash)
+ {
+ if (nCredit + pcoin.first->vout[pcoin.second].nValue > nBalance - nBalanceReserve)
+ break;
+ txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second));
+ nCredit += pcoin.first->vout[pcoin.second].nValue;
+ vwtxPrev.push_back(pcoin.first);
+ }
+ }
+ // Calculate coin age reward
+ {
+ uint64 nCoinAge;
+ CTxDB txdb("r");
+ if (!txNew.GetCoinAge(txdb, nCoinAge))
+ return error("CreateCoinStake : failed to calculate coin age");
+ nCredit += GetProofOfStakeReward(nCoinAge);
+ }
+ // Set output amount
+ txNew.vout[1].nValue = nCredit;
+
+ // Sign
+ int nIn = 0;
+ BOOST_FOREACH(const CWalletTx* pcoin, vwtxPrev)
+ {
+ if (!SignSignature(*this, *pcoin, txNew, nIn++))
+ return error("CreateCoinStake : failed to sign coinstake");
+ }
+ }
+ return true;
+}
+
// Call after CreateTransaction unless you want to abort
-bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
+bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
{
CRITICAL_BLOCK(cs_main)
+ CRITICAL_BLOCK(cs_wallet)
{
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");
+ CWalletDB* pwalletdb = fFileBacked ? new CWalletDB(strWalletFile,"r") : NULL;
// Take key pair from key pool so it won't be used again
reservekey.KeepKey();
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());
+ CWalletTx &coin = mapWallet[txin.prevout.hash];
+ coin.pwallet = this;
+ coin.MarkSpent(txin.prevout.n);
+ coin.WriteToDisk();
+ vWalletUpdated.push_back(coin.GetHash());
}
+
+ if (fFileBacked)
+ delete pwalletdb;
}
// Track how many getdata requests our transaction gets
- CRITICAL_BLOCK(cs_mapRequestCount)
- mapRequestCount[wtxNew.GetHash()] = 0;
+ mapRequestCount[wtxNew.GetHash()] = 0;
// Broadcast
if (!wtxNew.AcceptToMemoryPool())
-// requires cs_main lock
-string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee)
+string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee)
{
- CReserveKey reservekey;
+ CReserveKey reservekey(this);
int64 nFeeRequired;
+
+ if (IsLocked())
+ {
+ string strError = _("Error: Wallet locked, unable to create transaction ");
+ printf("SendMoney() : %s", strError.c_str());
+ return strError;
+ }
+ if (fWalletUnlockStakeOnly)
+ {
+ string strError = _("Error: Wallet unlocked for coinstake only, unable to create transaction.");
+ printf("SendMoney() : %s", strError.c_str());
+ return strError;
+ }
if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired))
{
string strError;
-// requires cs_main lock
-string SendMoneyToBitcoinAddress(string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee)
+string CWallet::SendMoneyToBitcoinAddress(const CBitcoinAddress& address, int64 nValue, CWalletTx& wtxNew, bool fAskFee)
{
// Check amount
if (nValue <= 0)
// Parse bitcoin address
CScript scriptPubKey;
- if (!scriptPubKey.SetBitcoinAddress(strAddress))
- return _("Invalid bitcoin address");
+ scriptPubKey.SetBitcoinAddress(address);
return SendMoney(scriptPubKey, nValue, wtxNew, fAskFee);
}
-bool LoadWallet(bool& fFirstRunRet)
+int CWallet::LoadWallet(bool& fFirstRunRet)
{
- fFirstRunRet = false;
- if (!CWalletDB("cr+").LoadWallet())
+ if (!fFileBacked)
return false;
- fFirstRunRet = vchDefaultKey.empty();
-
- if (mapKeys.count(vchDefaultKey))
+ fFirstRunRet = false;
+ int nLoadWalletRet = CWalletDB(strWalletFile,"cr+").LoadWallet(this);
+ if (nLoadWalletRet == DB_NEED_REWRITE)
{
- // Set keyUser
- keyUser.SetPubKey(vchDefaultKey);
- keyUser.SetPrivKey(mapKeys[vchDefaultKey]);
+ if (CDB::Rewrite(strWalletFile, "\x04pool"))
+ {
+ setKeyPool.clear();
+ // Note: can't top-up keypool here, because wallet is locked.
+ // User will be prompted to unlock wallet the next operation
+ // the requires a new key.
+ }
+ nLoadWalletRet = DB_NEED_REWRITE;
}
- else
+
+ if (nLoadWalletRet != DB_LOAD_OK)
+ return nLoadWalletRet;
+ fFirstRunRet = vchDefaultKey.empty();
+
+ if (!HaveKey(Hash160(vchDefaultKey)))
{
// Create new keyUser and set as default key
RandAddSeedPerfmon();
- CWalletDB walletdb;
- vchDefaultKey = GetKeyFromKeyPool();
- walletdb.WriteDefaultKey(vchDefaultKey);
- walletdb.WriteName(PubKeyToAddress(vchDefaultKey), "");
+ std::vector<unsigned char> newDefaultKey;
+ if (!GetKeyFromPool(newDefaultKey, false))
+ return DB_LOAD_FAIL;
+ SetDefaultKey(newDefaultKey);
+ if (!SetAddressBookName(CBitcoinAddress(vchDefaultKey), ""))
+ return DB_LOAD_FAIL;
}
- CreateThread(ThreadFlushWalletDB, NULL);
+ CreateThread(ThreadFlushWalletDB, &strWalletFile);
+ return DB_LOAD_OK;
+}
+
+
+bool CWallet::SetAddressBookName(const CBitcoinAddress& address, const string& strName)
+{
+ mapAddressBook[address] = strName;
+ if (!fFileBacked)
+ return false;
+ return CWalletDB(strWalletFile).WriteName(address.ToString(), strName);
+}
+
+bool CWallet::DelAddressBookName(const CBitcoinAddress& address)
+{
+ mapAddressBook.erase(address);
+ if (!fFileBacked)
+ return false;
+ return CWalletDB(strWalletFile).EraseName(address.ToString());
+}
+
+
+void CWallet::PrintWallet(const CBlock& block)
+{
+ CRITICAL_BLOCK(cs_wallet)
+ {
+ if (block.IsProofOfWork() && mapWallet.count(block.vtx[0].GetHash()))
+ {
+ CWalletTx& wtx = mapWallet[block.vtx[0].GetHash()];
+ printf(" mine: %d %d %s", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), FormatMoney(wtx.GetCredit()).c_str());
+ }
+ if (block.IsProofOfStake() && mapWallet.count(block.vtx[1].GetHash()))
+ {
+ CWalletTx& wtx = mapWallet[block.vtx[1].GetHash()];
+ printf(" stake: %d %d %s", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), FormatMoney(wtx.GetCredit()).c_str());
+ }
+ }
+ printf("\n");
+}
+
+bool CWallet::GetTransaction(const uint256 &hashTx, CWalletTx& wtx)
+{
+ CRITICAL_BLOCK(cs_wallet)
+ {
+ map<uint256, CWalletTx>::iterator mi = mapWallet.find(hashTx);
+ if (mi != mapWallet.end())
+ {
+ wtx = (*mi).second;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CWallet::SetDefaultKey(const std::vector<unsigned char> &vchPubKey)
+{
+ if (fFileBacked)
+ {
+ if (!CWalletDB(strWalletFile).WriteDefaultKey(vchPubKey))
+ return false;
+ }
+ vchDefaultKey = vchPubKey;
return true;
}
-void CWalletDB::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool)
+bool GetWalletFile(CWallet* pwallet, string &strWalletFileOut)
{
- nIndex = -1;
- keypool.vchPubKey.clear();
- CRITICAL_BLOCK(cs_main)
- CRITICAL_BLOCK(cs_mapWallet)
- CRITICAL_BLOCK(cs_setKeyPool)
+ if (!pwallet->fFileBacked)
+ return false;
+ strWalletFileOut = pwallet->strWalletFile;
+ return true;
+}
+
+//
+// Mark old keypool keys as used,
+// and generate all new keys
+//
+bool CWallet::NewKeyPool()
+{
+ CRITICAL_BLOCK(cs_wallet)
{
+ CWalletDB walletdb(strWalletFile);
+ BOOST_FOREACH(int64 nIndex, setKeyPool)
+ walletdb.ErasePool(nIndex);
+ setKeyPool.clear();
+
+ if (IsLocked())
+ return false;
+
+ int64 nKeys = max(GetArg("-keypool", 100), (int64)0);
+ for (int i = 0; i < nKeys; i++)
+ {
+ int64 nIndex = i+1;
+ walletdb.WritePool(nIndex, CKeyPool(GenerateNewKey()));
+ setKeyPool.insert(nIndex);
+ }
+ printf("CWallet::NewKeyPool wrote %"PRI64d" new keys\n", nKeys);
+ }
+ return true;
+}
+
+bool CWallet::TopUpKeyPool()
+{
+ CRITICAL_BLOCK(cs_wallet)
+ {
+ if (IsLocked())
+ return false;
+
+ CWalletDB walletdb(strWalletFile);
+
// 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");
+ if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey())))
+ throw runtime_error("TopUpKeyPool() : writing generated key failed");
setKeyPool.insert(nEnd);
printf("keypool added key %"PRI64d", size=%d\n", nEnd, setKeyPool.size());
}
+ }
+ return true;
+}
+
+void CWallet::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool)
+{
+ nIndex = -1;
+ keypool.vchPubKey.clear();
+ CRITICAL_BLOCK(cs_wallet)
+ {
+ if (!IsLocked())
+ TopUpKeyPool();
// Get the oldest key
- assert(!setKeyPool.empty());
+ if(setKeyPool.empty())
+ return;
+
+ CWalletDB walletdb(strWalletFile);
+
nIndex = *(setKeyPool.begin());
setKeyPool.erase(setKeyPool.begin());
- if (!Read(make_pair(string("pool"), nIndex), keypool))
+ if (!walletdb.ReadPool(nIndex, keypool))
throw runtime_error("ReserveKeyFromKeyPool() : read failed");
- if (!mapKeys.count(keypool.vchPubKey))
+ if (!HaveKey(Hash160(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)
+void CWallet::KeepKey(int64 nIndex)
{
// Remove from key pool
- CRITICAL_BLOCK(cs_main)
- CRITICAL_BLOCK(cs_mapWallet)
+ if (fFileBacked)
{
- Erase(make_pair(string("pool"), nIndex));
+ CWalletDB walletdb(strWalletFile);
+ walletdb.ErasePool(nIndex);
}
printf("keypool keep %"PRI64d"\n", nIndex);
}
-void CWalletDB::ReturnKey(int64 nIndex)
+void CWallet::ReturnKey(int64 nIndex)
{
// Return to key pool
- CRITICAL_BLOCK(cs_setKeyPool)
+ CRITICAL_BLOCK(cs_wallet)
setKeyPool.insert(nIndex);
printf("keypool return %"PRI64d"\n", nIndex);
}
-vector<unsigned char> GetKeyFromKeyPool()
+bool CWallet::GetKeyFromPool(vector<unsigned char>& result, bool fAllowReuse)
{
- CWalletDB walletdb;
int64 nIndex = 0;
CKeyPool keypool;
- walletdb.ReserveKeyFromKeyPool(nIndex, keypool);
- walletdb.KeepKey(nIndex);
- return keypool.vchPubKey;
+ CRITICAL_BLOCK(cs_wallet)
+ {
+ ReserveKeyFromKeyPool(nIndex, keypool);
+ if (nIndex == -1)
+ {
+ if (fAllowReuse && !vchDefaultKey.empty())
+ {
+ result = vchDefaultKey;
+ return true;
+ }
+ if (IsLocked()) return false;
+ result = GenerateNewKey();
+ return true;
+ }
+ KeepKey(nIndex);
+ result = keypool.vchPubKey;
+ }
+ return true;
}
-int64 GetOldestKeyPoolTime()
+int64 CWallet::GetOldestKeyPoolTime()
{
- CWalletDB walletdb;
int64 nIndex = 0;
CKeyPool keypool;
- walletdb.ReserveKeyFromKeyPool(nIndex, keypool);
- walletdb.ReturnKey(nIndex);
+ ReserveKeyFromKeyPool(nIndex, keypool);
+ if (nIndex == -1)
+ return GetTime();
+ ReturnKey(nIndex);
return keypool.nTime;
}
-std::vector<unsigned char> CReserveKey::GetReservedKey()
+// ppcoin: check 'spent' consistency between wallet and txindex
+bool CWallet::CheckSpentCoins(int& nMismatchFound, int64& nBalanceInQuestion)
+{
+ nMismatchFound = 0;
+ nBalanceInQuestion = 0;
+ CRITICAL_BLOCK(cs_wallet)
+ {
+ vector<const CWalletTx*> vCoins;
+ vCoins.reserve(mapWallet.size());
+ for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ vCoins.push_back(&(*it).second);
+
+ CTxDB txdb("r");
+ BOOST_FOREACH(const CWalletTx* pcoin, vCoins)
+ {
+ // Find the corresponding transaction index
+ CTxIndex txindex;
+ if (!txdb.ReadTxIndex(pcoin->GetHash(), txindex))
+ continue;
+ for (int n=0; n < pcoin->vout.size(); n++)
+ {
+ if (pcoin->IsSpent(n) && (txindex.vSpent.size() <= n || txindex.vSpent[n].IsNull()))
+ {
+ printf("CheckSpentCoins found lost coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n);
+ nMismatchFound++;
+ nBalanceInQuestion += pcoin->vout[n].nValue;
+ }
+ else if (!pcoin->IsSpent(n) && (txindex.vSpent.size() > n && !txindex.vSpent[n].IsNull()))
+ {
+ printf("CheckSpentCoins found spent coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n);
+ nMismatchFound++;
+ nBalanceInQuestion += pcoin->vout[n].nValue;
+ }
+ }
+ }
+ }
+ return (nMismatchFound == 0);
+}
+
+// ppcoin: fix wallet spent state according to txindex
+void CWallet::FixSpentCoins(int& nMismatchFound, int64& nBalanceInQuestion)
+{
+ nMismatchFound = 0;
+ nBalanceInQuestion = 0;
+ CRITICAL_BLOCK(cs_wallet)
+ {
+ vector<CWalletTx*> vCoins;
+ vCoins.reserve(mapWallet.size());
+ for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ vCoins.push_back(&(*it).second);
+
+ CTxDB txdb("r");
+ BOOST_FOREACH(CWalletTx* pcoin, vCoins)
+ {
+ // Find the corresponding transaction index
+ CTxIndex txindex;
+ if (!txdb.ReadTxIndex(pcoin->GetHash(), txindex))
+ continue;
+ for (int n=0; n < pcoin->vout.size(); n++)
+ {
+ if (pcoin->IsSpent(n) && (txindex.vSpent.size() <= n || txindex.vSpent[n].IsNull()))
+ {
+ printf("FixSpentCoins found lost coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n);
+ nMismatchFound++;
+ nBalanceInQuestion += pcoin->vout[n].nValue;
+ pcoin->MarkUnspent(n);
+ pcoin->WriteToDisk();
+ }
+ else if (!pcoin->IsSpent(n) && (txindex.vSpent.size() > n && !txindex.vSpent[n].IsNull()))
+ {
+ printf("FixSpentCoins found spent coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n);
+ nMismatchFound++;
+ nBalanceInQuestion += pcoin->vout[n].nValue;
+ pcoin->MarkSpent(n);
+ pcoin->WriteToDisk();
+ }
+ }
+ }
+ }
+}
+
+// ppcoin: disable transaction (only for coinstake)
+void CWallet::DisableTransaction(const CTransaction &tx)
+{
+ if (!tx.IsCoinStake() || !IsFromMe(tx))
+ return; // only disconnecting coinstake requires marking input unspent
+ CRITICAL_BLOCK(cs_wallet)
+ {
+ BOOST_FOREACH(const CTxIn& txin, tx.vin)
+ {
+ map<uint256, CWalletTx>::iterator mi = mapWallet.find(txin.prevout.hash);
+ if (mi != mapWallet.end())
+ {
+ CWalletTx& prev = (*mi).second;
+ if (txin.prevout.n < prev.vout.size() && IsMine(prev.vout[txin.prevout.n]))
+ {
+ prev.MarkUnspent(txin.prevout.n);
+ prev.WriteToDisk();
+ }
+ }
+ }
+ }
+}
+
+vector<unsigned char> CReserveKey::GetReservedKey()
{
if (nIndex == -1)
{
CKeyPool keypool;
- CWalletDB().ReserveKeyFromKeyPool(nIndex, keypool);
- vchPubKey = keypool.vchPubKey;
+ pwallet->ReserveKeyFromKeyPool(nIndex, keypool);
+ if (nIndex != -1)
+ vchPubKey = keypool.vchPubKey;
+ else
+ {
+ printf("CReserveKey::GetReservedKey(): Warning: using default key instead of a new key, top up your keypool.");
+ vchPubKey = pwallet->vchDefaultKey;
+ }
}
assert(!vchPubKey.empty());
return vchPubKey;
void CReserveKey::KeepKey()
{
if (nIndex != -1)
- CWalletDB().KeepKey(nIndex);
+ pwallet->KeepKey(nIndex);
nIndex = -1;
vchPubKey.clear();
}
void CReserveKey::ReturnKey()
{
if (nIndex != -1)
- CWalletDB::ReturnKey(nIndex);
+ pwallet->ReturnKey(nIndex);
nIndex = -1;
vchPubKey.clear();
}
+