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.
2608 lines
85 KiB
2608 lines
85 KiB
12 years ago
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||
8 years ago
|
// Copyright (c) 2009-2014 The Bitcoin Core developers
|
||
8 years ago
|
// Distributed under the MIT software license, see the accompanying
|
||
11 years ago
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||
12 years ago
|
|
||
8 years ago
|
#include "wallet/wallet.h"
|
||
10 years ago
|
|
||
11 years ago
|
#include "base58.h"
|
||
9 years ago
|
#include "checkpoints.h"
|
||
10 years ago
|
#include "coincontrol.h"
|
||
9 years ago
|
#include "net.h"
|
||
9 years ago
|
#include "script/script.h"
|
||
|
#include "script/sign.h"
|
||
9 years ago
|
#include "timedata.h"
|
||
9 years ago
|
#include "util.h"
|
||
|
#include "utilmoneystr.h"
|
||
10 years ago
|
|
||
8 years ago
|
#include <assert.h>
|
||
|
|
||
10 years ago
|
#include <boost/algorithm/string/replace.hpp>
|
||
9 years ago
|
#include <boost/thread.hpp>
|
||
12 years ago
|
|
||
|
using namespace std;
|
||
|
|
||
8 years ago
|
/**
|
||
|
* Settings
|
||
|
*/
|
||
9 years ago
|
CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE);
|
||
8 years ago
|
CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE;
|
||
9 years ago
|
unsigned int nTxConfirmTarget = 1;
|
||
9 years ago
|
bool bSpendZeroConfChange = true;
|
||
8 years ago
|
bool fSendFreeTransactions = false;
|
||
8 years ago
|
bool fPayAtLeastCustomFee = true;
|
||
12 years ago
|
|
||
8 years ago
|
/**
|
||
|
* Fees smaller than this (in satoshi) are considered zero fee (for transaction creation)
|
||
|
* Override with -mintxfee
|
||
|
*/
|
||
8 years ago
|
CFeeRate CWallet::minTxFee = CFeeRate(1000);
|
||
9 years ago
|
|
||
8 years ago
|
/** @defgroup mapWallet
|
||
|
*
|
||
|
* @{
|
||
|
*/
|
||
12 years ago
|
|
||
11 years ago
|
struct CompareValueOnly
|
||
|
{
|
||
9 years ago
|
bool operator()(const pair<CAmount, pair<const CWalletTx*, unsigned int> >& t1,
|
||
|
const pair<CAmount, pair<const CWalletTx*, unsigned int> >& t2) const
|
||
11 years ago
|
{
|
||
|
return t1.first < t2.first;
|
||
|
}
|
||
|
};
|
||
|
|
||
9 years ago
|
std::string COutput::ToString() const
|
||
|
{
|
||
9 years ago
|
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->vout[i].nValue));
|
||
9 years ago
|
}
|
||
|
|
||
9 years ago
|
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
|
||
|
{
|
||
|
LOCK(cs_wallet);
|
||
|
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(hash);
|
||
|
if (it == mapWallet.end())
|
||
|
return NULL;
|
||
|
return &(it->second);
|
||
|
}
|
||
|
|
||
11 years ago
|
CPubKey CWallet::GenerateNewKey()
|
||
11 years ago
|
{
|
||
9 years ago
|
AssertLockHeld(cs_wallet); // mapKeyMetadata
|
||
11 years ago
|
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
|
||
11 years ago
|
|
||
10 years ago
|
CKey secret;
|
||
|
secret.MakeNewKey(fCompressed);
|
||
11 years ago
|
|
||
|
// Compressed public keys were introduced in version 0.6.0
|
||
|
if (fCompressed)
|
||
11 years ago
|
SetMinVersion(FEATURE_COMPRPUBKEY);
|
||
11 years ago
|
|
||
10 years ago
|
CPubKey pubkey = secret.GetPubKey();
|
||
8 years ago
|
assert(secret.VerifyPubKey(pubkey));
|
||
10 years ago
|
|
||
|
// Create new metadata
|
||
10 years ago
|
int64_t nCreationTime = GetTime();
|
||
10 years ago
|
mapKeyMetadata[pubkey.GetID()] = CKeyMetadata(nCreationTime);
|
||
|
if (!nTimeFirstKey || nCreationTime < nTimeFirstKey)
|
||
|
nTimeFirstKey = nCreationTime;
|
||
|
|
||
10 years ago
|
if (!AddKeyPubKey(secret, pubkey))
|
||
8 years ago
|
throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed");
|
||
10 years ago
|
return pubkey;
|
||
11 years ago
|
}
|
||
12 years ago
|
|
||
10 years ago
|
bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey)
|
||
12 years ago
|
{
|
||
9 years ago
|
AssertLockHeld(cs_wallet); // mapKeyMetadata
|
||
10 years ago
|
if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey))
|
||
12 years ago
|
return false;
|
||
9 years ago
|
|
||
|
// check if we need to remove from watch-only
|
||
|
CScript script;
|
||
|
script = GetScriptForDestination(pubkey.GetID());
|
||
|
if (HaveWatchOnly(script))
|
||
|
RemoveWatchOnly(script);
|
||
|
|
||
12 years ago
|
if (!fFileBacked)
|
||
|
return true;
|
||
10 years ago
|
if (!IsCrypted()) {
|
||
10 years ago
|
return CWalletDB(strWalletFile).WriteKey(pubkey,
|
||
|
secret.GetPrivKey(),
|
||
10 years ago
|
mapKeyMetadata[pubkey.GetID()]);
|
||
10 years ago
|
}
|
||
12 years ago
|
return true;
|
||
12 years ago
|
}
|
||
|
|
||
10 years ago
|
bool CWallet::AddCryptedKey(const CPubKey &vchPubKey,
|
||
10 years ago
|
const vector<unsigned char> &vchCryptedSecret)
|
||
12 years ago
|
{
|
||
|
if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret))
|
||
|
return false;
|
||
|
if (!fFileBacked)
|
||
|
return true;
|
||
12 years ago
|
{
|
||
11 years ago
|
LOCK(cs_wallet);
|
||
12 years ago
|
if (pwalletdbEncryption)
|
||
10 years ago
|
return pwalletdbEncryption->WriteCryptedKey(vchPubKey,
|
||
|
vchCryptedSecret,
|
||
10 years ago
|
mapKeyMetadata[vchPubKey.GetID()]);
|
||
12 years ago
|
else
|
||
10 years ago
|
return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey,
|
||
|
vchCryptedSecret,
|
||
10 years ago
|
mapKeyMetadata[vchPubKey.GetID()]);
|
||
12 years ago
|
}
|
||
11 years ago
|
return false;
|
||
12 years ago
|
}
|
||
|
|
||
10 years ago
|
bool CWallet::LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &meta)
|
||
|
{
|
||
9 years ago
|
AssertLockHeld(cs_wallet); // mapKeyMetadata
|
||
10 years ago
|
if (meta.nCreateTime && (!nTimeFirstKey || meta.nCreateTime < nTimeFirstKey))
|
||
|
nTimeFirstKey = meta.nCreateTime;
|
||
|
|
||
|
mapKeyMetadata[pubkey.GetID()] = meta;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
10 years ago
|
bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
|
||
|
{
|
||
|
return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret);
|
||
|
}
|
||
|
|
||
11 years ago
|
bool CWallet::AddCScript(const CScript& redeemScript)
|
||
11 years ago
|
{
|
||
11 years ago
|
if (!CCryptoKeyStore::AddCScript(redeemScript))
|
||
11 years ago
|
return false;
|
||
|
if (!fFileBacked)
|
||
|
return true;
|
||
11 years ago
|
return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript);
|
||
11 years ago
|
}
|
||
|
|
||
9 years ago
|
bool CWallet::LoadCScript(const CScript& redeemScript)
|
||
|
{
|
||
|
/* A sanity check was added in pull #3843 to avoid adding redeemScripts
|
||
|
* that never can be redeemed. However, old wallets may still contain
|
||
|
* these. Do not add them to the wallet and warn. */
|
||
|
if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE)
|
||
|
{
|
||
8 years ago
|
std::string strAddr = CBitcoinAddress(CScriptID(redeemScript)).ToString();
|
||
9 years ago
|
LogPrintf("%s: Warning: This wallet contains a redeemScript of size %i which exceeds maximum size %i thus can never be redeemed. Do not use address %s.\n",
|
||
|
__func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, strAddr);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return CCryptoKeyStore::AddCScript(redeemScript);
|
||
|
}
|
||
|
|
||
9 years ago
|
bool CWallet::AddWatchOnly(const CScript &dest)
|
||
10 years ago
|
{
|
||
|
if (!CCryptoKeyStore::AddWatchOnly(dest))
|
||
|
return false;
|
||
|
nTimeFirstKey = 1; // No birthday information for watch-only keys.
|
||
9 years ago
|
NotifyWatchonlyChanged(true);
|
||
10 years ago
|
if (!fFileBacked)
|
||
|
return true;
|
||
|
return CWalletDB(strWalletFile).WriteWatchOnly(dest);
|
||
|
}
|
||
|
|
||
9 years ago
|
bool CWallet::RemoveWatchOnly(const CScript &dest)
|
||
|
{
|
||
|
AssertLockHeld(cs_wallet);
|
||
|
if (!CCryptoKeyStore::RemoveWatchOnly(dest))
|
||
|
return false;
|
||
|
if (!HaveWatchOnly())
|
||
|
NotifyWatchonlyChanged(false);
|
||
|
if (fFileBacked)
|
||
|
if (!CWalletDB(strWalletFile).EraseWatchOnly(dest))
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
9 years ago
|
bool CWallet::LoadWatchOnly(const CScript &dest)
|
||
10 years ago
|
{
|
||
|
return CCryptoKeyStore::AddWatchOnly(dest);
|
||
|
}
|
||
|
|
||
11 years ago
|
bool CWallet::Unlock(const SecureString& strWalletPassphrase)
|
||
12 years ago
|
{
|
||
12 years ago
|
CCrypter crypter;
|
||
|
CKeyingMaterial vMasterKey;
|
||
12 years ago
|
|
||
11 years ago
|
{
|
||
|
LOCK(cs_wallet);
|
||
12 years ago
|
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))
|
||
10 years ago
|
continue; // try another master key
|
||
12 years ago
|
if (CCryptoKeyStore::Unlock(vMasterKey))
|
||
|
return true;
|
||
|
}
|
||
11 years ago
|
}
|
||
12 years ago
|
return false;
|
||
|
}
|
||
|
|
||
11 years ago
|
bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase)
|
||
12 years ago
|
{
|
||
12 years ago
|
bool fWasLocked = IsLocked();
|
||
12 years ago
|
|
||
12 years ago
|
{
|
||
11 years ago
|
LOCK(cs_wallet);
|
||
12 years ago
|
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;
|
||
12 years ago
|
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey))
|
||
12 years ago
|
return false;
|
||
|
if (CCryptoKeyStore::Unlock(vMasterKey))
|
||
|
{
|
||
10 years ago
|
int64_t nStartTime = GetTimeMillis();
|
||
12 years ago
|
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;
|
||
|
|
||
9 years ago
|
LogPrintf("Wallet passphrase changed to an nDeriveIterations of %i\n", pMasterKey.second.nDeriveIterations);
|
||
12 years ago
|
|
||
12 years ago
|
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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
12 years ago
|
|
||
12 years ago
|
return false;
|
||
|
}
|
||
|
|
||
11 years ago
|
void CWallet::SetBestChain(const CBlockLocator& loc)
|
||
|
{
|
||
|
CWalletDB walletdb(strWalletFile);
|
||
|
walletdb.WriteBestBlock(loc);
|
||
|
}
|
||
12 years ago
|
|
||
11 years ago
|
bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit)
|
||
11 years ago
|
{
|
||
9 years ago
|
LOCK(cs_wallet); // nWalletVersion
|
||
11 years ago
|
if (nWalletVersion >= nVersion)
|
||
|
return true;
|
||
|
|
||
11 years ago
|
// when doing an explicit upgrade, if we pass the max version permitted, upgrade all the way
|
||
|
if (fExplicit && nVersion > nWalletMaxVersion)
|
||
|
nVersion = FEATURE_LATEST;
|
||
|
|
||
11 years ago
|
nWalletVersion = nVersion;
|
||
|
|
||
11 years ago
|
if (nVersion > nWalletMaxVersion)
|
||
|
nWalletMaxVersion = nVersion;
|
||
|
|
||
11 years ago
|
if (fFileBacked)
|
||
|
{
|
||
|
CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile);
|
||
|
if (nWalletVersion > 40000)
|
||
|
pwalletdb->WriteMinVersion(nWalletVersion);
|
||
|
if (!pwalletdbIn)
|
||
|
delete pwalletdb;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
11 years ago
|
bool CWallet::SetMaxVersion(int nVersion)
|
||
|
{
|
||
9 years ago
|
LOCK(cs_wallet); // nWalletVersion, nWalletMaxVersion
|
||
11 years ago
|
// cannot downgrade below current version
|
||
|
if (nWalletVersion > nVersion)
|
||
|
return false;
|
||
|
|
||
|
nWalletMaxVersion = nVersion;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
9 years ago
|
set<uint256> CWallet::GetConflicts(const uint256& txid) const
|
||
9 years ago
|
{
|
||
|
set<uint256> result;
|
||
|
AssertLockHeld(cs_wallet);
|
||
|
|
||
|
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(txid);
|
||
|
if (it == mapWallet.end())
|
||
|
return result;
|
||
|
const CWalletTx& wtx = it->second;
|
||
|
|
||
9 years ago
|
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range;
|
||
9 years ago
|
|
||
|
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
|
||
|
{
|
||
9 years ago
|
if (mapTxSpends.count(txin.prevout) <= 1)
|
||
|
continue; // No conflict if zero or one spends
|
||
|
range = mapTxSpends.equal_range(txin.prevout);
|
||
|
for (TxSpends::const_iterator it = range.first; it != range.second; ++it)
|
||
9 years ago
|
result.insert(it->second);
|
||
9 years ago
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
9 years ago
|
void CWallet::SyncMetaData(pair<TxSpends::iterator, TxSpends::iterator> range)
|
||
9 years ago
|
{
|
||
|
// We want all the wallet transactions in range to have the same metadata as
|
||
|
// the oldest (smallest nOrderPos).
|
||
|
// So: find smallest nOrderPos:
|
||
|
|
||
|
int nMinOrderPos = std::numeric_limits<int>::max();
|
||
|
const CWalletTx* copyFrom = NULL;
|
||
9 years ago
|
for (TxSpends::iterator it = range.first; it != range.second; ++it)
|
||
9 years ago
|
{
|
||
|
const uint256& hash = it->second;
|
||
|
int n = mapWallet[hash].nOrderPos;
|
||
|
if (n < nMinOrderPos)
|
||
|
{
|
||
|
nMinOrderPos = n;
|
||
|
copyFrom = &mapWallet[hash];
|
||
|
}
|
||
|
}
|
||
|
// Now copy data from copyFrom to rest:
|
||
9 years ago
|
for (TxSpends::iterator it = range.first; it != range.second; ++it)
|
||
9 years ago
|
{
|
||
|
const uint256& hash = it->second;
|
||
|
CWalletTx* copyTo = &mapWallet[hash];
|
||
|
if (copyFrom == copyTo) continue;
|
||
|
copyTo->mapValue = copyFrom->mapValue;
|
||
|
copyTo->vOrderForm = copyFrom->vOrderForm;
|
||
|
// fTimeReceivedIsTxTime not copied on purpose
|
||
|
// nTimeReceived not copied on purpose
|
||
|
copyTo->nTimeSmart = copyFrom->nTimeSmart;
|
||
|
copyTo->fFromMe = copyFrom->fFromMe;
|
||
|
copyTo->strFromAccount = copyFrom->strFromAccount;
|
||
|
// nOrderPos not copied on purpose
|
||
|
// cached members not copied on purpose
|
||
|
}
|
||
|
}
|
||
|
|
||
8 years ago
|
/**
|
||
|
* Outpoint is spent if any non-conflicted transaction
|
||
|
* spends it:
|
||
|
*/
|
||
9 years ago
|
bool CWallet::IsSpent(const uint256& hash, unsigned int n) const
|
||
9 years ago
|
{
|
||
9 years ago
|
const COutPoint outpoint(hash, n);
|
||
|
pair<TxSpends::const_iterator, TxSpends::const_iterator> range;
|
||
|
range = mapTxSpends.equal_range(outpoint);
|
||
9 years ago
|
|
||
9 years ago
|
for (TxSpends::const_iterator it = range.first; it != range.second; ++it)
|
||
9 years ago
|
{
|
||
9 years ago
|
const uint256& wtxid = it->second;
|
||
|
std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(wtxid);
|
||
|
if (mit != mapWallet.end() && mit->second.GetDepthInMainChain() >= 0)
|
||
|
return true; // Spent
|
||
9 years ago
|
}
|
||
9 years ago
|
return false;
|
||
|
}
|
||
|
|
||
|
void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid)
|
||
|
{
|
||
|
mapTxSpends.insert(make_pair(outpoint, wtxid));
|
||
|
|
||
|
pair<TxSpends::iterator, TxSpends::iterator> range;
|
||
|
range = mapTxSpends.equal_range(outpoint);
|
||
|
SyncMetaData(range);
|
||
|
}
|
||
|
|
||
|
|
||
|
void CWallet::AddToSpends(const uint256& wtxid)
|
||
|
{
|
||
|
assert(mapWallet.count(wtxid));
|
||
|
CWalletTx& thisTx = mapWallet[wtxid];
|
||
|
if (thisTx.IsCoinBase()) // Coinbases don't spend anything!
|
||
|
return;
|
||
|
|
||
|
BOOST_FOREACH(const CTxIn& txin, thisTx.vin)
|
||
|
AddToSpends(txin.prevout, wtxid);
|
||
9 years ago
|
}
|
||
|
|
||
11 years ago
|
bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
|
||
12 years ago
|
{
|
||
12 years ago
|
if (IsCrypted())
|
||
|
return false;
|
||
12 years ago
|
|
||
12 years ago
|
CKeyingMaterial vMasterKey;
|
||
|
RandAddSeedPerfmon();
|
||
12 years ago
|
|
||
12 years ago
|
vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE);
|
||
8 years ago
|
GetRandBytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE);
|
||
12 years ago
|
|
||
12 years ago
|
CMasterKey kMasterKey;
|
||
|
RandAddSeedPerfmon();
|
||
9 years ago
|
|
||
12 years ago
|
kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE);
|
||
8 years ago
|
GetRandBytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE);
|
||
12 years ago
|
|
||
12 years ago
|
CCrypter crypter;
|
||
10 years ago
|
int64_t nStartTime = GetTimeMillis();
|
||
12 years ago
|
crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod);
|
||
|
kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime));
|
||
12 years ago
|
|
||
12 years ago
|
nStartTime = GetTimeMillis();
|
||
|
crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod);
|
||
|
kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2;
|
||
12 years ago
|
|
||
12 years ago
|
if (kMasterKey.nDeriveIterations < 25000)
|
||
|
kMasterKey.nDeriveIterations = 25000;
|
||
12 years ago
|
|
||
9 years ago
|
LogPrintf("Encrypting Wallet with an nDeriveIterations of %i\n", kMasterKey.nDeriveIterations);
|
||
12 years ago
|
|
||
12 years ago
|
if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod))
|
||
|
return false;
|
||
|
if (!crypter.Encrypt(vMasterKey, kMasterKey.vchCryptedKey))
|
||
|
return false;
|
||
12 years ago
|
|
||
12 years ago
|
{
|
||
11 years ago
|
LOCK(cs_wallet);
|
||
12 years ago
|
mapMasterKeys[++nMasterKeyMaxID] = kMasterKey;
|
||
|
if (fFileBacked)
|
||
|
{
|
||
8 years ago
|
assert(!pwalletdbEncryption);
|
||
12 years ago
|
pwalletdbEncryption = new CWalletDB(strWalletFile);
|
||
8 years ago
|
if (!pwalletdbEncryption->TxnBegin()) {
|
||
|
delete pwalletdbEncryption;
|
||
|
pwalletdbEncryption = NULL;
|
||
11 years ago
|
return false;
|
||
8 years ago
|
}
|
||
12 years ago
|
pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey);
|
||
12 years ago
|
}
|
||
|
|
||
|
if (!EncryptKeys(vMasterKey))
|
||
12 years ago
|
{
|
||
8 years ago
|
if (fFileBacked) {
|
||
12 years ago
|
pwalletdbEncryption->TxnAbort();
|
||
8 years ago
|
delete pwalletdbEncryption;
|
||
|
}
|
||
|
// We now probably have half of our keys encrypted in memory, and half not...
|
||
|
// die and let the user reload their unencrypted wallet.
|
||
8 years ago
|
assert(false);
|
||
12 years ago
|
}
|
||
|
|
||
11 years ago
|
// Encryption was introduced in version 0.4.0
|
||
11 years ago
|
SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true);
|
||
11 years ago
|
|
||
12 years ago
|
if (fFileBacked)
|
||
|
{
|
||
8 years ago
|
if (!pwalletdbEncryption->TxnCommit()) {
|
||
|
delete pwalletdbEncryption;
|
||
8 years ago
|
// We now have keys encrypted in memory, but not on disk...
|
||
8 years ago
|
// die to avoid confusion and let the user reload their unencrypted wallet.
|
||
8 years ago
|
assert(false);
|
||
8 years ago
|
}
|
||
12 years ago
|
|
||
11 years ago
|
delete pwalletdbEncryption;
|
||
12 years ago
|
pwalletdbEncryption = NULL;
|
||
|
}
|
||
12 years ago
|
|
||
11 years ago
|
Lock();
|
||
|
Unlock(strWalletPassphrase);
|
||
|
NewKeyPool();
|
||
12 years ago
|
Lock();
|
||
12 years ago
|
|
||
11 years ago
|
// 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.
|
||
11 years ago
|
CDB::Rewrite(strWalletFile);
|
||
11 years ago
|
|
||
11 years ago
|
}
|
||
11 years ago
|
NotifyStatusChanged(this);
|
||
11 years ago
|
|
||
12 years ago
|
return true;
|
||
12 years ago
|
}
|
||
|
|
||
10 years ago
|
int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb)
|
||
11 years ago
|
{
|
||
9 years ago
|
AssertLockHeld(cs_wallet); // nOrderPosNext
|
||
10 years ago
|
int64_t nRet = nOrderPosNext++;
|
||
10 years ago
|
if (pwalletdb) {
|
||
|
pwalletdb->WriteOrderPosNext(nOrderPosNext);
|
||
|
} else {
|
||
|
CWalletDB(strWalletFile).WriteOrderPosNext(nOrderPosNext);
|
||
|
}
|
||
11 years ago
|
return nRet;
|
||
|
}
|
||
|
|
||
11 years ago
|
CWallet::TxItems CWallet::OrderedTxItems(std::list<CAccountingEntry>& acentries, std::string strAccount)
|
||
11 years ago
|
{
|
||
9 years ago
|
AssertLockHeld(cs_wallet); // mapWallet
|
||
11 years ago
|
CWalletDB walletdb(strWalletFile);
|
||
|
|
||
|
// First: get all CWalletTx and CAccountingEntry into a sorted-by-order multimap.
|
||
|
TxItems txOrdered;
|
||
|
|
||
|
// Note: maintaining indices in the database of (account,time) --> txid and (account, time) --> acentry
|
||
|
// would make this much faster for applications that do this a lot.
|
||
|
for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
|
||
|
{
|
||
|
CWalletTx* wtx = &((*it).second);
|
||
|
txOrdered.insert(make_pair(wtx->nOrderPos, TxPair(wtx, (CAccountingEntry*)0)));
|
||
|
}
|
||
11 years ago
|
acentries.clear();
|
||
11 years ago
|
walletdb.ListAccountCreditDebit(strAccount, acentries);
|
||
|
BOOST_FOREACH(CAccountingEntry& entry, acentries)
|
||
|
{
|
||
|
txOrdered.insert(make_pair(entry.nOrderPos, TxPair((CWalletTx*)0, &entry)));
|
||
|
}
|
||
|
|
||
|
return txOrdered;
|
||
|
}
|
||
|
|
||
12 years ago
|
void CWallet::MarkDirty()
|
||
|
{
|
||
|
{
|
||
11 years ago
|
LOCK(cs_wallet);
|
||
12 years ago
|
BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
|
||
|
item.second.MarkDirty();
|
||
|
}
|
||
|
}
|
||
|
|
||
9 years ago
|
bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletDB* pwalletdb)
|
||
12 years ago
|
{
|
||
|
uint256 hash = wtxIn.GetHash();
|
||
9 years ago
|
|
||
|
if (fFromLoadWallet)
|
||
|
{
|
||
|
mapWallet[hash] = wtxIn;
|
||
9 years ago
|
mapWallet[hash].BindWallet(this);
|
||
9 years ago
|
AddToSpends(hash);
|
||
9 years ago
|
}
|
||
|
else
|
||
12 years ago
|
{
|
||
11 years ago
|
LOCK(cs_wallet);
|
||
12 years ago
|
// 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;
|
||
12 years ago
|
wtx.BindWallet(this);
|
||
12 years ago
|
bool fInsertedNew = ret.second;
|
||
|
if (fInsertedNew)
|
||
11 years ago
|
{
|
||
12 years ago
|
wtx.nTimeReceived = GetAdjustedTime();
|
||
9 years ago
|
wtx.nOrderPos = IncOrderPosNext(pwalletdb);
|
||
11 years ago
|
|
||
|
wtx.nTimeSmart = wtx.nTimeReceived;
|
||
8 years ago
|
if (!wtxIn.hashBlock.IsNull())
|
||
11 years ago
|
{
|
||
|
if (mapBlockIndex.count(wtxIn.hashBlock))
|
||
|
{
|
||
9 years ago
|
int64_t latestNow = wtx.nTimeReceived;
|
||
|
int64_t latestEntry = 0;
|
||
11 years ago
|
{
|
||
|
// Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future
|
||
10 years ago
|
int64_t latestTolerated = latestNow + 300;
|
||
11 years ago
|
std::list<CAccountingEntry> acentries;
|
||
|
TxItems txOrdered = OrderedTxItems(acentries);
|
||
11 years ago
|
for (TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
|
||
|
{
|
||
|
CWalletTx *const pwtx = (*it).second.first;
|
||
|
if (pwtx == &wtx)
|
||
|
continue;
|
||
|
CAccountingEntry *const pacentry = (*it).second.second;
|
||
10 years ago
|
int64_t nSmartTime;
|
||
11 years ago
|
if (pwtx)
|
||
|
{
|
||
|
nSmartTime = pwtx->nTimeSmart;
|
||
|
if (!nSmartTime)
|
||
|
nSmartTime = pwtx->nTimeReceived;
|
||
|
}
|
||
|
else
|
||
|
nSmartTime = pacentry->nTime;
|
||
|
if (nSmartTime <= latestTolerated)
|
||
|
{
|
||
|
latestEntry = nSmartTime;
|
||
|
if (nSmartTime > latestNow)
|
||
|
latestNow = nSmartTime;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
9 years ago
|
int64_t blocktime = mapBlockIndex[wtxIn.hashBlock]->GetBlockTime();
|
||
11 years ago
|
wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow));
|
||
|
}
|
||
|
else
|
||
8 years ago
|
LogPrintf("AddToWallet(): found %s in block %s not in index\n",
|
||
9 years ago
|
wtxIn.GetHash().ToString(),
|
||
|
wtxIn.hashBlock.ToString());
|
||
11 years ago
|
}
|
||
9 years ago
|
AddToSpends(hash);
|
||
11 years ago
|
}
|
||
12 years ago
|
|
||
|
bool fUpdated = false;
|
||
|
if (!fInsertedNew)
|
||
|
{
|
||
|
// Merge
|
||
8 years ago
|
if (!wtxIn.hashBlock.IsNull() && wtxIn.hashBlock != wtx.hashBlock)
|
||
12 years ago
|
{
|
||
|
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
|
||
9 years ago
|
LogPrintf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : ""));
|
||
12 years ago
|
|
||
|
// Write to disk
|
||
|
if (fInsertedNew || fUpdated)
|
||
9 years ago
|
if (!wtx.WriteToDisk(pwalletdb))
|
||
12 years ago
|
return false;
|
||
10 years ago
|
|
||
9 years ago
|
// Break debit/credit balance caches:
|
||
|
wtx.MarkDirty();
|
||
12 years ago
|
|
||
11 years ago
|
// Notify UI of new or updated transaction
|
||
|
NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED);
|
||
10 years ago
|
|
||
|
// 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
|
||
|
}
|
||
|
|
||
11 years ago
|
}
|
||
12 years ago
|
return true;
|
||
|
}
|
||
|
|
||
8 years ago
|
/**
|
||
|
* 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.
|
||
|
*/
|
||
9 years ago
|
bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate)
|
||
12 years ago
|
{
|
||
|
{
|
||
9 years ago
|
AssertLockHeld(cs_wallet);
|
||
9 years ago
|
bool fExisted = mapWallet.count(tx.GetHash()) != 0;
|
||
12 years ago
|
if (fExisted && !fUpdate) return false;
|
||
9 years ago
|
if (fExisted || IsMine(tx) || IsFromMe(tx))
|
||
12 years ago
|
{
|
||
|
CWalletTx wtx(this,tx);
|
||
9 years ago
|
|
||
12 years ago
|
// Get merkle branch if transaction was found in a block
|
||
|
if (pblock)
|
||
9 years ago
|
wtx.SetMerkleBranch(*pblock);
|
||
9 years ago
|
|
||
|
// Do not flush the wallet here for performance reasons
|
||
|
// this is safe, as in case of a crash, we rescan the necessary blocks on startup through our SetBestChain-mechanism
|
||
|
CWalletDB walletdb(strWalletFile, "r+", false);
|
||
|
|
||
|
return AddToWallet(wtx, false, &walletdb);
|
||
12 years ago
|
}
|
||
12 years ago
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
9 years ago
|
void CWallet::SyncTransaction(const CTransaction& tx, const CBlock* pblock)
|
||
9 years ago
|
{
|
||
9 years ago
|
LOCK2(cs_main, cs_wallet);
|
||
9 years ago
|
if (!AddToWalletIfInvolvingMe(tx, pblock, true))
|
||
|