mirror of
https://github.com/twisterarmy/twister-core.git
synced 2025-01-10 23:07:52 +00:00
858 lines
28 KiB
C++
858 lines
28 KiB
C++
// 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
|
|
}
|