twisterp2pnetworkbittorrentblockchainmicrobloggingipv6social-networkdhtdecentralizedtwister-servertwister-ipv6twister-coretwisterarmyp2p-network
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
857 lines
28 KiB
857 lines
28 KiB
// Copyright (c) 2009-2010 Satoshi Nakamoto |
|
// Copyright (c) 2009-2012 The Bitcoin developers |
|
// Distributed under the MIT/X11 software license, see the accompanying |
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php. |
|
|
|
#include "wallet.h" |
|
#include "walletdb.h" |
|
#include "crypter.h" |
|
#include "ui_interface.h" |
|
#include "base58.h" |
|
#include <boost/algorithm/string/replace.hpp> |
|
|
|
using namespace std; |
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// mapWallet |
|
// |
|
|
|
struct CompareValueOnly |
|
{ |
|
bool operator()(const pair<int64, pair<const CWalletTx*, unsigned int> >& t1, |
|
const pair<int64, pair<const CWalletTx*, unsigned int> >& t2) const |
|
{ |
|
return t1.first < t2.first; |
|
} |
|
}; |
|
|
|
CPubKey CWallet::GenerateNewKey(std::string username) |
|
{ |
|
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets |
|
|
|
RandAddSeedPerfmon(); |
|
CKey secret; |
|
secret.MakeNewKey(fCompressed); |
|
|
|
// Compressed public keys were introduced in version 0.6.0 |
|
if (fCompressed) |
|
SetMinVersion(FEATURE_COMPRPUBKEY); |
|
|
|
CPubKey pubkey = secret.GetPubKey(); |
|
|
|
// Create new metadata |
|
int64 nCreationTime = GetTime(); |
|
mapKeyMetadata[pubkey.GetID()] = CKeyMetadata(nCreationTime, username); |
|
if (!nTimeFirstKey || nCreationTime < nTimeFirstKey) |
|
nTimeFirstKey = nCreationTime; |
|
|
|
if (!AddKeyPubKey(secret, pubkey)) |
|
throw std::runtime_error("CWallet::GenerateNewKey() : AddKey failed"); |
|
|
|
if (!vchDefaultKey.IsValid()) |
|
vchDefaultKey = pubkey; |
|
return pubkey; |
|
} |
|
|
|
bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) |
|
{ |
|
if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) |
|
return false; |
|
if (!fFileBacked) |
|
return true; |
|
if (!IsCrypted()) { |
|
return CWalletDB(strWalletFile).WriteKey(pubkey, |
|
secret.GetPrivKey(), |
|
mapKeyMetadata[pubkey.GetID()]); |
|
} |
|
return true; |
|
} |
|
|
|
bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, |
|
const vector<unsigned char> &vchCryptedSecret) |
|
{ |
|
if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) |
|
return false; |
|
if (!fFileBacked) |
|
return true; |
|
{ |
|
LOCK(cs_wallet); |
|
if (pwalletdbEncryption) |
|
return pwalletdbEncryption->WriteCryptedKey(vchPubKey, |
|
vchCryptedSecret, |
|
mapKeyMetadata[vchPubKey.GetID()]); |
|
else |
|
return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, |
|
vchCryptedSecret, |
|
mapKeyMetadata[vchPubKey.GetID()]); |
|
} |
|
return false; |
|
} |
|
|
|
bool CWallet::LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &meta) |
|
{ |
|
if (meta.nCreateTime && (!nTimeFirstKey || meta.nCreateTime < nTimeFirstKey)) |
|
nTimeFirstKey = meta.nCreateTime; |
|
|
|
mapKeyMetadata[pubkey.GetID()] = meta; |
|
return true; |
|
} |
|
|
|
bool CWallet::GetKeyIdFromUsername(std::string username, CKeyID &keyid) |
|
{ |
|
for (std::map<CKeyID, CKeyMetadata>::const_iterator it = mapKeyMetadata.begin(); it != mapKeyMetadata.end(); it++) { |
|
if (it->second.username == username) { |
|
keyid = it->first; |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
bool CWallet::GetUsernameFromKeyId(CKeyID keyid, std::string &username) |
|
{ |
|
for (std::map<CKeyID, CKeyMetadata>::const_iterator it = mapKeyMetadata.begin(); it != mapKeyMetadata.end(); it++) { |
|
if (it->first == keyid) { |
|
username = it->second.username; |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
bool CWallet::MoveKeyForReplacement(std::string username) |
|
{ |
|
for (std::map<CKeyID, CKeyMetadata>::iterator it = mapKeyMetadata.begin(); it != mapKeyMetadata.end(); it++) { |
|
if (it->second.username == username) { |
|
mapKeyReplacement.insert(make_pair(it->first, username)); |
|
it->second.username += "/replaced"; // prevents being used again with GetKeyIdFromUsername |
|
|
|
// [MF] make sure old metadata (with new name) is updated on disk |
|
CPubKey pubkey; |
|
CKey secret; |
|
GetPubKey(it->first, pubkey); |
|
GetKey(it->first,secret); |
|
if (!IsCrypted()) { |
|
CWalletDB(strWalletFile).WriteKey(pubkey, |
|
secret.GetPrivKey(), |
|
it->second); |
|
} else { |
|
printf("WARNING: MoveKeyForReplacement not implemeted for crypted wallet. duplicate metadata may occur!\n" ); |
|
} |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
bool CWallet::GetKeyIdBeingReplaced(std::string username, CKeyID &keyid) |
|
{ |
|
for (std::map<CKeyID, string>::const_iterator it = mapKeyReplacement.begin(); it != mapKeyReplacement.end(); it++) { |
|
if (it->second == username) { |
|
keyid = it->first; |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
bool CWallet::ForgetReplacementMap(std::string username) |
|
{ |
|
for (std::map<CKeyID, string>::iterator it = mapKeyReplacement.begin(); it != mapKeyReplacement.end(); it++) { |
|
if (it->second == username) { |
|
mapKeyReplacement.erase(it); |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
|
|
bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) |
|
{ |
|
return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); |
|
} |
|
|
|
bool CWallet::Unlock(const SecureString& strWalletPassphrase) |
|
{ |
|
CCrypter crypter; |
|
CKeyingMaterial vMasterKey; |
|
|
|
{ |
|
LOCK(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)) |
|
continue; // try another master key |
|
if (CCryptoKeyStore::Unlock(vMasterKey)) |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase) |
|
{ |
|
bool fWasLocked = IsLocked(); |
|
|
|
{ |
|
LOCK(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; |
|
} |
|
|
|
void CWallet::SetBestChain(const CBlockLocator& loc) |
|
{ |
|
CWalletDB walletdb(strWalletFile); |
|
walletdb.WriteBestBlock(loc); |
|
} |
|
|
|
// This class implements an addrIncoming entry that causes pre-0.4 |
|
// clients to crash on startup if reading a private-key-encrypted wallet. |
|
class CCorruptAddress |
|
{ |
|
public: |
|
IMPLEMENT_SERIALIZE |
|
( |
|
if (nType & SER_DISK) |
|
READWRITE(nVersion); |
|
) |
|
}; |
|
|
|
bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit) |
|
{ |
|
if (nWalletVersion >= nVersion) |
|
return true; |
|
|
|
// when doing an explicit upgrade, if we pass the max version permitted, upgrade all the way |
|
if (fExplicit && nVersion > nWalletMaxVersion) |
|
nVersion = FEATURE_LATEST; |
|
|
|
nWalletVersion = nVersion; |
|
|
|
if (nVersion > nWalletMaxVersion) |
|
nWalletMaxVersion = nVersion; |
|
|
|
if (fFileBacked) |
|
{ |
|
CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile); |
|
if (nWalletVersion >= 40000) |
|
{ |
|
// Versions prior to 0.4.0 did not support the "minversion" record. |
|
// Use a CCorruptAddress to make them crash instead. |
|
CCorruptAddress corruptAddress; |
|
pwalletdb->WriteSetting("addrIncoming", corruptAddress); |
|
} |
|
if (nWalletVersion > 40000) |
|
pwalletdb->WriteMinVersion(nWalletVersion); |
|
if (!pwalletdbIn) |
|
delete pwalletdb; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool CWallet::SetMaxVersion(int nVersion) |
|
{ |
|
// cannot downgrade below current version |
|
if (nWalletVersion > nVersion) |
|
return false; |
|
|
|
nWalletMaxVersion = nVersion; |
|
|
|
return true; |
|
} |
|
|
|
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; |
|
|
|
{ |
|
LOCK(cs_wallet); |
|
mapMasterKeys[++nMasterKeyMaxID] = kMasterKey; |
|
if (fFileBacked) |
|
{ |
|
pwalletdbEncryption = new CWalletDB(strWalletFile); |
|
if (!pwalletdbEncryption->TxnBegin()) |
|
return false; |
|
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. |
|
} |
|
|
|
// Encryption was introduced in version 0.4.0 |
|
SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true); |
|
|
|
if (fFileBacked) |
|
{ |
|
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. |
|
|
|
delete pwalletdbEncryption; |
|
pwalletdbEncryption = NULL; |
|
} |
|
|
|
Lock(); |
|
Unlock(strWalletPassphrase); |
|
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); |
|
|
|
} |
|
NotifyStatusChanged(this); |
|
|
|
return true; |
|
} |
|
|
|
int64 CWallet::IncOrderPosNext(CWalletDB *pwalletdb) |
|
{ |
|
int64 nRet = nOrderPosNext++; |
|
if (pwalletdb) { |
|
pwalletdb->WriteOrderPosNext(nOrderPosNext); |
|
} else { |
|
CWalletDB(strWalletFile).WriteOrderPosNext(nOrderPosNext); |
|
} |
|
return nRet; |
|
} |
|
|
|
void CWallet::MarkDirty() |
|
{ |
|
{ |
|
LOCK(cs_wallet); |
|
/* |
|
BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) |
|
item.second.MarkDirty(); |
|
*/ |
|
} |
|
} |
|
|
|
bool CWallet::AddToWallet(const CWalletTx& wtxIn) |
|
{ |
|
uint256 hash = wtxIn.GetHash(); |
|
{ |
|
LOCK(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.BindWallet(this); |
|
bool fInsertedNew = ret.second; |
|
if (fInsertedNew) |
|
{ |
|
wtx.nTimeReceived = GetAdjustedTime(); |
|
wtx.nOrderPos = IncOrderPosNext(); |
|
|
|
wtx.nTimeSmart = wtx.nTimeReceived; |
|
if (wtxIn.hashBlock != 0) |
|
{ |
|
if (mapBlockIndex.count(wtxIn.hashBlock)) |
|
{ |
|
unsigned int& blocktime = mapBlockIndex[wtxIn.hashBlock]->nTime; |
|
wtx.nTimeSmart = blocktime; |
|
} |
|
else |
|
printf("AddToWallet() : found %s in block %s not in index\n", |
|
wtxIn.GetHash().ToString().c_str(), |
|
wtxIn.hashBlock.ToString().c_str()); |
|
} |
|
} |
|
|
|
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; |
|
} |
|
} |
|
|
|
//// debug print |
|
printf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString().c_str(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); |
|
|
|
// Write to disk |
|
if (fInsertedNew || fUpdated) |
|
if (!wtx.WriteToDisk()) |
|
return false; |
|
|
|
if (!fHaveGUI) { |
|
// If default receiving address gets used, replace it with a new one |
|
if (vchDefaultKey.IsValid()) { |
|
CScript scriptDefaultKey; |
|
scriptDefaultKey.SetDestination(vchDefaultKey.GetID()); |
|
/* |
|
BOOST_FOREACH(const CTxOut& txout, wtx.vout) |
|
{ |
|
if (txout.scriptPubKey == scriptDefaultKey) |
|
{ |
|
CPubKey newDefaultKey; |
|
if (GetKeyFromPool(newDefaultKey, false)) |
|
{ |
|
SetDefaultKey(newDefaultKey); |
|
SetAddressBookName(vchDefaultKey.GetID(), ""); |
|
} |
|
} |
|
} |
|
*/ |
|
} |
|
} |
|
|
|
// Notify UI of new or updated transaction |
|
NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); |
|
|
|
// notify an external script when a wallet transaction comes in or is updated |
|
std::string strCmd = GetArg("-walletnotify", ""); |
|
|
|
if ( !strCmd.empty()) |
|
{ |
|
boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); |
|
boost::thread t(runCommand, strCmd); // thread runs free |
|
} |
|
|
|
} |
|
return true; |
|
} |
|
|
|
// 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 uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fFindBlock) |
|
{ |
|
{ |
|
LOCK(cs_wallet); |
|
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); |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
bool CWallet::EraseFromWallet(uint256 hash) |
|
{ |
|
if (!fFileBacked) |
|
return false; |
|
{ |
|
LOCK(cs_wallet); |
|
if (mapWallet.erase(hash)) |
|
CWalletDB(strWalletFile).EraseTx(hash); |
|
} |
|
return true; |
|
} |
|
|
|
|
|
int64 CWalletTx::GetTxTime() const |
|
{ |
|
int64 n = nTimeSmart; |
|
return n ? n : nTimeReceived; |
|
} |
|
|
|
int CWalletTx::GetRequestCount() const |
|
{ |
|
// Returns -1 if it wasn't being tracked |
|
int nRequests = -1; |
|
{ |
|
LOCK(pwallet->cs_wallet); |
|
if (IsSpamMessage()) |
|
{ |
|
// Generated block |
|
if (hashBlock != 0) |
|
{ |
|
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>::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>::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::AddSupportingTransactions() |
|
{ |
|
// [MF] remove me |
|
} |
|
|
|
bool CWalletTx::WriteToDisk() |
|
{ |
|
return CWalletDB(pwallet->strWalletFile).WriteTx(GetHash(), *this); |
|
} |
|
|
|
// 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; |
|
{ |
|
LOCK(cs_wallet); |
|
while (pindex) |
|
{ |
|
// no need to read and scan block, if block was created before |
|
// our wallet birthday (as adjusted for block time variability) |
|
if (nTimeFirstKey && (pindex->nTime < (nTimeFirstKey - 7200))) { |
|
pindex = pindex->GetNextInMainChain(); |
|
continue; |
|
} |
|
|
|
CBlock block; |
|
ReadBlockFromDisk(block, pindex); |
|
BOOST_FOREACH(CTransaction& tx, block.vtx) |
|
{ |
|
if (AddToWalletIfInvolvingMe(tx.GetHash(), tx, &block, fUpdate)) |
|
ret++; |
|
} |
|
pindex = pindex->GetNextInMainChain(); |
|
} |
|
} |
|
return ret; |
|
} |
|
|
|
void CWallet::ReacceptWalletTransactions() |
|
{ |
|
bool fRepeat = true; |
|
while (fRepeat) |
|
{ |
|
LOCK(cs_wallet); |
|
fRepeat = false; |
|
bool fMissing = false; |
|
BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) |
|
{ |
|
CWalletTx& wtx = item.second; |
|
if (wtx.IsSpamMessage() ) |
|
continue; |
|
|
|
CTransaction tx; |
|
uint256 hashBlock; |
|
//bool fUpdated = false; |
|
bool fFound = false; |
|
// [MF] can't use GetTransaction from main.cpp here. so? |
|
// GetTransaction(wtx.GetUsernameHash(), tx, hashBlock); |
|
if (fFound || wtx.GetDepthInMainChain() > 0) |
|
{ |
|
/* |
|
// Update fSpent if a tx got spent somewhere else by a copy of wallet.dat |
|
for (unsigned int i = 0; i < wtx.vout.size(); i++) |
|
{ |
|
if (wtx.IsSpent(i)) |
|
continue; |
|
if ((i >= coins.vout.size() || coins.vout[i].IsNull()) && IsMine(wtx.vout[i])) |
|
{ |
|
wtx.MarkSpent(i); |
|
fUpdated = true; |
|
fMissing = true; |
|
} |
|
} |
|
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 |
|
{ |
|
// Re-accept any txes of ours that aren't already in a block |
|
if (!wtx.IsSpamMessage()) |
|
wtx.AcceptWalletTransaction(); |
|
} |
|
} |
|
if (fMissing) |
|
{ |
|
// TODO: optimize this to scan just part of the block chain? |
|
if (ScanForWalletTransactions(pindexGenesisBlock)) |
|
fRepeat = true; // Found missing transactions: re-do re-accept. |
|
} |
|
} |
|
} |
|
|
|
void CWalletTx::RelayWalletTransaction() |
|
{ |
|
if (!IsSpamMessage()) |
|
{ |
|
if (GetDepthInMainChain() == 0) { |
|
uint256 hash = GetHash(); |
|
printf("Relaying wtx %s\n", hash.ToString().c_str()); |
|
RelayTransaction((CTransaction)*this, hash); |
|
} |
|
} |
|
} |
|
|
|
void CWallet::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"); |
|
{ |
|
LOCK(cs_wallet); |
|
// 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(); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// Actions |
|
// |
|
|
|
DBErrors CWallet::LoadWallet(bool& fFirstRunRet) |
|
{ |
|
if (!fFileBacked) |
|
return DB_LOAD_OK; |
|
fFirstRunRet = false; |
|
DBErrors nLoadWalletRet = CWalletDB(strWalletFile,"cr+").LoadWallet(this); |
|
if (nLoadWalletRet == DB_NEED_REWRITE) |
|
{ |
|
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. |
|
} |
|
} |
|
|
|
if (nLoadWalletRet != DB_LOAD_OK) |
|
return nLoadWalletRet; |
|
fFirstRunRet = !vchDefaultKey.IsValid(); |
|
|
|
return DB_LOAD_OK; |
|
} |
|
|
|
|
|
|
|
|
|
bool CWallet::SetDefaultKey(const CPubKey &vchPubKey) |
|
{ |
|
if (fFileBacked) |
|
{ |
|
if (!CWalletDB(strWalletFile).WriteDefaultKey(vchPubKey)) |
|
return false; |
|
} |
|
vchDefaultKey = vchPubKey; |
|
return true; |
|
} |
|
|
|
bool GetWalletFile(CWallet* pwallet, string &strWalletFileOut) |
|
{ |
|
if (!pwallet->fFileBacked) |
|
return false; |
|
strWalletFileOut = pwallet->strWalletFile; |
|
return true; |
|
} |
|
|
|
|
|
|
|
void CWallet::UpdatedTransaction(const uint256 &hashTx) |
|
{ |
|
{ |
|
LOCK(cs_wallet); |
|
// Only notify UI if this transaction is in this wallet |
|
map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(hashTx); |
|
if (mi != mapWallet.end()) |
|
NotifyTransactionChanged(this, hashTx, CT_UPDATED); |
|
} |
|
} |
|
|
|
|
|
void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64> &mapKeyBirth) const { |
|
mapKeyBirth.clear(); |
|
|
|
// get birth times for keys with metadata |
|
for (std::map<CKeyID, CKeyMetadata>::const_iterator it = mapKeyMetadata.begin(); it != mapKeyMetadata.end(); it++) |
|
if (it->second.nCreateTime) |
|
mapKeyBirth[it->first] = it->second.nCreateTime; |
|
|
|
// map in which we'll infer heights of other keys |
|
CBlockIndex *pindexMax = FindBlockByHeight(std::max(0, nBestHeight - 144)); // the tip can be reorganised; use a 144-block safety margin |
|
std::map<CKeyID, CBlockIndex*> mapKeyFirstBlock; |
|
std::set<CKeyID> setKeys; |
|
GetKeys(setKeys); |
|
BOOST_FOREACH(const CKeyID &keyid, setKeys) { |
|
if (mapKeyBirth.count(keyid) == 0) |
|
mapKeyFirstBlock[keyid] = pindexMax; |
|
} |
|
setKeys.clear(); |
|
|
|
// if there are no such keys, we're done |
|
if (mapKeyFirstBlock.empty()) |
|
return; |
|
/* |
|
// find first block that affects those keys, if there are any left |
|
std::vector<CKeyID> vAffected; |
|
for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); it++) { |
|
// iterate over all wallet transactions... |
|
const CWalletTx &wtx = (*it).second; |
|
std::map<uint256, CBlockIndex*>::const_iterator blit = mapBlockIndex.find(wtx.hashBlock); |
|
if (blit != mapBlockIndex.end() && blit->second->IsInMainChain()) { |
|
// ... which are already in a block |
|
int nHeight = blit->second->nHeight; |
|
BOOST_FOREACH(const CTxOut &txout, wtx.vout) { |
|
// iterate over all their outputs |
|
::ExtractAffectedKeys(*this, txout.scriptPubKey, vAffected); |
|
BOOST_FOREACH(const CKeyID &keyid, vAffected) { |
|
// ... and all their affected keys |
|
std::map<CKeyID, CBlockIndex*>::iterator rit = mapKeyFirstBlock.find(keyid); |
|
if (rit != mapKeyFirstBlock.end() && nHeight < rit->second->nHeight) |
|
rit->second = blit->second; |
|
} |
|
vAffected.clear(); |
|
} |
|
} |
|
} |
|
*/ |
|
// Extract block timestamps for those keys |
|
for (std::map<CKeyID, CBlockIndex*>::const_iterator it = mapKeyFirstBlock.begin(); it != mapKeyFirstBlock.end(); it++) |
|
mapKeyBirth[it->first] = it->second->nTime - 7200; // block times can be 2h off |
|
}
|
|
|