Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
Show All 25 Lines | |||||
#include <script/signingprovider.h> | #include <script/signingprovider.h> | ||||
#include <util/bip32.h> | #include <util/bip32.h> | ||||
#include <util/error.h> | #include <util/error.h> | ||||
#include <util/moneystr.h> | #include <util/moneystr.h> | ||||
#include <util/translation.h> | #include <util/translation.h> | ||||
#include <util/validation.h> | #include <util/validation.h> | ||||
#include <wallet/coincontrol.h> | #include <wallet/coincontrol.h> | ||||
#include <wallet/fees.h> | #include <wallet/fees.h> | ||||
#include <wallet/scriptpubkeyman.h> | |||||
#include <boost/algorithm/string/replace.hpp> | #include <boost/algorithm/string/replace.hpp> | ||||
#include <cassert> | #include <cassert> | ||||
const std::map<uint64_t, std::string> WALLET_FLAG_CAVEATS{ | const std::map<uint64_t, std::string> WALLET_FLAG_CAVEATS{ | ||||
{WALLET_FLAG_AVOID_REUSE, | {WALLET_FLAG_AVOID_REUSE, | ||||
"You need to rescan the blockchain in order to correctly mark used " | "You need to rescan the blockchain in order to correctly mark used " | ||||
▲ Show 20 Lines • Show All 197 Lines • ▼ Show 20 Lines | if (!passphrase.empty() && | ||||
} | } | ||||
} | } | ||||
AddWallet(wallet); | AddWallet(wallet); | ||||
wallet->postInitProcess(); | wallet->postInitProcess(); | ||||
result = wallet; | result = wallet; | ||||
return WalletCreationStatus::SUCCESS; | return WalletCreationStatus::SUCCESS; | ||||
} | } | ||||
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; | |||||
const BlockHash CWalletTx::ABANDON_HASH(uint256S( | const BlockHash CWalletTx::ABANDON_HASH(uint256S( | ||||
"0000000000000000000000000000000000000000000000000000000000000001")); | "0000000000000000000000000000000000000000000000000000000000000001")); | ||||
/** @defgroup mapWallet | /** @defgroup mapWallet | ||||
* | * | ||||
* @{ | * @{ | ||||
*/ | */ | ||||
std::string COutput::ToString() const { | std::string COutput::ToString() const { | ||||
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetId().ToString(), i, | return strprintf("COutput(%s, %d, %d) [%s]", tx->GetId().ToString(), i, | ||||
nDepth, FormatMoney(tx->tx->vout[i].nValue)); | nDepth, FormatMoney(tx->tx->vout[i].nValue)); | ||||
} | } | ||||
std::vector<CKeyID> GetAffectedKeys(const CScript &spk, | std::vector<CKeyID> GetAffectedKeys(const CScript &spk, | ||||
const SigningProvider &provider) { | const SigningProvider &provider); | ||||
std::vector<CScript> dummy; | |||||
FlatSigningProvider out; | |||||
InferDescriptor(spk, provider) | |||||
->Expand(0, DUMMY_SIGNING_PROVIDER, dummy, out); | |||||
std::vector<CKeyID> ret; | |||||
for (const auto &entry : out.pubkeys) { | |||||
ret.push_back(entry.first); | |||||
} | |||||
return ret; | |||||
} | |||||
const CWalletTx *CWallet::GetWalletTx(const TxId &txid) const { | const CWalletTx *CWallet::GetWalletTx(const TxId &txid) const { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
std::map<TxId, CWalletTx>::const_iterator it = mapWallet.find(txid); | std::map<TxId, CWalletTx>::const_iterator it = mapWallet.find(txid); | ||||
if (it == mapWallet.end()) { | if (it == mapWallet.end()) { | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
return &(it->second); | return &(it->second); | ||||
} | } | ||||
CPubKey CWallet::GenerateNewKey(WalletBatch &batch, bool internal) { | |||||
assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); | |||||
assert(!IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET)); | |||||
AssertLockHeld(cs_wallet); | |||||
// default to compressed public keys if we want 0.6.0 wallets | |||||
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); | |||||
CKey secret; | |||||
// Create new metadata | |||||
int64_t nCreationTime = GetTime(); | |||||
CKeyMetadata metadata(nCreationTime); | |||||
// use HD key derivation if HD was enabled during wallet creation and a seed | |||||
// is present | |||||
if (IsHDEnabled()) { | |||||
DeriveNewChildKey( | |||||
batch, metadata, secret, | |||||
(CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false)); | |||||
} else { | |||||
secret.MakeNewKey(fCompressed); | |||||
} | |||||
// Compressed public keys were introduced in version 0.6.0 | |||||
if (fCompressed) { | |||||
SetMinVersion(FEATURE_COMPRPUBKEY); | |||||
} | |||||
CPubKey pubkey = secret.GetPubKey(); | |||||
assert(secret.VerifyPubKey(pubkey)); | |||||
mapKeyMetadata[pubkey.GetID()] = metadata; | |||||
UpdateTimeFirstKey(nCreationTime); | |||||
if (!AddKeyPubKeyWithDB(batch, secret, pubkey)) { | |||||
throw std::runtime_error(std::string(__func__) + ": AddKey failed"); | |||||
} | |||||
return pubkey; | |||||
} | |||||
void CWallet::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata &metadata, | |||||
CKey &secret, bool internal) { | |||||
// for now we use a fixed keypath scheme of m/0'/0'/k | |||||
// seed (256bit) | |||||
CKey seed; | |||||
// hd master key | |||||
CExtKey masterKey; | |||||
// key at m/0' | |||||
CExtKey accountKey; | |||||
// key at m/0'/0' (external) or m/0'/1' (internal) | |||||
CExtKey chainChildKey; | |||||
// key at m/0'/0'/<n>' | |||||
CExtKey childKey; | |||||
// try to get the seed | |||||
if (!GetKey(hdChain.seed_id, seed)) { | |||||
throw std::runtime_error(std::string(__func__) + ": seed not found"); | |||||
} | |||||
masterKey.SetSeed(seed.begin(), seed.size()); | |||||
// derive m/0' | |||||
// use hardened derivation (child keys >= 0x80000000 are hardened after | |||||
// bip32) | |||||
masterKey.Derive(accountKey, BIP32_HARDENED_KEY_LIMIT); | |||||
// derive m/0'/0' (external chain) OR m/0'/1' (internal chain) | |||||
assert(internal ? CanSupportFeature(FEATURE_HD_SPLIT) : true); | |||||
accountKey.Derive(chainChildKey, | |||||
BIP32_HARDENED_KEY_LIMIT + (internal ? 1 : 0)); | |||||
// derive child key at next index, skip keys already known to the wallet | |||||
do { | |||||
// always derive hardened keys | |||||
// childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened | |||||
// child-index-range | |||||
// example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649 | |||||
if (internal) { | |||||
chainChildKey.Derive(childKey, hdChain.nInternalChainCounter | | |||||
BIP32_HARDENED_KEY_LIMIT); | |||||
metadata.hdKeypath = "m/0'/1'/" + | |||||
std::to_string(hdChain.nInternalChainCounter) + | |||||
"'"; | |||||
metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT); | |||||
metadata.key_origin.path.push_back(1 | BIP32_HARDENED_KEY_LIMIT); | |||||
metadata.key_origin.path.push_back(hdChain.nInternalChainCounter | | |||||
BIP32_HARDENED_KEY_LIMIT); | |||||
hdChain.nInternalChainCounter++; | |||||
} else { | |||||
chainChildKey.Derive(childKey, hdChain.nExternalChainCounter | | |||||
BIP32_HARDENED_KEY_LIMIT); | |||||
metadata.hdKeypath = "m/0'/0'/" + | |||||
std::to_string(hdChain.nExternalChainCounter) + | |||||
"'"; | |||||
metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT); | |||||
metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT); | |||||
metadata.key_origin.path.push_back(hdChain.nExternalChainCounter | | |||||
BIP32_HARDENED_KEY_LIMIT); | |||||
hdChain.nExternalChainCounter++; | |||||
} | |||||
} while (HaveKey(childKey.key.GetPubKey().GetID())); | |||||
secret = childKey.key; | |||||
metadata.hd_seed_id = hdChain.seed_id; | |||||
CKeyID master_id = masterKey.key.GetPubKey().GetID(); | |||||
std::copy(master_id.begin(), master_id.begin() + 4, | |||||
metadata.key_origin.fingerprint); | |||||
metadata.has_key_origin = true; | |||||
// update the chain model in the database | |||||
if (!batch.WriteHDChain(hdChain)) { | |||||
throw std::runtime_error(std::string(__func__) + | |||||
": Writing HD chain model failed"); | |||||
} | |||||
} | |||||
bool CWallet::AddKeyPubKeyWithDB(WalletBatch &batch, const CKey &secret, | |||||
const CPubKey &pubkey) { | |||||
AssertLockHeld(cs_wallet); | |||||
// Make sure we aren't adding private keys to private key disabled wallets | |||||
assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); | |||||
// FillableSigningProvider has no concept of wallet databases, but calls | |||||
// AddCryptedKey which is overridden below. To avoid flushes, the database | |||||
// handle is tunneled through to it. | |||||
bool needsDB = !encrypted_batch; | |||||
if (needsDB) { | |||||
encrypted_batch = &batch; | |||||
} | |||||
if (!AddKeyPubKeyInner(secret, pubkey)) { | |||||
if (needsDB) { | |||||
encrypted_batch = nullptr; | |||||
} | |||||
return false; | |||||
} | |||||
if (needsDB) { | |||||
encrypted_batch = nullptr; | |||||
} | |||||
// Check if we need to remove from watch-only. | |||||
CScript script; | |||||
script = GetScriptForDestination(PKHash(pubkey)); | |||||
if (HaveWatchOnly(script)) { | |||||
RemoveWatchOnly(script); | |||||
} | |||||
script = GetScriptForRawPubKey(pubkey); | |||||
if (HaveWatchOnly(script)) { | |||||
RemoveWatchOnly(script); | |||||
} | |||||
if (!IsCrypted()) { | |||||
return batch.WriteKey(pubkey, secret.GetPrivKey(), | |||||
mapKeyMetadata[pubkey.GetID()]); | |||||
} | |||||
UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET); | |||||
return true; | |||||
} | |||||
bool CWallet::AddKeyPubKey(const CKey &secret, const CPubKey &pubkey) { | |||||
WalletBatch batch(*database); | |||||
return CWallet::AddKeyPubKeyWithDB(batch, secret, pubkey); | |||||
} | |||||
bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, | |||||
const std::vector<uint8_t> &vchCryptedSecret) { | |||||
if (!AddCryptedKeyInner(vchPubKey, vchCryptedSecret)) { | |||||
return false; | |||||
} | |||||
LOCK(cs_wallet); | |||||
if (encrypted_batch) { | |||||
return encrypted_batch->WriteCryptedKey( | |||||
vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]); | |||||
} | |||||
return WalletBatch(*database).WriteCryptedKey( | |||||
vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]); | |||||
} | |||||
void CWallet::LoadKeyMetadata(const CKeyID &keyID, const CKeyMetadata &meta) { | |||||
AssertLockHeld(cs_wallet); | |||||
UpdateTimeFirstKey(meta.nCreateTime); | |||||
mapKeyMetadata[keyID] = meta; | |||||
} | |||||
void CWallet::LoadScriptMetadata(const CScriptID &script_id, | |||||
const CKeyMetadata &meta) { | |||||
AssertLockHeld(cs_wallet); | |||||
UpdateTimeFirstKey(meta.nCreateTime); | |||||
m_script_metadata[script_id] = meta; | |||||
} | |||||
void CWallet::UpgradeKeyMetadata() { | |||||
AssertLockHeld(cs_wallet); | |||||
if (IsLocked() || IsWalletFlagSet(WALLET_FLAG_KEY_ORIGIN_METADATA)) { | |||||
return; | |||||
} | |||||
auto batch = std::make_unique<WalletBatch>(*database); | |||||
for (auto &meta_pair : mapKeyMetadata) { | |||||
CKeyMetadata &meta = meta_pair.second; | |||||
// If the hdKeypath is "s", that's the seed and it doesn't have a key | |||||
// origin | |||||
if (!meta.hd_seed_id.IsNull() && !meta.has_key_origin && | |||||
meta.hdKeypath != "s") { | |||||
CKey key; | |||||
GetKey(meta.hd_seed_id, key); | |||||
CExtKey masterKey; | |||||
masterKey.SetSeed(key.begin(), key.size()); | |||||
// Add to map | |||||
CKeyID master_id = masterKey.key.GetPubKey().GetID(); | |||||
std::copy(master_id.begin(), master_id.begin() + 4, | |||||
meta.key_origin.fingerprint); | |||||
if (!ParseHDKeypath(meta.hdKeypath, meta.key_origin.path)) { | |||||
throw std::runtime_error("Invalid stored hdKeypath"); | |||||
} | |||||
meta.has_key_origin = true; | |||||
if (meta.nVersion < CKeyMetadata::VERSION_WITH_KEY_ORIGIN) { | |||||
meta.nVersion = CKeyMetadata::VERSION_WITH_KEY_ORIGIN; | |||||
} | |||||
// Write meta to wallet | |||||
CPubKey pubkey; | |||||
if (GetPubKey(meta_pair.first, pubkey)) { | |||||
batch->WriteKeyMetadata(meta, pubkey, true); | |||||
} | |||||
} | |||||
} | |||||
// write before setting the flag | |||||
batch.reset(); | |||||
SetWalletFlag(WALLET_FLAG_KEY_ORIGIN_METADATA); | |||||
} | |||||
bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, | |||||
const std::vector<uint8_t> &vchCryptedSecret) { | |||||
return AddCryptedKeyInner(vchPubKey, vchCryptedSecret); | |||||
} | |||||
/** | |||||
* Update wallet first key creation time. This should be called whenever keys | |||||
* are added to the wallet, with the oldest key creation time. | |||||
*/ | |||||
void CWallet::UpdateTimeFirstKey(int64_t nCreateTime) { | |||||
AssertLockHeld(cs_wallet); | |||||
if (nCreateTime <= 1) { | |||||
// Cannot determine birthday information, so set the wallet birthday to | |||||
// the beginning of time. | |||||
nTimeFirstKey = 1; | |||||
} else if (!nTimeFirstKey || nCreateTime < nTimeFirstKey) { | |||||
nTimeFirstKey = nCreateTime; | |||||
} | |||||
} | |||||
bool CWallet::AddCScript(const CScript &redeemScript) { | |||||
WalletBatch batch(*database); | |||||
return AddCScriptWithDB(batch, redeemScript); | |||||
} | |||||
bool CWallet::AddCScriptWithDB(WalletBatch &batch, | |||||
const CScript &redeemScript) { | |||||
if (!FillableSigningProvider::AddCScript(redeemScript)) { | |||||
return false; | |||||
} | |||||
if (batch.WriteCScript(Hash160(redeemScript), redeemScript)) { | |||||
UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET); | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
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) { | |||||
std::string strAddr = | |||||
EncodeDestination(ScriptHash(redeemScript), GetConfig()); | |||||
WalletLogPrintf("%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 FillableSigningProvider::AddCScript(redeemScript); | |||||
} | |||||
static bool ExtractPubKey(const CScript &dest, CPubKey &pubKeyOut) { | |||||
std::vector<std::vector<uint8_t>> solutions; | |||||
return Solver(dest, solutions) == TX_PUBKEY && | |||||
(pubKeyOut = CPubKey(solutions[0])).IsFullyValid(); | |||||
} | |||||
bool CWallet::AddWatchOnlyInMem(const CScript &dest) { | |||||
LOCK(cs_KeyStore); | |||||
setWatchOnly.insert(dest); | |||||
CPubKey pubKey; | |||||
if (ExtractPubKey(dest, pubKey)) { | |||||
mapWatchKeys[pubKey.GetID()] = pubKey; | |||||
} | |||||
return true; | |||||
} | |||||
bool CWallet::AddWatchOnlyWithDB(WalletBatch &batch, const CScript &dest) { | |||||
if (!AddWatchOnlyInMem(dest)) { | |||||
return false; | |||||
} | |||||
const CKeyMetadata &meta = m_script_metadata[CScriptID(dest)]; | |||||
UpdateTimeFirstKey(meta.nCreateTime); | |||||
NotifyWatchonlyChanged(true); | |||||
if (batch.WriteWatchOnly(dest, meta)) { | |||||
UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET); | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
bool CWallet::AddWatchOnlyWithDB(WalletBatch &batch, const CScript &dest, | |||||
int64_t create_time) { | |||||
m_script_metadata[CScriptID(dest)].nCreateTime = create_time; | |||||
return AddWatchOnlyWithDB(batch, dest); | |||||
} | |||||
bool CWallet::AddWatchOnly(const CScript &dest) { | |||||
WalletBatch batch(*database); | |||||
return AddWatchOnlyWithDB(batch, dest); | |||||
} | |||||
bool CWallet::AddWatchOnly(const CScript &dest, int64_t nCreateTime) { | |||||
m_script_metadata[CScriptID(dest)].nCreateTime = nCreateTime; | |||||
return AddWatchOnly(dest); | |||||
} | |||||
bool CWallet::RemoveWatchOnly(const CScript &dest) { | |||||
AssertLockHeld(cs_wallet); | |||||
{ | |||||
LOCK(cs_KeyStore); | |||||
setWatchOnly.erase(dest); | |||||
CPubKey pubKey; | |||||
if (ExtractPubKey(dest, pubKey)) { | |||||
mapWatchKeys.erase(pubKey.GetID()); | |||||
} | |||||
} | |||||
if (!HaveWatchOnly()) { | |||||
NotifyWatchonlyChanged(false); | |||||
} | |||||
return WalletBatch(*database).EraseWatchOnly(dest); | |||||
} | |||||
bool CWallet::LoadWatchOnly(const CScript &dest) { | |||||
return AddWatchOnlyInMem(dest); | |||||
} | |||||
bool CWallet::HaveWatchOnly(const CScript &dest) const { | |||||
LOCK(cs_KeyStore); | |||||
return setWatchOnly.count(dest) > 0; | |||||
} | |||||
bool CWallet::HaveWatchOnly() const { | |||||
LOCK(cs_KeyStore); | |||||
return (!setWatchOnly.empty()); | |||||
} | |||||
bool CWallet::Unlock(const SecureString &strWalletPassphrase, | bool CWallet::Unlock(const SecureString &strWalletPassphrase, | ||||
bool accept_no_keys) { | bool accept_no_keys) { | ||||
CCrypter crypter; | CCrypter crypter; | ||||
CKeyingMaterial _vMasterKey; | CKeyingMaterial _vMasterKey; | ||||
{ | { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
for (const MasterKeyMap::value_type &pMasterKey : mapMasterKeys) { | for (const MasterKeyMap::value_type &pMasterKey : mapMasterKeys) { | ||||
▲ Show 20 Lines • Show All 931 Lines • ▼ Show 20 Lines | Amount CWallet::GetDebit(const CTxIn &txin, const isminefilter &filter) const { | ||||
return Amount::zero(); | return Amount::zero(); | ||||
} | } | ||||
isminetype CWallet::IsMine(const CTxOut &txout) const { | isminetype CWallet::IsMine(const CTxOut &txout) const { | ||||
return ::IsMine(*this, txout.scriptPubKey); | return ::IsMine(*this, txout.scriptPubKey); | ||||
} | } | ||||
isminetype IsMine(const CWallet &keystore, const CTxDestination &dest) { | |||||
CScript script = GetScriptForDestination(dest); | |||||
return IsMine(keystore, script); | |||||
} | |||||
Amount CWallet::GetCredit(const CTxOut &txout, | Amount CWallet::GetCredit(const CTxOut &txout, | ||||
const isminefilter &filter) const { | const isminefilter &filter) const { | ||||
if (!MoneyRange(txout.nValue)) { | if (!MoneyRange(txout.nValue)) { | ||||
throw std::runtime_error(std::string(__func__) + | throw std::runtime_error(std::string(__func__) + | ||||
": value out of range"); | ": value out of range"); | ||||
} | } | ||||
return (IsMine(txout) & filter) ? txout.nValue : Amount::zero(); | return (IsMine(txout) & filter) ? txout.nValue : Amount::zero(); | ||||
▲ Show 20 Lines • Show All 112 Lines • ▼ Show 20 Lines | for (const CTxOut &txout : tx.vout) { | ||||
throw std::runtime_error(std::string(__func__) + | throw std::runtime_error(std::string(__func__) + | ||||
": value out of range"); | ": value out of range"); | ||||
} | } | ||||
} | } | ||||
return nChange; | return nChange; | ||||
} | } | ||||
CPubKey CWallet::GenerateNewSeed() { | |||||
assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); | |||||
CKey key; | |||||
key.MakeNewKey(true); | |||||
return DeriveNewSeed(key); | |||||
} | |||||
CPubKey CWallet::DeriveNewSeed(const CKey &key) { | |||||
int64_t nCreationTime = GetTime(); | |||||
CKeyMetadata metadata(nCreationTime); | |||||
// Calculate the seed | |||||
CPubKey seed = key.GetPubKey(); | |||||
assert(key.VerifyPubKey(seed)); | |||||
// Set the hd keypath to "s" -> Seed, refers the seed to itself | |||||
metadata.hdKeypath = "s"; | |||||
metadata.has_key_origin = false; | |||||
metadata.hd_seed_id = seed.GetID(); | |||||
LOCK(cs_wallet); | |||||
// mem store the metadata | |||||
mapKeyMetadata[seed.GetID()] = metadata; | |||||
// Write the key&metadata to the database | |||||
if (!AddKeyPubKey(key, seed)) { | |||||
throw std::runtime_error(std::string(__func__) + | |||||
": AddKeyPubKey failed"); | |||||
} | |||||
return seed; | |||||
} | |||||
void CWallet::SetHDSeed(const CPubKey &seed) { | |||||
LOCK(cs_wallet); | |||||
// Store the keyid (hash160) together with the child index counter in the | |||||
// database as a hdchain object. | |||||
CHDChain newHdChain; | |||||
newHdChain.nVersion = CanSupportFeature(FEATURE_HD_SPLIT) | |||||
? CHDChain::VERSION_HD_CHAIN_SPLIT | |||||
: CHDChain::VERSION_HD_BASE; | |||||
newHdChain.seed_id = seed.GetID(); | |||||
SetHDChain(newHdChain, false); | |||||
NotifyCanGetAddressesChanged(); | |||||
UnsetWalletFlag(WALLET_FLAG_BLANK_WALLET); | |||||
} | |||||
void CWallet::SetHDChain(const CHDChain &chain, bool memonly) { | |||||
LOCK(cs_wallet); | |||||
if (!memonly && !WalletBatch(*database).WriteHDChain(chain)) { | |||||
throw std::runtime_error(std::string(__func__) + | |||||
": writing chain failed"); | |||||
} | |||||
hdChain = chain; | |||||
} | |||||
bool CWallet::IsHDEnabled() const { | |||||
return !hdChain.seed_id.IsNull(); | |||||
} | |||||
bool CWallet::CanGenerateKeys() { | |||||
// A wallet can generate keys if it has an HD seed (IsHDEnabled) or it is a | |||||
// non-HD wallet (pre FEATURE_HD) | |||||
LOCK(cs_wallet); | |||||
return IsHDEnabled() || !CanSupportFeature(FEATURE_HD); | |||||
} | |||||
bool CWallet::CanGetAddresses(bool internal) { | |||||
LOCK(cs_wallet); | |||||
// Check if the keypool has keys | |||||
bool keypool_has_keys; | |||||
if (internal && CanSupportFeature(FEATURE_HD_SPLIT)) { | |||||
keypool_has_keys = setInternalKeyPool.size() > 0; | |||||
} else { | |||||
keypool_has_keys = KeypoolCountExternalKeys() > 0; | |||||
} | |||||
// If the keypool doesn't have keys, check if we can generate them | |||||
if (!keypool_has_keys) { | |||||
return CanGenerateKeys(); | |||||
} | |||||
return keypool_has_keys; | |||||
} | |||||
void CWallet::SetWalletFlag(uint64_t flags) { | void CWallet::SetWalletFlag(uint64_t flags) { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
m_wallet_flags |= flags; | m_wallet_flags |= flags; | ||||
if (!WalletBatch(*database).WriteWalletFlags(m_wallet_flags)) { | if (!WalletBatch(*database).WriteWalletFlags(m_wallet_flags)) { | ||||
throw std::runtime_error(std::string(__func__) + | throw std::runtime_error(std::string(__func__) + | ||||
": writing wallet flags failed"); | ": writing wallet flags failed"); | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | for (const auto &txout : txouts) { | ||||
return false; | return false; | ||||
} | } | ||||
nIn++; | nIn++; | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool CWallet::ImportScripts(const std::set<CScript> scripts, | |||||
int64_t timestamp) { | |||||
WalletBatch batch(*database); | |||||
for (const auto &entry : scripts) { | |||||
CScriptID id(entry); | |||||
if (HaveCScript(id)) { | |||||
WalletLogPrintf("Already have script %s, skipping\n", | |||||
HexStr(entry)); | |||||
continue; | |||||
} | |||||
if (!AddCScriptWithDB(batch, entry)) { | |||||
return false; | |||||
} | |||||
if (timestamp > 0) { | |||||
m_script_metadata[CScriptID(entry)].nCreateTime = timestamp; | |||||
} | |||||
} | |||||
if (timestamp > 0) { | |||||
UpdateTimeFirstKey(timestamp); | |||||
} | |||||
return true; | |||||
} | |||||
bool CWallet::ImportPrivKeys(const std::map<CKeyID, CKey> &privkey_map, | |||||
const int64_t timestamp) { | |||||
WalletBatch batch(*database); | |||||
for (const auto &entry : privkey_map) { | |||||
const CKey &key = entry.second; | |||||
CPubKey pubkey = key.GetPubKey(); | |||||
const CKeyID &id = entry.first; | |||||
assert(key.VerifyPubKey(pubkey)); | |||||
// Skip if we already have the key | |||||
if (HaveKey(id)) { | |||||
WalletLogPrintf("Already have key with pubkey %s, skipping\n", | |||||
HexStr(pubkey)); | |||||
continue; | |||||
} | |||||
mapKeyMetadata[id].nCreateTime = timestamp; | |||||
// If the private key is not present in the wallet, insert it. | |||||
if (!AddKeyPubKeyWithDB(batch, key, pubkey)) { | |||||
return false; | |||||
} | |||||
UpdateTimeFirstKey(timestamp); | |||||
} | |||||
return true; | |||||
} | |||||
bool CWallet::ImportPubKeys( | |||||
const std::vector<CKeyID> &ordered_pubkeys, | |||||
const std::map<CKeyID, CPubKey> &pubkey_map, | |||||
const std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> &key_origins, | |||||
const bool add_keypool, const bool internal, const int64_t timestamp) { | |||||
WalletBatch batch(*database); | |||||
for (const auto &entry : key_origins) { | |||||
AddKeyOriginWithDB(batch, entry.second.first, entry.second.second); | |||||
} | |||||
for (const CKeyID &id : ordered_pubkeys) { | |||||
auto entry = pubkey_map.find(id); | |||||
if (entry == pubkey_map.end()) { | |||||
continue; | |||||
} | |||||
const CPubKey &pubkey = entry->second; | |||||
CPubKey temp; | |||||
if (GetPubKey(id, temp)) { | |||||
// Already have pubkey, skipping | |||||
WalletLogPrintf("Already have pubkey %s, skipping\n", HexStr(temp)); | |||||
continue; | |||||
} | |||||
if (!AddWatchOnlyWithDB(batch, GetScriptForRawPubKey(pubkey), | |||||
timestamp)) { | |||||
return false; | |||||
} | |||||
mapKeyMetadata[id].nCreateTime = timestamp; | |||||
// Add to keypool only works with pubkeys | |||||
if (add_keypool) { | |||||
AddKeypoolPubkeyWithDB(pubkey, internal, batch); | |||||
NotifyCanGetAddressesChanged(); | |||||
} | |||||
} | |||||
return true; | |||||
} | |||||
bool CWallet::ImportScriptPubKeys(const std::string &label, | |||||
const std::set<CScript> &script_pub_keys, | |||||
const bool have_solving_data, | |||||
const bool apply_label, | |||||
const int64_t timestamp) { | |||||
WalletBatch batch(*database); | |||||
for (const CScript &script : script_pub_keys) { | |||||
if (!have_solving_data || !::IsMine(*this, script)) { | |||||
// Always call AddWatchOnly for non-solvable watch-only, so that | |||||
// watch timestamp gets updated | |||||
if (!AddWatchOnlyWithDB(batch, script, timestamp)) { | |||||
return false; | |||||
} | |||||
} | |||||
CTxDestination dest; | |||||
ExtractDestination(script, dest); | |||||
if (apply_label && IsValidDestination(dest)) { | |||||
SetAddressBookWithDB(batch, dest, label, "receive"); | |||||
} | |||||
} | |||||
return true; | |||||
} | |||||
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, | int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, | ||||
const CWallet *wallet, bool use_max_sig) { | const CWallet *wallet, bool use_max_sig) { | ||||
std::vector<CTxOut> txouts; | std::vector<CTxOut> txouts; | ||||
// Look up the inputs. We should have already checked that this transaction | // Look up the inputs. We should have already checked that this transaction | ||||
// IsAllFromMe(ISMINE_SPENDABLE), so every input should already be in our | // IsAllFromMe(ISMINE_SPENDABLE), so every input should already be in our | ||||
// wallet, with a valid index into the vout array, and the ability to sign. | // wallet, with a valid index into the vout array, and the ability to sign. | ||||
for (auto &input : tx.vin) { | for (auto &input : tx.vin) { | ||||
const auto mi = wallet->mapWallet.find(input.prevout.GetTxId()); | const auto mi = wallet->mapWallet.find(input.prevout.GetTxId()); | ||||
▲ Show 20 Lines • Show All 1,830 Lines • ▼ Show 20 Lines | bool CWallet::DelAddressBook(const CTxDestination &address) { | ||||
NotifyAddressBookChanged(this, address, "", | NotifyAddressBookChanged(this, address, "", | ||||
::IsMine(*this, address) != ISMINE_NO, "", | ::IsMine(*this, address) != ISMINE_NO, "", | ||||
CT_DELETED); | CT_DELETED); | ||||
WalletBatch(*database).ErasePurpose(address); | WalletBatch(*database).ErasePurpose(address); | ||||
return WalletBatch(*database).EraseName(address); | return WalletBatch(*database).EraseName(address); | ||||
} | } | ||||
/** | |||||
* Mark old keypool keys as used, and generate all new keys. | |||||
*/ | |||||
bool CWallet::NewKeyPool() { | |||||
if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { | |||||
return false; | |||||
} | |||||
LOCK(cs_wallet); | |||||
WalletBatch batch(*database); | |||||
for (const int64_t nIndex : setInternalKeyPool) { | |||||
batch.ErasePool(nIndex); | |||||
} | |||||
setInternalKeyPool.clear(); | |||||
for (const int64_t nIndex : setExternalKeyPool) { | |||||
batch.ErasePool(nIndex); | |||||
} | |||||
setExternalKeyPool.clear(); | |||||
for (int64_t nIndex : set_pre_split_keypool) { | |||||
batch.ErasePool(nIndex); | |||||
} | |||||
set_pre_split_keypool.clear(); | |||||
m_pool_key_to_index.clear(); | |||||
if (!TopUpKeyPool()) { | |||||
return false; | |||||
} | |||||
WalletLogPrintf("CWallet::NewKeyPool rewrote keypool\n"); | |||||
return true; | |||||
} | |||||
size_t CWallet::KeypoolCountExternalKeys() { | |||||
AssertLockHeld(cs_wallet); | |||||
return setExternalKeyPool.size() + set_pre_split_keypool.size(); | |||||
} | |||||
void CWallet::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) { | |||||
AssertLockHeld(cs_wallet); | |||||
if (keypool.m_pre_split) { | |||||
set_pre_split_keypool.insert(nIndex); | |||||
} else if (keypool.fInternal) { | |||||
setInternalKeyPool.insert(nIndex); | |||||
} else { | |||||
setExternalKeyPool.insert(nIndex); | |||||
} | |||||
m_max_keypool_index = std::max(m_max_keypool_index, nIndex); | |||||
m_pool_key_to_index[keypool.vchPubKey.GetID()] = nIndex; | |||||
// If no metadata exists yet, create a default with the pool key's | |||||
// creation time. Note that this may be overwritten by actually | |||||
// stored metadata for that key later, which is fine. | |||||
CKeyID keyid = keypool.vchPubKey.GetID(); | |||||
if (mapKeyMetadata.count(keyid) == 0) { | |||||
mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime); | |||||
} | |||||
} | |||||
bool CWallet::TopUpKeyPool(unsigned int kpSize) { | |||||
if (!CanGenerateKeys()) { | |||||
return false; | |||||
} | |||||
{ | |||||
LOCK(cs_wallet); | |||||
if (IsLocked()) { | |||||
return false; | |||||
} | |||||
// Top up key pool | |||||
unsigned int nTargetSize; | |||||
if (kpSize > 0) { | |||||
nTargetSize = kpSize; | |||||
} else { | |||||
nTargetSize = std::max<int64_t>( | |||||
gArgs.GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), 0); | |||||
} | |||||
// count amount of available keys (internal, external) | |||||
// make sure the keypool of external and internal keys fits the user | |||||
// selected target (-keypool) | |||||
int64_t missingExternal = std::max<int64_t>( | |||||
std::max<int64_t>(nTargetSize, 1) - setExternalKeyPool.size(), 0); | |||||
int64_t missingInternal = std::max<int64_t>( | |||||
std::max<int64_t>(nTargetSize, 1) - setInternalKeyPool.size(), 0); | |||||
if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT)) { | |||||
// don't create extra internal keys | |||||
missingInternal = 0; | |||||
} | |||||
bool internal = false; | |||||
WalletBatch batch(*database); | |||||
for (int64_t i = missingInternal + missingExternal; i--;) { | |||||
if (i < missingInternal) { | |||||
internal = true; | |||||
} | |||||
CPubKey pubkey(GenerateNewKey(batch, internal)); | |||||
AddKeypoolPubkeyWithDB(pubkey, internal, batch); | |||||
} | |||||
if (missingInternal + missingExternal > 0) { | |||||
WalletLogPrintf( | |||||
"keypool added %d keys (%d internal), size=%u (%u internal)\n", | |||||
missingInternal + missingExternal, missingInternal, | |||||
setInternalKeyPool.size() + setExternalKeyPool.size() + | |||||
set_pre_split_keypool.size(), | |||||
setInternalKeyPool.size()); | |||||
} | |||||
} | |||||
NotifyCanGetAddressesChanged(); | |||||
return true; | |||||
} | |||||
void CWallet::AddKeypoolPubkeyWithDB(const CPubKey &pubkey, const bool internal, | |||||
WalletBatch &batch) { | |||||
LOCK(cs_wallet); | |||||
// How in the hell did you use so many keys? | |||||
assert(m_max_keypool_index < std::numeric_limits<int64_t>::max()); | |||||
int64_t index = ++m_max_keypool_index; | |||||
if (!batch.WritePool(index, CKeyPool(pubkey, internal))) { | |||||
throw std::runtime_error(std::string(__func__) + | |||||
": writing imported pubkey failed"); | |||||
} | |||||
if (internal) { | |||||
setInternalKeyPool.insert(index); | |||||
} else { | |||||
setExternalKeyPool.insert(index); | |||||
} | |||||
m_pool_key_to_index[pubkey.GetID()] = index; | |||||
} | |||||
bool CWallet::ReserveKeyFromKeyPool(int64_t &nIndex, CKeyPool &keypool, | |||||
bool fRequestedInternal) { | |||||
nIndex = -1; | |||||
keypool.vchPubKey = CPubKey(); | |||||
{ | |||||
LOCK(cs_wallet); | |||||
TopUpKeyPool(); | |||||
bool fReturningInternal = fRequestedInternal; | |||||
fReturningInternal &= | |||||
(IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) || | |||||
IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); | |||||
bool use_split_keypool = set_pre_split_keypool.empty(); | |||||
std::set<int64_t> &setKeyPool = | |||||
use_split_keypool | |||||
? (fReturningInternal ? setInternalKeyPool : setExternalKeyPool) | |||||
: set_pre_split_keypool; | |||||
// Get the oldest key | |||||
if (setKeyPool.empty()) { | |||||
return false; | |||||
} | |||||
WalletBatch batch(*database); | |||||
auto it = setKeyPool.begin(); | |||||
nIndex = *it; | |||||
setKeyPool.erase(it); | |||||
if (!batch.ReadPool(nIndex, keypool)) { | |||||
throw std::runtime_error(std::string(__func__) + ": read failed"); | |||||
} | |||||
CPubKey pk; | |||||
if (!GetPubKey(keypool.vchPubKey.GetID(), pk)) { | |||||
throw std::runtime_error(std::string(__func__) + | |||||
": unknown key in key pool"); | |||||
} | |||||
// If the key was pre-split keypool, we don't care about what type it is | |||||
if (use_split_keypool && keypool.fInternal != fReturningInternal) { | |||||
throw std::runtime_error(std::string(__func__) + | |||||
": keypool entry misclassified"); | |||||
} | |||||
if (!keypool.vchPubKey.IsValid()) { | |||||
throw std::runtime_error(std::string(__func__) + | |||||
": keypool entry invalid"); | |||||
} | |||||
m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); | |||||
WalletLogPrintf("keypool reserve %d\n", nIndex); | |||||
} | |||||
NotifyCanGetAddressesChanged(); | |||||
return true; | |||||
} | |||||
void CWallet::KeepKey(int64_t nIndex) { | |||||
// Remove from key pool. | |||||
WalletBatch batch(*database); | |||||
batch.ErasePool(nIndex); | |||||
WalletLogPrintf("keypool keep %d\n", nIndex); | |||||
} | |||||
void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey &pubkey) { | |||||
// Return to key pool | |||||
{ | |||||
LOCK(cs_wallet); | |||||
if (fInternal) { | |||||
setInternalKeyPool.insert(nIndex); | |||||
} else if (!set_pre_split_keypool.empty()) { | |||||
set_pre_split_keypool.insert(nIndex); | |||||
} else { | |||||
setExternalKeyPool.insert(nIndex); | |||||
} | |||||
m_pool_key_to_index[pubkey.GetID()] = nIndex; | |||||
NotifyCanGetAddressesChanged(); | |||||
} | |||||
WalletLogPrintf("keypool return %d\n", nIndex); | |||||
} | |||||
bool CWallet::GetKeyFromPool(CPubKey &result, bool internal) { | |||||
if (!CanGetAddresses(internal)) { | |||||
return false; | |||||
} | |||||
CKeyPool keypool; | |||||
LOCK(cs_wallet); | |||||
int64_t nIndex; | |||||
if (!ReserveKeyFromKeyPool(nIndex, keypool, internal) && | |||||
!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { | |||||
if (IsLocked()) { | |||||
return false; | |||||
} | |||||
WalletBatch batch(*database); | |||||
result = GenerateNewKey(batch, internal); | |||||
return true; | |||||
} | |||||
KeepKey(nIndex); | |||||
result = keypool.vchPubKey; | |||||
return true; | |||||
} | |||||
bool CWallet::GetNewDestination(const OutputType type, const std::string label, | |||||
CTxDestination &dest, std::string &error) { | |||||
LOCK(cs_wallet); | |||||
error.clear(); | |||||
TopUpKeyPool(); | |||||
// Generate a new key that is added to wallet | |||||
CPubKey new_key; | |||||
if (!GetKeyFromPool(new_key)) { | |||||
error = "Error: Keypool ran out, please call keypoolrefill first"; | |||||
return false; | |||||
} | |||||
LearnRelatedScripts(new_key, type); | |||||
dest = GetDestinationForKey(new_key, type); | |||||
SetAddressBook(dest, label, "receive"); | |||||
return true; | |||||
} | |||||
bool CWallet::GetNewChangeDestination(const OutputType type, | bool CWallet::GetNewChangeDestination(const OutputType type, | ||||
CTxDestination &dest, | CTxDestination &dest, | ||||
std::string &error) { | std::string &error) { | ||||
error.clear(); | error.clear(); | ||||
TopUpKeyPool(); | TopUpKeyPool(); | ||||
ReserveDestination reservedest(this); | ReserveDestination reservedest(this); | ||||
if (!reservedest.GetReservedDestination(type, dest, true)) { | if (!reservedest.GetReservedDestination(type, dest, true)) { | ||||
error = "Error: Keypool ran out, please call keypoolrefill first"; | error = "Error: Keypool ran out, please call keypoolrefill first"; | ||||
return false; | return false; | ||||
} | } | ||||
reservedest.KeepDestination(); | reservedest.KeepDestination(); | ||||
return true; | return true; | ||||
} | } | ||||
static int64_t GetOldestKeyTimeInPool(const std::set<int64_t> &setKeyPool, | |||||
WalletBatch &batch) { | |||||
if (setKeyPool.empty()) { | |||||
return GetTime(); | |||||
} | |||||
CKeyPool keypool; | |||||
int64_t nIndex = *(setKeyPool.begin()); | |||||
if (!batch.ReadPool(nIndex, keypool)) { | |||||
throw std::runtime_error(std::string(__func__) + | |||||
": read oldest key in keypool failed"); | |||||
} | |||||
assert(keypool.vchPubKey.IsValid()); | |||||
return keypool.nTime; | |||||
} | |||||
int64_t CWallet::GetOldestKeyPoolTime() { | |||||
LOCK(cs_wallet); | |||||
WalletBatch batch(*database); | |||||
// load oldest key from keypool, get time and return | |||||
int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, batch); | |||||
if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) { | |||||
oldestKey = std::max(GetOldestKeyTimeInPool(setInternalKeyPool, batch), | |||||
oldestKey); | |||||
if (!set_pre_split_keypool.empty()) { | |||||
oldestKey = | |||||
std::max(GetOldestKeyTimeInPool(set_pre_split_keypool, batch), | |||||
oldestKey); | |||||
} | |||||
} | |||||
return oldestKey; | |||||
} | |||||
std::map<CTxDestination, Amount> | std::map<CTxDestination, Amount> | ||||
CWallet::GetAddressBalances(interfaces::Chain::Lock &locked_chain) { | CWallet::GetAddressBalances(interfaces::Chain::Lock &locked_chain) { | ||||
std::map<CTxDestination, Amount> balances; | std::map<CTxDestination, Amount> balances; | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
for (const auto &walletEntry : mapWallet) { | for (const auto &walletEntry : mapWallet) { | ||||
const CWalletTx &wtx = walletEntry.second; | const CWalletTx &wtx = walletEntry.second; | ||||
▲ Show 20 Lines • Show All 192 Lines • ▼ Show 20 Lines | void ReserveDestination::ReturnDestination() { | ||||
if (nIndex != -1) { | if (nIndex != -1) { | ||||
pwallet->ReturnKey(nIndex, fInternal, vchPubKey); | pwallet->ReturnKey(nIndex, fInternal, vchPubKey); | ||||
} | } | ||||
nIndex = -1; | nIndex = -1; | ||||
vchPubKey = CPubKey(); | vchPubKey = CPubKey(); | ||||
address = CNoDestination(); | address = CNoDestination(); | ||||
} | } | ||||
void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id) { | |||||
AssertLockHeld(cs_wallet); | |||||
bool internal = setInternalKeyPool.count(keypool_id); | |||||
if (!internal) { | |||||
assert(setExternalKeyPool.count(keypool_id) || | |||||
set_pre_split_keypool.count(keypool_id)); | |||||
} | |||||
std::set<int64_t> *setKeyPool = | |||||
internal ? &setInternalKeyPool | |||||
: (set_pre_split_keypool.empty() ? &setExternalKeyPool | |||||
: &set_pre_split_keypool); | |||||
auto it = setKeyPool->begin(); | |||||
WalletBatch batch(*database); | |||||
while (it != std::end(*setKeyPool)) { | |||||
const int64_t &index = *(it); | |||||
if (index > keypool_id) { | |||||
// set*KeyPool is ordered | |||||
break; | |||||
} | |||||
CKeyPool keypool; | |||||
if (batch.ReadPool(index, keypool)) { | |||||
// TODO: This should be unnecessary | |||||
m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); | |||||
} | |||||
LearnAllRelatedScripts(keypool.vchPubKey); | |||||
batch.ErasePool(index); | |||||
WalletLogPrintf("keypool index %d removed\n", index); | |||||
it = setKeyPool->erase(it); | |||||
} | |||||
} | |||||
void CWallet::LockCoin(const COutPoint &output) { | void CWallet::LockCoin(const COutPoint &output) { | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
setLockedCoins.insert(output); | setLockedCoins.insert(output); | ||||
} | } | ||||
void CWallet::UnlockCoin(const COutPoint &output) { | void CWallet::UnlockCoin(const COutPoint &output) { | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
setLockedCoins.erase(output); | setLockedCoins.erase(output); | ||||
▲ Show 20 Lines • Show All 264 Lines • ▼ Show 20 Lines | if (salvage_wallet) { | ||||
WalletBatch::RecoverKeysOnlyFilter, backup_filename)) { | WalletBatch::RecoverKeysOnlyFilter, backup_filename)) { | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
return WalletBatch::VerifyDatabaseFile(wallet_path, warnings, error_string); | return WalletBatch::VerifyDatabaseFile(wallet_path, warnings, error_string); | ||||
} | } | ||||
void CWallet::MarkPreSplitKeys() { | |||||
WalletBatch batch(*database); | |||||
for (auto it = setExternalKeyPool.begin(); | |||||
it != setExternalKeyPool.end();) { | |||||
int64_t index = *it; | |||||
CKeyPool keypool; | |||||
if (!batch.ReadPool(index, keypool)) { | |||||
throw std::runtime_error(std::string(__func__) + | |||||
": read keypool entry failed"); | |||||
} | |||||
keypool.m_pre_split = true; | |||||
if (!batch.WritePool(index, keypool)) { | |||||
throw std::runtime_error(std::string(__func__) + | |||||
": writing modified keypool entry failed"); | |||||
} | |||||
set_pre_split_keypool.insert(index); | |||||
it = setExternalKeyPool.erase(it); | |||||
} | |||||
} | |||||
std::shared_ptr<CWallet> CWallet::CreateWalletFromFile( | std::shared_ptr<CWallet> CWallet::CreateWalletFromFile( | ||||
const CChainParams &chainParams, interfaces::Chain &chain, | const CChainParams &chainParams, interfaces::Chain &chain, | ||||
const WalletLocation &location, std::string &error, | const WalletLocation &location, std::string &error, | ||||
std::vector<std::string> &warnings, uint64_t wallet_creation_flags) { | std::vector<std::string> &warnings, uint64_t wallet_creation_flags) { | ||||
const std::string walletFile = | const std::string walletFile = | ||||
WalletDataFilePath(location.GetPath()).string(); | WalletDataFilePath(location.GetPath()).string(); | ||||
// Needed to restore wallet transaction meta data after -zapwallettxes | // Needed to restore wallet transaction meta data after -zapwallettxes | ||||
▲ Show 20 Lines • Show All 463 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
bool CWalletTx::IsImmatureCoinBase( | bool CWalletTx::IsImmatureCoinBase( | ||||
interfaces::Chain::Lock &locked_chain) const { | interfaces::Chain::Lock &locked_chain) const { | ||||
// note GetBlocksToMaturity is 0 for non-coinbase tx | // note GetBlocksToMaturity is 0 for non-coinbase tx | ||||
return GetBlocksToMaturity(locked_chain) > 0; | return GetBlocksToMaturity(locked_chain) > 0; | ||||
} | } | ||||
void CWallet::LearnRelatedScripts(const CPubKey &key, OutputType type) { | |||||
// Nothing to do... | |||||
} | |||||
void CWallet::LearnAllRelatedScripts(const CPubKey &key) { | |||||
// Nothing to do... | |||||
} | |||||
std::vector<OutputGroup> | std::vector<OutputGroup> | ||||
CWallet::GroupOutputs(const std::vector<COutput> &outputs, | CWallet::GroupOutputs(const std::vector<COutput> &outputs, | ||||
bool single_coin) const { | bool single_coin) const { | ||||
std::vector<OutputGroup> groups; | std::vector<OutputGroup> groups; | ||||
std::map<CTxDestination, OutputGroup> gmap; | std::map<CTxDestination, OutputGroup> gmap; | ||||
CTxDestination dst; | CTxDestination dst; | ||||
for (const auto &output : outputs) { | for (const auto &output : outputs) { | ||||
if (output.fSpendable) { | if (output.fSpendable) { | ||||
Show All 25 Lines | CWallet::GroupOutputs(const std::vector<COutput> &outputs, | ||||
if (!single_coin) { | if (!single_coin) { | ||||
for (const auto &it : gmap) { | for (const auto &it : gmap) { | ||||
groups.push_back(it.second); | groups.push_back(it.second); | ||||
} | } | ||||
} | } | ||||
return groups; | return groups; | ||||
} | } | ||||
bool CWallet::GetKeyOrigin(const CKeyID &keyID, KeyOriginInfo &info) const { | |||||
CKeyMetadata meta; | |||||
{ | |||||
LOCK(cs_wallet); | |||||
auto it = mapKeyMetadata.find(keyID); | |||||
if (it != mapKeyMetadata.end()) { | |||||
meta = it->second; | |||||
} | |||||
} | |||||
if (meta.has_key_origin) { | |||||
std::copy(meta.key_origin.fingerprint, meta.key_origin.fingerprint + 4, | |||||
info.fingerprint); | |||||
info.path = meta.key_origin.path; | |||||
} else { | |||||
// Single pubkeys get the master fingerprint of themselves | |||||
std::copy(keyID.begin(), keyID.begin() + 4, info.fingerprint); | |||||
} | |||||
return true; | |||||
} | |||||
bool CWallet::AddKeyOriginWithDB(WalletBatch &batch, const CPubKey &pubkey, | |||||
const KeyOriginInfo &info) { | |||||
LOCK(cs_wallet); | |||||
std::copy(info.fingerprint, info.fingerprint + 4, | |||||
mapKeyMetadata[pubkey.GetID()].key_origin.fingerprint); | |||||
mapKeyMetadata[pubkey.GetID()].key_origin.path = info.path; | |||||
mapKeyMetadata[pubkey.GetID()].has_key_origin = true; | |||||
mapKeyMetadata[pubkey.GetID()].hdKeypath = WriteHDKeypath(info.path); | |||||
return batch.WriteKeyMetadata(mapKeyMetadata[pubkey.GetID()], pubkey, true); | |||||
} | |||||
bool CWallet::SetCrypted() { | bool CWallet::SetCrypted() { | ||||
LOCK(cs_KeyStore); | LOCK(cs_KeyStore); | ||||
if (fUseCrypto) { | if (fUseCrypto) { | ||||
return true; | return true; | ||||
} | } | ||||
if (!mapKeys.empty()) { | if (!mapKeys.empty()) { | ||||
return false; | return false; | ||||
} | } | ||||
Show All 17 Lines | bool CWallet::Lock() { | ||||
{ | { | ||||
LOCK(cs_KeyStore); | LOCK(cs_KeyStore); | ||||
vMasterKey.clear(); | vMasterKey.clear(); | ||||
} | } | ||||
NotifyStatusChanged(this); | NotifyStatusChanged(this); | ||||
return true; | return true; | ||||
} | } | ||||
bool CWallet::Unlock(const CKeyingMaterial &vMasterKeyIn, bool accept_no_keys) { | |||||
{ | |||||
LOCK(cs_KeyStore); | |||||
if (!SetCrypted()) { | |||||
return false; | |||||
} | |||||
// Always pass when there are no encrypted keys | |||||
bool keyPass = mapCryptedKeys.empty(); | |||||
bool keyFail = false; | |||||
CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); | |||||
for (; mi != mapCryptedKeys.end(); ++mi) { | |||||
const CPubKey &vchPubKey = (*mi).second.first; | |||||
const std::vector<uint8_t> &vchCryptedSecret = (*mi).second.second; | |||||
CKey key; | |||||
if (!DecryptKey(vMasterKeyIn, vchCryptedSecret, vchPubKey, key)) { | |||||
keyFail = true; | |||||
break; | |||||
} | |||||
keyPass = true; | |||||
if (fDecryptionThoroughlyChecked) { | |||||
break; | |||||
} | |||||
} | |||||
if (keyPass && keyFail) { | |||||
LogPrintf("The wallet is probably corrupted: Some keys decrypt but " | |||||
"not all.\n"); | |||||
assert(false); | |||||
} | |||||
if (keyFail || (!keyPass && !accept_no_keys)) { | |||||
return false; | |||||
} | |||||
vMasterKey = vMasterKeyIn; | |||||
fDecryptionThoroughlyChecked = true; | |||||
} | |||||
NotifyStatusChanged(this); | |||||
return true; | |||||
} | |||||
bool CWallet::HaveKey(const CKeyID &address) const { | |||||
LOCK(cs_KeyStore); | |||||
if (!IsCrypted()) { | |||||
return FillableSigningProvider::HaveKey(address); | |||||
} | |||||
return mapCryptedKeys.count(address) > 0; | |||||
} | |||||
bool CWallet::GetKey(const CKeyID &address, CKey &keyOut) const { | |||||
LOCK(cs_KeyStore); | |||||
if (!IsCrypted()) { | |||||
return FillableSigningProvider::GetKey(address, keyOut); | |||||
} | |||||
CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); | |||||
if (mi != mapCryptedKeys.end()) { | |||||
const CPubKey &vchPubKey = (*mi).second.first; | |||||
const std::vector<uint8_t> &vchCryptedSecret = (*mi).second.second; | |||||
return DecryptKey(vMasterKey, vchCryptedSecret, vchPubKey, keyOut); | |||||
} | |||||
return false; | |||||
} | |||||
bool CWallet::GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const { | |||||
LOCK(cs_KeyStore); | |||||
WatchKeyMap::const_iterator it = mapWatchKeys.find(address); | |||||
if (it != mapWatchKeys.end()) { | |||||
pubkey_out = it->second; | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
bool CWallet::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const { | |||||
LOCK(cs_KeyStore); | |||||
if (!IsCrypted()) { | |||||
if (!FillableSigningProvider::GetPubKey(address, vchPubKeyOut)) { | |||||
return GetWatchPubKey(address, vchPubKeyOut); | |||||
} | |||||
return true; | |||||
} | |||||
CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); | |||||
if (mi != mapCryptedKeys.end()) { | |||||
vchPubKeyOut = (*mi).second.first; | |||||
return true; | |||||
} | |||||
// Check for watch-only pubkeys | |||||
return GetWatchPubKey(address, vchPubKeyOut); | |||||
} | |||||
std::set<CKeyID> CWallet::GetKeys() const { | |||||
LOCK(cs_KeyStore); | |||||
if (!IsCrypted()) { | |||||
return FillableSigningProvider::GetKeys(); | |||||
} | |||||
std::set<CKeyID> set_address; | |||||
for (const auto &mi : mapCryptedKeys) { | |||||
set_address.insert(mi.first); | |||||
} | |||||
return set_address; | |||||
} | |||||
bool CWallet::EncryptKeys(CKeyingMaterial &vMasterKeyIn) { | |||||
LOCK(cs_KeyStore); | |||||
if (!mapCryptedKeys.empty() || IsCrypted()) { | |||||
return false; | |||||
} | |||||
fUseCrypto = true; | |||||
for (const KeyMap::value_type &mKey : mapKeys) { | |||||
const CKey &key = mKey.second; | |||||
CPubKey vchPubKey = key.GetPubKey(); | |||||
CKeyingMaterial vchSecret(key.begin(), key.end()); | |||||
std::vector<uint8_t> vchCryptedSecret; | |||||
if (!EncryptSecret(vMasterKeyIn, vchSecret, vchPubKey.GetHash(), | |||||
vchCryptedSecret)) { | |||||
return false; | |||||
} | |||||
if (!AddCryptedKey(vchPubKey, vchCryptedSecret)) { | |||||
return false; | |||||
} | |||||
} | |||||
mapKeys.clear(); | |||||
return true; | |||||
} | |||||
bool CWallet::AddKeyPubKeyInner(const CKey &key, const CPubKey &pubkey) { | |||||
LOCK(cs_KeyStore); | |||||
if (!IsCrypted()) { | |||||
return FillableSigningProvider::AddKeyPubKey(key, pubkey); | |||||
} | |||||
if (IsLocked()) { | |||||
return false; | |||||
} | |||||
std::vector<uint8_t> vchCryptedSecret; | |||||
CKeyingMaterial vchSecret(key.begin(), key.end()); | |||||
if (!EncryptSecret(vMasterKey, vchSecret, pubkey.GetHash(), | |||||
vchCryptedSecret)) { | |||||
return false; | |||||
} | |||||
if (!AddCryptedKey(pubkey, vchCryptedSecret)) { | |||||
return false; | |||||
} | |||||
return true; | |||||
} | |||||
bool CWallet::AddCryptedKeyInner(const CPubKey &vchPubKey, | |||||
const std::vector<uint8_t> &vchCryptedSecret) { | |||||
LOCK(cs_KeyStore); | |||||
if (!SetCrypted()) { | |||||
return false; | |||||
} | |||||
mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret); | |||||
return true; | |||||
} |