mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-26 11:13:23 -03:00
Merge bitcoin/bitcoin#26596: wallet: Migrate legacy wallets to descriptor wallets without requiring BDB
8ce3739edb
test: verify wallet is still active post-migration failure (furszy)771bc60f13
wallet: Use LegacyDataSPKM when loading (Ava Chow)61d872f1b3
wallet: Move MigrateToDescriptor and DeleteRecords to LegacyDataSPKM (Ava Chow)b231f4d556
wallet: Move LegacyScriptPubKeyMan::IsMine to LegacyDataSPKM (Ava Chow)7461d0c006
wallet: Move LegacySPKM data storage and handling to LegacyDataSPKM (Ava Chow)517e204bac
Change MigrateLegacyToDescriptor to reopen wallet as BERKELEY_RO (Ava Chow) Pull request description: #26606 introduced `BerkeleyRODatabase` which is an independent parser for BDB files. This PR uses this in legacy wallet migration so that migration will continue to work once the legacy wallet and BDB are removed. `LegacyDataSPKM` is introduced to have the minimum data and functions necessary for a legacy wallet to be loaded for migration. ACKs for top commit: cbergqvist: ACK8ce3739edb
theStack: Code-review ACK8ce3739edb
furszy: Code review ACK8ce3739edb
Tree-SHA512: dccea12d6c597de15e3e42f97ab483cfd069e103611200279a177e021e8e9c4e74387c4f45d2e58b3a1e7e2bdb32a1d2d2060b1f8086c03eeaa0c68579d9d54e
This commit is contained in:
commit
d9aa7b23e4
6 changed files with 221 additions and 125 deletions
|
@ -84,7 +84,7 @@ bool PermitsUncompressed(IsMineSigVersion sigversion)
|
||||||
return sigversion == IsMineSigVersion::TOP || sigversion == IsMineSigVersion::P2SH;
|
return sigversion == IsMineSigVersion::TOP || sigversion == IsMineSigVersion::P2SH;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HaveKeys(const std::vector<valtype>& pubkeys, const LegacyScriptPubKeyMan& keystore)
|
bool HaveKeys(const std::vector<valtype>& pubkeys, const LegacyDataSPKM& keystore)
|
||||||
{
|
{
|
||||||
for (const valtype& pubkey : pubkeys) {
|
for (const valtype& pubkey : pubkeys) {
|
||||||
CKeyID keyID = CPubKey(pubkey).GetID();
|
CKeyID keyID = CPubKey(pubkey).GetID();
|
||||||
|
@ -102,7 +102,7 @@ bool HaveKeys(const std::vector<valtype>& pubkeys, const LegacyScriptPubKeyMan&
|
||||||
//! scripts or simply treat any script that has been
|
//! scripts or simply treat any script that has been
|
||||||
//! stored in the keystore as spendable
|
//! stored in the keystore as spendable
|
||||||
// NOLINTNEXTLINE(misc-no-recursion)
|
// NOLINTNEXTLINE(misc-no-recursion)
|
||||||
IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& scriptPubKey, IsMineSigVersion sigversion, bool recurse_scripthash=true)
|
IsMineResult IsMineInner(const LegacyDataSPKM& keystore, const CScript& scriptPubKey, IsMineSigVersion sigversion, bool recurse_scripthash=true)
|
||||||
{
|
{
|
||||||
IsMineResult ret = IsMineResult::NO;
|
IsMineResult ret = IsMineResult::NO;
|
||||||
|
|
||||||
|
@ -217,7 +217,7 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
isminetype LegacyScriptPubKeyMan::IsMine(const CScript& script) const
|
isminetype LegacyDataSPKM::IsMine(const CScript& script) const
|
||||||
{
|
{
|
||||||
switch (IsMineInner(*this, script, IsMineSigVersion::TOP)) {
|
switch (IsMineInner(*this, script, IsMineSigVersion::TOP)) {
|
||||||
case IsMineResult::INVALID:
|
case IsMineResult::INVALID:
|
||||||
|
@ -231,7 +231,7 @@ isminetype LegacyScriptPubKeyMan::IsMine(const CScript& script) const
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LegacyScriptPubKeyMan::CheckDecryptionKey(const CKeyingMaterial& master_key)
|
bool LegacyDataSPKM::CheckDecryptionKey(const CKeyingMaterial& master_key)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
LOCK(cs_KeyStore);
|
LOCK(cs_KeyStore);
|
||||||
|
@ -585,7 +585,7 @@ int64_t LegacyScriptPubKeyMan::GetTimeFirstKey() const
|
||||||
return nTimeFirstKey;
|
return nTimeFirstKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<SigningProvider> LegacyScriptPubKeyMan::GetSolvingProvider(const CScript& script) const
|
std::unique_ptr<SigningProvider> LegacyDataSPKM::GetSolvingProvider(const CScript& script) const
|
||||||
{
|
{
|
||||||
return std::make_unique<LegacySigningProvider>(*this);
|
return std::make_unique<LegacySigningProvider>(*this);
|
||||||
}
|
}
|
||||||
|
@ -721,7 +721,7 @@ void LegacyScriptPubKeyMan::UpdateTimeFirstKey(int64_t nCreateTime)
|
||||||
NotifyFirstKeyTimeChanged(this, nTimeFirstKey);
|
NotifyFirstKeyTimeChanged(this, nTimeFirstKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LegacyScriptPubKeyMan::LoadKey(const CKey& key, const CPubKey &pubkey)
|
bool LegacyDataSPKM::LoadKey(const CKey& key, const CPubKey &pubkey)
|
||||||
{
|
{
|
||||||
return AddKeyPubKeyInner(key, pubkey);
|
return AddKeyPubKeyInner(key, pubkey);
|
||||||
}
|
}
|
||||||
|
@ -773,7 +773,7 @@ bool LegacyScriptPubKeyMan::AddKeyPubKeyWithDB(WalletBatch& batch, const CKey& s
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LegacyScriptPubKeyMan::LoadCScript(const CScript& redeemScript)
|
bool LegacyDataSPKM::LoadCScript(const CScript& redeemScript)
|
||||||
{
|
{
|
||||||
/* A sanity check was added in pull #3843 to avoid adding redeemScripts
|
/* A sanity check was added in pull #3843 to avoid adding redeemScripts
|
||||||
* that never can be redeemed. However, old wallets may still contain
|
* that never can be redeemed. However, old wallets may still contain
|
||||||
|
@ -788,18 +788,36 @@ bool LegacyScriptPubKeyMan::LoadCScript(const CScript& redeemScript)
|
||||||
return FillableSigningProvider::AddCScript(redeemScript);
|
return FillableSigningProvider::AddCScript(redeemScript);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LegacyDataSPKM::LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata& meta)
|
||||||
|
{
|
||||||
|
LOCK(cs_KeyStore);
|
||||||
|
mapKeyMetadata[keyID] = meta;
|
||||||
|
}
|
||||||
|
|
||||||
void LegacyScriptPubKeyMan::LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata& meta)
|
void LegacyScriptPubKeyMan::LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata& meta)
|
||||||
{
|
{
|
||||||
LOCK(cs_KeyStore);
|
LOCK(cs_KeyStore);
|
||||||
|
LegacyDataSPKM::LoadKeyMetadata(keyID, meta);
|
||||||
UpdateTimeFirstKey(meta.nCreateTime);
|
UpdateTimeFirstKey(meta.nCreateTime);
|
||||||
mapKeyMetadata[keyID] = meta;
|
}
|
||||||
|
|
||||||
|
void LegacyDataSPKM::LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata& meta)
|
||||||
|
{
|
||||||
|
LOCK(cs_KeyStore);
|
||||||
|
m_script_metadata[script_id] = meta;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LegacyScriptPubKeyMan::LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata& meta)
|
void LegacyScriptPubKeyMan::LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata& meta)
|
||||||
{
|
{
|
||||||
LOCK(cs_KeyStore);
|
LOCK(cs_KeyStore);
|
||||||
|
LegacyDataSPKM::LoadScriptMetadata(script_id, meta);
|
||||||
UpdateTimeFirstKey(meta.nCreateTime);
|
UpdateTimeFirstKey(meta.nCreateTime);
|
||||||
m_script_metadata[script_id] = meta;
|
}
|
||||||
|
|
||||||
|
bool LegacyDataSPKM::AddKeyPubKeyInner(const CKey& key, const CPubKey& pubkey)
|
||||||
|
{
|
||||||
|
LOCK(cs_KeyStore);
|
||||||
|
return FillableSigningProvider::AddKeyPubKey(key, pubkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LegacyScriptPubKeyMan::AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey)
|
bool LegacyScriptPubKeyMan::AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey)
|
||||||
|
@ -827,7 +845,7 @@ bool LegacyScriptPubKeyMan::AddKeyPubKeyInner(const CKey& key, const CPubKey &pu
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LegacyScriptPubKeyMan::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret, bool checksum_valid)
|
bool LegacyDataSPKM::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret, bool checksum_valid)
|
||||||
{
|
{
|
||||||
// Set fDecryptionThoroughlyChecked to false when the checksum is invalid
|
// Set fDecryptionThoroughlyChecked to false when the checksum is invalid
|
||||||
if (!checksum_valid) {
|
if (!checksum_valid) {
|
||||||
|
@ -837,7 +855,7 @@ bool LegacyScriptPubKeyMan::LoadCryptedKey(const CPubKey &vchPubKey, const std::
|
||||||
return AddCryptedKeyInner(vchPubKey, vchCryptedSecret);
|
return AddCryptedKeyInner(vchPubKey, vchCryptedSecret);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LegacyScriptPubKeyMan::AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
|
bool LegacyDataSPKM::AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
|
||||||
{
|
{
|
||||||
LOCK(cs_KeyStore);
|
LOCK(cs_KeyStore);
|
||||||
assert(mapKeys.empty());
|
assert(mapKeys.empty());
|
||||||
|
@ -865,13 +883,13 @@ bool LegacyScriptPubKeyMan::AddCryptedKey(const CPubKey &vchPubKey,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LegacyScriptPubKeyMan::HaveWatchOnly(const CScript &dest) const
|
bool LegacyDataSPKM::HaveWatchOnly(const CScript &dest) const
|
||||||
{
|
{
|
||||||
LOCK(cs_KeyStore);
|
LOCK(cs_KeyStore);
|
||||||
return setWatchOnly.count(dest) > 0;
|
return setWatchOnly.count(dest) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LegacyScriptPubKeyMan::HaveWatchOnly() const
|
bool LegacyDataSPKM::HaveWatchOnly() const
|
||||||
{
|
{
|
||||||
LOCK(cs_KeyStore);
|
LOCK(cs_KeyStore);
|
||||||
return (!setWatchOnly.empty());
|
return (!setWatchOnly.empty());
|
||||||
|
@ -905,12 +923,12 @@ bool LegacyScriptPubKeyMan::RemoveWatchOnly(const CScript &dest)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LegacyScriptPubKeyMan::LoadWatchOnly(const CScript &dest)
|
bool LegacyDataSPKM::LoadWatchOnly(const CScript &dest)
|
||||||
{
|
{
|
||||||
return AddWatchOnlyInMem(dest);
|
return AddWatchOnlyInMem(dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LegacyScriptPubKeyMan::AddWatchOnlyInMem(const CScript &dest)
|
bool LegacyDataSPKM::AddWatchOnlyInMem(const CScript &dest)
|
||||||
{
|
{
|
||||||
LOCK(cs_KeyStore);
|
LOCK(cs_KeyStore);
|
||||||
setWatchOnly.insert(dest);
|
setWatchOnly.insert(dest);
|
||||||
|
@ -954,7 +972,7 @@ bool LegacyScriptPubKeyMan::AddWatchOnly(const CScript& dest, int64_t nCreateTim
|
||||||
return AddWatchOnly(dest);
|
return AddWatchOnly(dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LegacyScriptPubKeyMan::LoadHDChain(const CHDChain& chain)
|
void LegacyDataSPKM::LoadHDChain(const CHDChain& chain)
|
||||||
{
|
{
|
||||||
LOCK(cs_KeyStore);
|
LOCK(cs_KeyStore);
|
||||||
m_hd_chain = chain;
|
m_hd_chain = chain;
|
||||||
|
@ -975,14 +993,14 @@ void LegacyScriptPubKeyMan::AddHDChain(const CHDChain& chain)
|
||||||
m_hd_chain = chain;
|
m_hd_chain = chain;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LegacyScriptPubKeyMan::AddInactiveHDChain(const CHDChain& chain)
|
void LegacyDataSPKM::AddInactiveHDChain(const CHDChain& chain)
|
||||||
{
|
{
|
||||||
LOCK(cs_KeyStore);
|
LOCK(cs_KeyStore);
|
||||||
assert(!chain.seed_id.IsNull());
|
assert(!chain.seed_id.IsNull());
|
||||||
m_inactive_hd_chains[chain.seed_id] = chain;
|
m_inactive_hd_chains[chain.seed_id] = chain;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LegacyScriptPubKeyMan::HaveKey(const CKeyID &address) const
|
bool LegacyDataSPKM::HaveKey(const CKeyID &address) const
|
||||||
{
|
{
|
||||||
LOCK(cs_KeyStore);
|
LOCK(cs_KeyStore);
|
||||||
if (!m_storage.HasEncryptionKeys()) {
|
if (!m_storage.HasEncryptionKeys()) {
|
||||||
|
@ -991,7 +1009,7 @@ bool LegacyScriptPubKeyMan::HaveKey(const CKeyID &address) const
|
||||||
return mapCryptedKeys.count(address) > 0;
|
return mapCryptedKeys.count(address) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LegacyScriptPubKeyMan::GetKey(const CKeyID &address, CKey& keyOut) const
|
bool LegacyDataSPKM::GetKey(const CKeyID &address, CKey& keyOut) const
|
||||||
{
|
{
|
||||||
LOCK(cs_KeyStore);
|
LOCK(cs_KeyStore);
|
||||||
if (!m_storage.HasEncryptionKeys()) {
|
if (!m_storage.HasEncryptionKeys()) {
|
||||||
|
@ -1010,7 +1028,7 @@ bool LegacyScriptPubKeyMan::GetKey(const CKeyID &address, CKey& keyOut) const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LegacyScriptPubKeyMan::GetKeyOrigin(const CKeyID& keyID, KeyOriginInfo& info) const
|
bool LegacyDataSPKM::GetKeyOrigin(const CKeyID& keyID, KeyOriginInfo& info) const
|
||||||
{
|
{
|
||||||
CKeyMetadata meta;
|
CKeyMetadata meta;
|
||||||
{
|
{
|
||||||
|
@ -1030,7 +1048,7 @@ bool LegacyScriptPubKeyMan::GetKeyOrigin(const CKeyID& keyID, KeyOriginInfo& inf
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LegacyScriptPubKeyMan::GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const
|
bool LegacyDataSPKM::GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const
|
||||||
{
|
{
|
||||||
LOCK(cs_KeyStore);
|
LOCK(cs_KeyStore);
|
||||||
WatchKeyMap::const_iterator it = mapWatchKeys.find(address);
|
WatchKeyMap::const_iterator it = mapWatchKeys.find(address);
|
||||||
|
@ -1041,7 +1059,7 @@ bool LegacyScriptPubKeyMan::GetWatchPubKey(const CKeyID &address, CPubKey &pubke
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LegacyScriptPubKeyMan::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const
|
bool LegacyDataSPKM::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const
|
||||||
{
|
{
|
||||||
LOCK(cs_KeyStore);
|
LOCK(cs_KeyStore);
|
||||||
if (!m_storage.HasEncryptionKeys()) {
|
if (!m_storage.HasEncryptionKeys()) {
|
||||||
|
@ -1160,7 +1178,7 @@ void LegacyScriptPubKeyMan::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata&
|
||||||
throw std::runtime_error(std::string(__func__) + ": writing HD chain model failed");
|
throw std::runtime_error(std::string(__func__) + ": writing HD chain model failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
void LegacyScriptPubKeyMan::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool)
|
void LegacyDataSPKM::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool)
|
||||||
{
|
{
|
||||||
LOCK(cs_KeyStore);
|
LOCK(cs_KeyStore);
|
||||||
if (keypool.m_pre_split) {
|
if (keypool.m_pre_split) {
|
||||||
|
@ -1681,7 +1699,7 @@ std::set<CKeyID> LegacyScriptPubKeyMan::GetKeys() const
|
||||||
return set_address;
|
return set_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unordered_set<CScript, SaltedSipHasher> LegacyScriptPubKeyMan::GetScriptPubKeys() const
|
std::unordered_set<CScript, SaltedSipHasher> LegacyDataSPKM::GetScriptPubKeys() const
|
||||||
{
|
{
|
||||||
LOCK(cs_KeyStore);
|
LOCK(cs_KeyStore);
|
||||||
std::unordered_set<CScript, SaltedSipHasher> spks;
|
std::unordered_set<CScript, SaltedSipHasher> spks;
|
||||||
|
@ -1739,7 +1757,7 @@ std::unordered_set<CScript, SaltedSipHasher> LegacyScriptPubKeyMan::GetScriptPub
|
||||||
return spks;
|
return spks;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unordered_set<CScript, SaltedSipHasher> LegacyScriptPubKeyMan::GetNotMineScriptPubKeys() const
|
std::unordered_set<CScript, SaltedSipHasher> LegacyDataSPKM::GetNotMineScriptPubKeys() const
|
||||||
{
|
{
|
||||||
LOCK(cs_KeyStore);
|
LOCK(cs_KeyStore);
|
||||||
std::unordered_set<CScript, SaltedSipHasher> spks;
|
std::unordered_set<CScript, SaltedSipHasher> spks;
|
||||||
|
@ -1749,7 +1767,7 @@ std::unordered_set<CScript, SaltedSipHasher> LegacyScriptPubKeyMan::GetNotMineSc
|
||||||
return spks;
|
return spks;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<MigrationData> LegacyScriptPubKeyMan::MigrateToDescriptor()
|
std::optional<MigrationData> LegacyDataSPKM::MigrateToDescriptor()
|
||||||
{
|
{
|
||||||
LOCK(cs_KeyStore);
|
LOCK(cs_KeyStore);
|
||||||
if (m_storage.IsLocked()) {
|
if (m_storage.IsLocked()) {
|
||||||
|
@ -1816,7 +1834,7 @@ std::optional<MigrationData> LegacyScriptPubKeyMan::MigrateToDescriptor()
|
||||||
WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0);
|
WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0);
|
||||||
|
|
||||||
// Make the DescriptorScriptPubKeyMan and get the scriptPubKeys
|
// Make the DescriptorScriptPubKeyMan and get the scriptPubKeys
|
||||||
auto desc_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(m_storage, w_desc, m_keypool_size));
|
auto desc_spk_man = std::make_unique<DescriptorScriptPubKeyMan>(m_storage, w_desc, /*keypool_size=*/0);
|
||||||
desc_spk_man->AddDescriptorKey(key, key.GetPubKey());
|
desc_spk_man->AddDescriptorKey(key, key.GetPubKey());
|
||||||
desc_spk_man->TopUp();
|
desc_spk_man->TopUp();
|
||||||
auto desc_spks = desc_spk_man->GetScriptPubKeys();
|
auto desc_spks = desc_spk_man->GetScriptPubKeys();
|
||||||
|
@ -1861,7 +1879,7 @@ std::optional<MigrationData> LegacyScriptPubKeyMan::MigrateToDescriptor()
|
||||||
WalletDescriptor w_desc(std::move(desc), 0, 0, chain_counter, 0);
|
WalletDescriptor w_desc(std::move(desc), 0, 0, chain_counter, 0);
|
||||||
|
|
||||||
// Make the DescriptorScriptPubKeyMan and get the scriptPubKeys
|
// Make the DescriptorScriptPubKeyMan and get the scriptPubKeys
|
||||||
auto desc_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(m_storage, w_desc, m_keypool_size));
|
auto desc_spk_man = std::make_unique<DescriptorScriptPubKeyMan>(m_storage, w_desc, /*keypool_size=*/0);
|
||||||
desc_spk_man->AddDescriptorKey(master_key.key, master_key.key.GetPubKey());
|
desc_spk_man->AddDescriptorKey(master_key.key, master_key.key.GetPubKey());
|
||||||
desc_spk_man->TopUp();
|
desc_spk_man->TopUp();
|
||||||
auto desc_spks = desc_spk_man->GetScriptPubKeys();
|
auto desc_spks = desc_spk_man->GetScriptPubKeys();
|
||||||
|
@ -1923,7 +1941,7 @@ std::optional<MigrationData> LegacyScriptPubKeyMan::MigrateToDescriptor()
|
||||||
} else {
|
} else {
|
||||||
// Make the DescriptorScriptPubKeyMan and get the scriptPubKeys
|
// Make the DescriptorScriptPubKeyMan and get the scriptPubKeys
|
||||||
WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0);
|
WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0);
|
||||||
auto desc_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(m_storage, w_desc, m_keypool_size));
|
auto desc_spk_man = std::make_unique<DescriptorScriptPubKeyMan>(m_storage, w_desc, /*keypool_size=*/0);
|
||||||
for (const auto& keyid : privkeyids) {
|
for (const auto& keyid : privkeyids) {
|
||||||
CKey key;
|
CKey key;
|
||||||
if (!GetKey(keyid, key)) {
|
if (!GetKey(keyid, key)) {
|
||||||
|
@ -2001,7 +2019,7 @@ std::optional<MigrationData> LegacyScriptPubKeyMan::MigrateToDescriptor()
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LegacyScriptPubKeyMan::DeleteRecords()
|
bool LegacyDataSPKM::DeleteRecords()
|
||||||
{
|
{
|
||||||
LOCK(cs_KeyStore);
|
LOCK(cs_KeyStore);
|
||||||
WalletBatch batch(m_storage.GetDatabase());
|
WalletBatch batch(m_storage.GetDatabase());
|
||||||
|
|
|
@ -278,31 +278,111 @@ static const std::unordered_set<OutputType> LEGACY_OUTPUT_TYPES {
|
||||||
|
|
||||||
class DescriptorScriptPubKeyMan;
|
class DescriptorScriptPubKeyMan;
|
||||||
|
|
||||||
class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProvider
|
// Manages the data for a LegacyScriptPubKeyMan.
|
||||||
|
// This is the minimum necessary to load a legacy wallet so that it can be migrated.
|
||||||
|
class LegacyDataSPKM : public ScriptPubKeyMan, public FillableSigningProvider
|
||||||
{
|
{
|
||||||
private:
|
protected:
|
||||||
//! keeps track of whether Unlock has run a thorough check before
|
|
||||||
bool fDecryptionThoroughlyChecked = true;
|
|
||||||
|
|
||||||
using WatchOnlySet = std::set<CScript>;
|
using WatchOnlySet = std::set<CScript>;
|
||||||
using WatchKeyMap = std::map<CKeyID, CPubKey>;
|
using WatchKeyMap = std::map<CKeyID, CPubKey>;
|
||||||
|
|
||||||
WalletBatch *encrypted_batch GUARDED_BY(cs_KeyStore) = nullptr;
|
|
||||||
|
|
||||||
using CryptedKeyMap = std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>>;
|
using CryptedKeyMap = std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>>;
|
||||||
|
|
||||||
CryptedKeyMap mapCryptedKeys GUARDED_BY(cs_KeyStore);
|
CryptedKeyMap mapCryptedKeys GUARDED_BY(cs_KeyStore);
|
||||||
WatchOnlySet setWatchOnly GUARDED_BY(cs_KeyStore);
|
WatchOnlySet setWatchOnly GUARDED_BY(cs_KeyStore);
|
||||||
WatchKeyMap mapWatchKeys GUARDED_BY(cs_KeyStore);
|
WatchKeyMap mapWatchKeys GUARDED_BY(cs_KeyStore);
|
||||||
|
|
||||||
|
/* the HD chain data model (external chain counters) */
|
||||||
|
CHDChain m_hd_chain;
|
||||||
|
std::unordered_map<CKeyID, CHDChain, SaltedSipHasher> m_inactive_hd_chains;
|
||||||
|
|
||||||
|
//! keeps track of whether Unlock has run a thorough check before
|
||||||
|
bool fDecryptionThoroughlyChecked = true;
|
||||||
|
|
||||||
|
bool AddWatchOnlyInMem(const CScript &dest);
|
||||||
|
virtual bool AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey);
|
||||||
|
bool AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
|
||||||
|
|
||||||
|
public:
|
||||||
|
using ScriptPubKeyMan::ScriptPubKeyMan;
|
||||||
|
|
||||||
|
// Map from Key ID to key metadata.
|
||||||
|
std::map<CKeyID, CKeyMetadata> mapKeyMetadata GUARDED_BY(cs_KeyStore);
|
||||||
|
|
||||||
|
// Map from Script ID to key metadata (for watch-only keys).
|
||||||
|
std::map<CScriptID, CKeyMetadata> m_script_metadata GUARDED_BY(cs_KeyStore);
|
||||||
|
|
||||||
|
// ScriptPubKeyMan overrides
|
||||||
|
bool CheckDecryptionKey(const CKeyingMaterial& master_key) override;
|
||||||
|
std::unordered_set<CScript, SaltedSipHasher> GetScriptPubKeys() const override;
|
||||||
|
std::unique_ptr<SigningProvider> GetSolvingProvider(const CScript& script) const override;
|
||||||
|
uint256 GetID() const override { return uint256::ONE; }
|
||||||
|
// TODO: Remove IsMine when deleting LegacyScriptPubKeyMan
|
||||||
|
isminetype IsMine(const CScript& script) const override;
|
||||||
|
|
||||||
|
// FillableSigningProvider overrides
|
||||||
|
bool HaveKey(const CKeyID &address) const override;
|
||||||
|
bool GetKey(const CKeyID &address, CKey& keyOut) const override;
|
||||||
|
bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;
|
||||||
|
bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
|
||||||
|
|
||||||
|
std::set<int64_t> setInternalKeyPool GUARDED_BY(cs_KeyStore);
|
||||||
|
std::set<int64_t> setExternalKeyPool GUARDED_BY(cs_KeyStore);
|
||||||
|
std::set<int64_t> set_pre_split_keypool GUARDED_BY(cs_KeyStore);
|
||||||
|
int64_t m_max_keypool_index GUARDED_BY(cs_KeyStore) = 0;
|
||||||
|
std::map<CKeyID, int64_t> m_pool_key_to_index;
|
||||||
|
|
||||||
|
//! Load metadata (used by LoadWallet)
|
||||||
|
virtual void LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata);
|
||||||
|
virtual void LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata);
|
||||||
|
|
||||||
|
//! Adds a watch-only address to the store, without saving it to disk (used by LoadWallet)
|
||||||
|
bool LoadWatchOnly(const CScript &dest);
|
||||||
|
//! Returns whether the watch-only script is in the wallet
|
||||||
|
bool HaveWatchOnly(const CScript &dest) const;
|
||||||
|
//! Returns whether there are any watch-only things in the wallet
|
||||||
|
bool HaveWatchOnly() const;
|
||||||
|
//! Adds a key to the store, without saving it to disk (used by LoadWallet)
|
||||||
|
bool LoadKey(const CKey& key, const CPubKey &pubkey);
|
||||||
|
//! Adds an encrypted key to the store, without saving it to disk (used by LoadWallet)
|
||||||
|
bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret, bool checksum_valid);
|
||||||
|
//! Adds a CScript to the store
|
||||||
|
bool LoadCScript(const CScript& redeemScript);
|
||||||
|
//! Load a HD chain model (used by LoadWallet)
|
||||||
|
void LoadHDChain(const CHDChain& chain);
|
||||||
|
void AddInactiveHDChain(const CHDChain& chain);
|
||||||
|
const CHDChain& GetHDChain() const { return m_hd_chain; }
|
||||||
|
//! Load a keypool entry
|
||||||
|
void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool);
|
||||||
|
|
||||||
|
//! Fetches a pubkey from mapWatchKeys if it exists there
|
||||||
|
bool GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves scripts that were imported by bugs into the legacy spkm and are
|
||||||
|
* simply invalid, such as a sh(sh(pkh())) script, or not watched.
|
||||||
|
*/
|
||||||
|
std::unordered_set<CScript, SaltedSipHasher> GetNotMineScriptPubKeys() const;
|
||||||
|
|
||||||
|
/** Get the DescriptorScriptPubKeyMans (with private keys) that have the same scriptPubKeys as this LegacyScriptPubKeyMan.
|
||||||
|
* Does not modify this ScriptPubKeyMan. */
|
||||||
|
std::optional<MigrationData> MigrateToDescriptor();
|
||||||
|
/** Delete all the records ofthis LegacyScriptPubKeyMan from disk*/
|
||||||
|
bool DeleteRecords();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Implements the full legacy wallet behavior
|
||||||
|
class LegacyScriptPubKeyMan : public LegacyDataSPKM
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
WalletBatch *encrypted_batch GUARDED_BY(cs_KeyStore) = nullptr;
|
||||||
|
|
||||||
// By default, do not scan any block until keys/scripts are generated/imported
|
// By default, do not scan any block until keys/scripts are generated/imported
|
||||||
int64_t nTimeFirstKey GUARDED_BY(cs_KeyStore) = UNKNOWN_TIME;
|
int64_t nTimeFirstKey GUARDED_BY(cs_KeyStore) = UNKNOWN_TIME;
|
||||||
|
|
||||||
//! Number of pre-generated keys/scripts (part of the look-ahead process, used to detect payments)
|
//! Number of pre-generated keys/scripts (part of the look-ahead process, used to detect payments)
|
||||||
int64_t m_keypool_size GUARDED_BY(cs_KeyStore){DEFAULT_KEYPOOL_SIZE};
|
int64_t m_keypool_size GUARDED_BY(cs_KeyStore){DEFAULT_KEYPOOL_SIZE};
|
||||||
|
|
||||||
bool AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey);
|
bool AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey) override;
|
||||||
bool AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private version of AddWatchOnly method which does not accept a
|
* Private version of AddWatchOnly method which does not accept a
|
||||||
|
@ -315,7 +395,6 @@ private:
|
||||||
*/
|
*/
|
||||||
bool AddWatchOnly(const CScript& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
bool AddWatchOnly(const CScript& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
||||||
bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
||||||
bool AddWatchOnlyInMem(const CScript &dest);
|
|
||||||
//! Adds a watch-only address to the store, and saves it to disk.
|
//! Adds a watch-only address to the store, and saves it to disk.
|
||||||
bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest, int64_t create_time) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest, int64_t create_time) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
||||||
|
|
||||||
|
@ -330,18 +409,9 @@ private:
|
||||||
/** Add a KeyOriginInfo to the wallet */
|
/** Add a KeyOriginInfo to the wallet */
|
||||||
bool AddKeyOriginWithDB(WalletBatch& batch, const CPubKey& pubkey, const KeyOriginInfo& info);
|
bool AddKeyOriginWithDB(WalletBatch& batch, const CPubKey& pubkey, const KeyOriginInfo& info);
|
||||||
|
|
||||||
/* the HD chain data model (external chain counters) */
|
|
||||||
CHDChain m_hd_chain;
|
|
||||||
std::unordered_map<CKeyID, CHDChain, SaltedSipHasher> m_inactive_hd_chains;
|
|
||||||
|
|
||||||
/* HD derive new child key (on internal or external chain) */
|
/* HD derive new child key (on internal or external chain) */
|
||||||
void DeriveNewChildKey(WalletBatch& batch, CKeyMetadata& metadata, CKey& secret, CHDChain& hd_chain, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
void DeriveNewChildKey(WalletBatch& batch, CKeyMetadata& metadata, CKey& secret, CHDChain& hd_chain, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
||||||
|
|
||||||
std::set<int64_t> setInternalKeyPool GUARDED_BY(cs_KeyStore);
|
|
||||||
std::set<int64_t> setExternalKeyPool GUARDED_BY(cs_KeyStore);
|
|
||||||
std::set<int64_t> set_pre_split_keypool GUARDED_BY(cs_KeyStore);
|
|
||||||
int64_t m_max_keypool_index GUARDED_BY(cs_KeyStore) = 0;
|
|
||||||
std::map<CKeyID, int64_t> m_pool_key_to_index;
|
|
||||||
// Tracks keypool indexes to CKeyIDs of keys that have been taken out of the keypool but may be returned to it
|
// Tracks keypool indexes to CKeyIDs of keys that have been taken out of the keypool but may be returned to it
|
||||||
std::map<int64_t, CKeyID> m_index_to_reserved_key;
|
std::map<int64_t, CKeyID> m_index_to_reserved_key;
|
||||||
|
|
||||||
|
@ -378,12 +448,10 @@ private:
|
||||||
|
|
||||||
bool TopUpChain(WalletBatch& batch, CHDChain& chain, unsigned int size);
|
bool TopUpChain(WalletBatch& batch, CHDChain& chain, unsigned int size);
|
||||||
public:
|
public:
|
||||||
LegacyScriptPubKeyMan(WalletStorage& storage, int64_t keypool_size) : ScriptPubKeyMan(storage), m_keypool_size(keypool_size) {}
|
LegacyScriptPubKeyMan(WalletStorage& storage, int64_t keypool_size) : LegacyDataSPKM(storage), m_keypool_size(keypool_size) {}
|
||||||
|
|
||||||
util::Result<CTxDestination> GetNewDestination(const OutputType type) override;
|
util::Result<CTxDestination> GetNewDestination(const OutputType type) override;
|
||||||
isminetype IsMine(const CScript& script) const override;
|
|
||||||
|
|
||||||
bool CheckDecryptionKey(const CKeyingMaterial& master_key) override;
|
|
||||||
bool Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) override;
|
bool Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) override;
|
||||||
|
|
||||||
util::Result<CTxDestination> GetReservedDestination(const OutputType type, bool internal, int64_t& index, CKeyPool& keypool) override;
|
util::Result<CTxDestination> GetReservedDestination(const OutputType type, bool internal, int64_t& index, CKeyPool& keypool) override;
|
||||||
|
@ -417,8 +485,6 @@ public:
|
||||||
|
|
||||||
bool CanGetAddresses(bool internal = false) const override;
|
bool CanGetAddresses(bool internal = false) const override;
|
||||||
|
|
||||||
std::unique_ptr<SigningProvider> GetSolvingProvider(const CScript& script) const override;
|
|
||||||
|
|
||||||
bool CanProvide(const CScript& script, SignatureData& sigdata) override;
|
bool CanProvide(const CScript& script, SignatureData& sigdata) override;
|
||||||
|
|
||||||
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors) const override;
|
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors) const override;
|
||||||
|
@ -427,58 +493,27 @@ public:
|
||||||
|
|
||||||
uint256 GetID() const override;
|
uint256 GetID() const override;
|
||||||
|
|
||||||
// Map from Key ID to key metadata.
|
|
||||||
std::map<CKeyID, CKeyMetadata> mapKeyMetadata GUARDED_BY(cs_KeyStore);
|
|
||||||
|
|
||||||
// Map from Script ID to key metadata (for watch-only keys).
|
|
||||||
std::map<CScriptID, CKeyMetadata> m_script_metadata GUARDED_BY(cs_KeyStore);
|
|
||||||
|
|
||||||
//! Adds a key to the store, and saves it to disk.
|
//! Adds a key to the store, and saves it to disk.
|
||||||
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override;
|
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override;
|
||||||
//! Adds a key to the store, without saving it to disk (used by LoadWallet)
|
|
||||||
bool LoadKey(const CKey& key, const CPubKey &pubkey);
|
|
||||||
//! Adds an encrypted key to the store, and saves it to disk.
|
//! Adds an encrypted key to the store, and saves it to disk.
|
||||||
bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
|
bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
|
||||||
//! Adds an encrypted key to the store, without saving it to disk (used by LoadWallet)
|
|
||||||
bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret, bool checksum_valid);
|
|
||||||
void UpdateTimeFirstKey(int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
void UpdateTimeFirstKey(int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
||||||
//! Adds a CScript to the store
|
|
||||||
bool LoadCScript(const CScript& redeemScript);
|
|
||||||
//! Load metadata (used by LoadWallet)
|
//! Load metadata (used by LoadWallet)
|
||||||
void LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata);
|
void LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata) override;
|
||||||
void LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata);
|
void LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata) override;
|
||||||
//! Generate a new key
|
//! Generate a new key
|
||||||
CPubKey GenerateNewKey(WalletBatch& batch, CHDChain& hd_chain, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
CPubKey GenerateNewKey(WalletBatch& batch, CHDChain& hd_chain, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
||||||
|
|
||||||
/* Set the HD chain model (chain child index counters) and writes it to the database */
|
/* Set the HD chain model (chain child index counters) and writes it to the database */
|
||||||
void AddHDChain(const CHDChain& chain);
|
void AddHDChain(const CHDChain& chain);
|
||||||
//! Load a HD chain model (used by LoadWallet)
|
|
||||||
void LoadHDChain(const CHDChain& chain);
|
|
||||||
const CHDChain& GetHDChain() const { return m_hd_chain; }
|
|
||||||
void AddInactiveHDChain(const CHDChain& chain);
|
|
||||||
|
|
||||||
//! Adds a watch-only address to the store, without saving it to disk (used by LoadWallet)
|
|
||||||
bool LoadWatchOnly(const CScript &dest);
|
|
||||||
//! Returns whether the watch-only script is in the wallet
|
|
||||||
bool HaveWatchOnly(const CScript &dest) const;
|
|
||||||
//! Returns whether there are any watch-only things in the wallet
|
|
||||||
bool HaveWatchOnly() const;
|
|
||||||
//! Remove a watch only script from the keystore
|
//! Remove a watch only script from the keystore
|
||||||
bool RemoveWatchOnly(const CScript &dest);
|
bool RemoveWatchOnly(const CScript &dest);
|
||||||
bool AddWatchOnly(const CScript& dest, int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
bool AddWatchOnly(const CScript& dest, int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
||||||
|
|
||||||
//! Fetches a pubkey from mapWatchKeys if it exists there
|
|
||||||
bool GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const;
|
|
||||||
|
|
||||||
/* SigningProvider overrides */
|
/* SigningProvider overrides */
|
||||||
bool HaveKey(const CKeyID &address) const override;
|
|
||||||
bool GetKey(const CKeyID &address, CKey& keyOut) const override;
|
|
||||||
bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;
|
|
||||||
bool AddCScript(const CScript& redeemScript) override;
|
bool AddCScript(const CScript& redeemScript) override;
|
||||||
bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
|
|
||||||
|
|
||||||
//! Load a keypool entry
|
|
||||||
void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool);
|
|
||||||
bool NewKeyPool();
|
bool NewKeyPool();
|
||||||
void MarkPreSplitKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
void MarkPreSplitKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
||||||
|
|
||||||
|
@ -527,28 +562,15 @@ public:
|
||||||
const std::map<CKeyID, int64_t>& GetAllReserveKeys() const { return m_pool_key_to_index; }
|
const std::map<CKeyID, int64_t>& GetAllReserveKeys() const { return m_pool_key_to_index; }
|
||||||
|
|
||||||
std::set<CKeyID> GetKeys() const override;
|
std::set<CKeyID> GetKeys() const override;
|
||||||
std::unordered_set<CScript, SaltedSipHasher> GetScriptPubKeys() const override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves scripts that were imported by bugs into the legacy spkm and are
|
|
||||||
* simply invalid, such as a sh(sh(pkh())) script, or not watched.
|
|
||||||
*/
|
|
||||||
std::unordered_set<CScript, SaltedSipHasher> GetNotMineScriptPubKeys() const;
|
|
||||||
|
|
||||||
/** Get the DescriptorScriptPubKeyMans (with private keys) that have the same scriptPubKeys as this LegacyScriptPubKeyMan.
|
|
||||||
* Does not modify this ScriptPubKeyMan. */
|
|
||||||
std::optional<MigrationData> MigrateToDescriptor();
|
|
||||||
/** Delete all the records ofthis LegacyScriptPubKeyMan from disk*/
|
|
||||||
bool DeleteRecords();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Wraps a LegacyScriptPubKeyMan so that it can be returned in a new unique_ptr. Does not provide privkeys */
|
/** Wraps a LegacyScriptPubKeyMan so that it can be returned in a new unique_ptr. Does not provide privkeys */
|
||||||
class LegacySigningProvider : public SigningProvider
|
class LegacySigningProvider : public SigningProvider
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
const LegacyScriptPubKeyMan& m_spk_man;
|
const LegacyDataSPKM& m_spk_man;
|
||||||
public:
|
public:
|
||||||
explicit LegacySigningProvider(const LegacyScriptPubKeyMan& spk_man) : m_spk_man(spk_man) {}
|
explicit LegacySigningProvider(const LegacyDataSPKM& spk_man) : m_spk_man(spk_man) {}
|
||||||
|
|
||||||
bool GetCScript(const CScriptID &scriptid, CScript& script) const override { return m_spk_man.GetCScript(scriptid, script); }
|
bool GetCScript(const CScriptID &scriptid, CScript& script) const override { return m_spk_man.GetCScript(scriptid, script); }
|
||||||
bool HaveCScript(const CScriptID &scriptid) const override { return m_spk_man.HaveCScript(scriptid); }
|
bool HaveCScript(const CScriptID &scriptid) const override { return m_spk_man.HaveCScript(scriptid); }
|
||||||
|
|
|
@ -2929,7 +2929,7 @@ bool CWallet::EraseAddressReceiveRequest(WalletBatch& batch, const CTxDestinatio
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error_string)
|
static util::Result<fs::path> GetWalletPath(const std::string& name)
|
||||||
{
|
{
|
||||||
// Do some checking on wallet path. It should be either a:
|
// Do some checking on wallet path. It should be either a:
|
||||||
//
|
//
|
||||||
|
@ -2942,15 +2942,24 @@ std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, cons
|
||||||
if (!(path_type == fs::file_type::not_found || path_type == fs::file_type::directory ||
|
if (!(path_type == fs::file_type::not_found || path_type == fs::file_type::directory ||
|
||||||
(path_type == fs::file_type::symlink && fs::is_directory(wallet_path)) ||
|
(path_type == fs::file_type::symlink && fs::is_directory(wallet_path)) ||
|
||||||
(path_type == fs::file_type::regular && fs::PathFromString(name).filename() == fs::PathFromString(name)))) {
|
(path_type == fs::file_type::regular && fs::PathFromString(name).filename() == fs::PathFromString(name)))) {
|
||||||
error_string = Untranslated(strprintf(
|
return util::Error{Untranslated(strprintf(
|
||||||
"Invalid -wallet path '%s'. -wallet path should point to a directory where wallet.dat and "
|
"Invalid -wallet path '%s'. -wallet path should point to a directory where wallet.dat and "
|
||||||
"database/log.?????????? files can be stored, a location where such a directory could be created, "
|
"database/log.?????????? files can be stored, a location where such a directory could be created, "
|
||||||
"or (for backwards compatibility) the name of an existing data file in -walletdir (%s)",
|
"or (for backwards compatibility) the name of an existing data file in -walletdir (%s)",
|
||||||
name, fs::quoted(fs::PathToString(GetWalletDir()))));
|
name, fs::quoted(fs::PathToString(GetWalletDir()))))};
|
||||||
|
}
|
||||||
|
return wallet_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error_string)
|
||||||
|
{
|
||||||
|
const auto& wallet_path = GetWalletPath(name);
|
||||||
|
if (!wallet_path) {
|
||||||
|
error_string = util::ErrorString(wallet_path);
|
||||||
status = DatabaseStatus::FAILED_BAD_PATH;
|
status = DatabaseStatus::FAILED_BAD_PATH;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return MakeDatabase(wallet_path, options, status, error_string);
|
return MakeDatabase(*wallet_path, options, status, error_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::string& name, std::unique_ptr<WalletDatabase> database, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings)
|
std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::string& name, std::unique_ptr<WalletDatabase> database, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings)
|
||||||
|
@ -3608,6 +3617,16 @@ LegacyScriptPubKeyMan* CWallet::GetLegacyScriptPubKeyMan() const
|
||||||
return dynamic_cast<LegacyScriptPubKeyMan*>(it->second);
|
return dynamic_cast<LegacyScriptPubKeyMan*>(it->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LegacyDataSPKM* CWallet::GetLegacyDataSPKM() const
|
||||||
|
{
|
||||||
|
if (IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto it = m_internal_spk_managers.find(OutputType::LEGACY);
|
||||||
|
if (it == m_internal_spk_managers.end()) return nullptr;
|
||||||
|
return dynamic_cast<LegacyDataSPKM*>(it->second);
|
||||||
|
}
|
||||||
|
|
||||||
LegacyScriptPubKeyMan* CWallet::GetOrCreateLegacyScriptPubKeyMan()
|
LegacyScriptPubKeyMan* CWallet::GetOrCreateLegacyScriptPubKeyMan()
|
||||||
{
|
{
|
||||||
SetupLegacyScriptPubKeyMan();
|
SetupLegacyScriptPubKeyMan();
|
||||||
|
@ -3624,13 +3643,22 @@ void CWallet::AddScriptPubKeyMan(const uint256& id, std::unique_ptr<ScriptPubKey
|
||||||
MaybeUpdateBirthTime(spkm->GetTimeFirstKey());
|
MaybeUpdateBirthTime(spkm->GetTimeFirstKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LegacyDataSPKM* CWallet::GetOrCreateLegacyDataSPKM()
|
||||||
|
{
|
||||||
|
SetupLegacyScriptPubKeyMan();
|
||||||
|
return GetLegacyDataSPKM();
|
||||||
|
}
|
||||||
|
|
||||||
void CWallet::SetupLegacyScriptPubKeyMan()
|
void CWallet::SetupLegacyScriptPubKeyMan()
|
||||||
{
|
{
|
||||||
if (!m_internal_spk_managers.empty() || !m_external_spk_managers.empty() || !m_spk_managers.empty() || IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
|
if (!m_internal_spk_managers.empty() || !m_external_spk_managers.empty() || !m_spk_managers.empty() || IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto spk_manager = std::unique_ptr<ScriptPubKeyMan>(new LegacyScriptPubKeyMan(*this, m_keypool_size));
|
std::unique_ptr<ScriptPubKeyMan> spk_manager = m_database->Format() == "bdb_ro" ?
|
||||||
|
std::make_unique<LegacyDataSPKM>(*this) :
|
||||||
|
std::make_unique<LegacyScriptPubKeyMan>(*this, m_keypool_size);
|
||||||
|
|
||||||
for (const auto& type : LEGACY_OUTPUT_TYPES) {
|
for (const auto& type : LEGACY_OUTPUT_TYPES) {
|
||||||
m_internal_spk_managers[type] = spk_manager.get();
|
m_internal_spk_managers[type] = spk_manager.get();
|
||||||
m_external_spk_managers[type] = spk_manager.get();
|
m_external_spk_managers[type] = spk_manager.get();
|
||||||
|
@ -3998,7 +4026,7 @@ std::optional<MigrationData> CWallet::GetDescriptorsForLegacy(bilingual_str& err
|
||||||
{
|
{
|
||||||
AssertLockHeld(cs_wallet);
|
AssertLockHeld(cs_wallet);
|
||||||
|
|
||||||
LegacyScriptPubKeyMan* legacy_spkm = GetLegacyScriptPubKeyMan();
|
LegacyDataSPKM* legacy_spkm = GetLegacyDataSPKM();
|
||||||
if (!Assume(legacy_spkm)) {
|
if (!Assume(legacy_spkm)) {
|
||||||
// This shouldn't happen
|
// This shouldn't happen
|
||||||
error = Untranslated(STR_INTERNAL_BUG("Error: Legacy wallet data missing"));
|
error = Untranslated(STR_INTERNAL_BUG("Error: Legacy wallet data missing"));
|
||||||
|
@ -4017,7 +4045,7 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error)
|
||||||
{
|
{
|
||||||
AssertLockHeld(cs_wallet);
|
AssertLockHeld(cs_wallet);
|
||||||
|
|
||||||
LegacyScriptPubKeyMan* legacy_spkm = GetLegacyScriptPubKeyMan();
|
LegacyDataSPKM* legacy_spkm = GetLegacyDataSPKM();
|
||||||
if (!Assume(legacy_spkm)) {
|
if (!Assume(legacy_spkm)) {
|
||||||
// This shouldn't happen
|
// This shouldn't happen
|
||||||
error = Untranslated(STR_INTERNAL_BUG("Error: Legacy wallet data missing"));
|
error = Untranslated(STR_INTERNAL_BUG("Error: Legacy wallet data missing"));
|
||||||
|
@ -4352,11 +4380,24 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& walle
|
||||||
// If the wallet is still loaded, unload it so that nothing else tries to use it while we're changing it
|
// If the wallet is still loaded, unload it so that nothing else tries to use it while we're changing it
|
||||||
bool was_loaded = false;
|
bool was_loaded = false;
|
||||||
if (auto wallet = GetWallet(context, wallet_name)) {
|
if (auto wallet = GetWallet(context, wallet_name)) {
|
||||||
|
if (wallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
|
||||||
|
return util::Error{_("Error: This wallet is already a descriptor wallet")};
|
||||||
|
}
|
||||||
|
|
||||||
if (!RemoveWallet(context, wallet, /*load_on_start=*/std::nullopt, warnings)) {
|
if (!RemoveWallet(context, wallet, /*load_on_start=*/std::nullopt, warnings)) {
|
||||||
return util::Error{_("Unable to unload the wallet before migrating")};
|
return util::Error{_("Unable to unload the wallet before migrating")};
|
||||||
}
|
}
|
||||||
UnloadWallet(std::move(wallet));
|
UnloadWallet(std::move(wallet));
|
||||||
was_loaded = true;
|
was_loaded = true;
|
||||||
|
} else {
|
||||||
|
// Check if the wallet is BDB
|
||||||
|
const auto& wallet_path = GetWalletPath(wallet_name);
|
||||||
|
if (!wallet_path) {
|
||||||
|
return util::Error{util::ErrorString(wallet_path)};
|
||||||
|
}
|
||||||
|
if (!IsBDBFile(BDBDataFile(*wallet_path))) {
|
||||||
|
return util::Error{_("Error: This wallet is already a descriptor wallet")};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the wallet but only in the context of this function.
|
// Load the wallet but only in the context of this function.
|
||||||
|
@ -4365,6 +4406,7 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& walle
|
||||||
empty_context.args = context.args;
|
empty_context.args = context.args;
|
||||||
DatabaseOptions options;
|
DatabaseOptions options;
|
||||||
options.require_existing = true;
|
options.require_existing = true;
|
||||||
|
options.require_format = DatabaseFormat::BERKELEY_RO;
|
||||||
DatabaseStatus status;
|
DatabaseStatus status;
|
||||||
std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(wallet_name, options, status, error);
|
std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(wallet_name, options, status, error);
|
||||||
if (!database) {
|
if (!database) {
|
||||||
|
@ -4379,6 +4421,8 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& walle
|
||||||
|
|
||||||
// Helper to reload as normal for some of our exit scenarios
|
// Helper to reload as normal for some of our exit scenarios
|
||||||
const auto& reload_wallet = [&](std::shared_ptr<CWallet>& to_reload) {
|
const auto& reload_wallet = [&](std::shared_ptr<CWallet>& to_reload) {
|
||||||
|
// Reset options.require_format as wallets of any format may be reloaded.
|
||||||
|
options.require_format = std::nullopt;
|
||||||
assert(to_reload.use_count() == 1);
|
assert(to_reload.use_count() == 1);
|
||||||
std::string name = to_reload->GetName();
|
std::string name = to_reload->GetName();
|
||||||
to_reload.reset();
|
to_reload.reset();
|
||||||
|
|
|
@ -963,8 +963,10 @@ public:
|
||||||
//! Get the LegacyScriptPubKeyMan which is used for all types, internal, and external.
|
//! Get the LegacyScriptPubKeyMan which is used for all types, internal, and external.
|
||||||
LegacyScriptPubKeyMan* GetLegacyScriptPubKeyMan() const;
|
LegacyScriptPubKeyMan* GetLegacyScriptPubKeyMan() const;
|
||||||
LegacyScriptPubKeyMan* GetOrCreateLegacyScriptPubKeyMan();
|
LegacyScriptPubKeyMan* GetOrCreateLegacyScriptPubKeyMan();
|
||||||
|
LegacyDataSPKM* GetLegacyDataSPKM() const;
|
||||||
|
LegacyDataSPKM* GetOrCreateLegacyDataSPKM();
|
||||||
|
|
||||||
//! Make a LegacyScriptPubKeyMan and set it for all types, internal, and external.
|
//! Make a Legacy(Data)SPKM and set it for all types, internal, and external.
|
||||||
void SetupLegacyScriptPubKeyMan();
|
void SetupLegacyScriptPubKeyMan();
|
||||||
|
|
||||||
bool WithEncryptionKey(std::function<bool (const CKeyingMaterial&)> cb) const override;
|
bool WithEncryptionKey(std::function<bool (const CKeyingMaterial&)> cb) const override;
|
||||||
|
|
|
@ -354,9 +354,9 @@ bool LoadKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::stri
|
||||||
strErr = "Error reading wallet database: CPrivKey corrupt";
|
strErr = "Error reading wallet database: CPrivKey corrupt";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKey(key, vchPubKey))
|
if (!pwallet->GetOrCreateLegacyDataSPKM()->LoadKey(key, vchPubKey))
|
||||||
{
|
{
|
||||||
strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadKey failed";
|
strErr = "Error reading wallet database: LegacyDataSPKM::LoadKey failed";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
|
@ -393,9 +393,9 @@ bool LoadCryptedKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, st
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadCryptedKey(vchPubKey, vchPrivKey, checksum_valid))
|
if (!pwallet->GetOrCreateLegacyDataSPKM()->LoadCryptedKey(vchPubKey, vchPrivKey, checksum_valid))
|
||||||
{
|
{
|
||||||
strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadCryptedKey failed";
|
strErr = "Error reading wallet database: LegacyDataSPKM::LoadCryptedKey failed";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
|
@ -440,7 +440,7 @@ bool LoadHDChain(CWallet* pwallet, DataStream& ssValue, std::string& strErr)
|
||||||
try {
|
try {
|
||||||
CHDChain chain;
|
CHDChain chain;
|
||||||
ssValue >> chain;
|
ssValue >> chain;
|
||||||
pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadHDChain(chain);
|
pwallet->GetOrCreateLegacyDataSPKM()->LoadHDChain(chain);
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
if (strErr.empty()) {
|
if (strErr.empty()) {
|
||||||
strErr = e.what();
|
strErr = e.what();
|
||||||
|
@ -584,9 +584,9 @@ static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch,
|
||||||
key >> hash;
|
key >> hash;
|
||||||
CScript script;
|
CScript script;
|
||||||
value >> script;
|
value >> script;
|
||||||
if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadCScript(script))
|
if (!pwallet->GetOrCreateLegacyDataSPKM()->LoadCScript(script))
|
||||||
{
|
{
|
||||||
strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadCScript failed";
|
strErr = "Error reading wallet database: LegacyDataSPKM::LoadCScript failed";
|
||||||
return DBErrors::NONCRITICAL_ERROR;
|
return DBErrors::NONCRITICAL_ERROR;
|
||||||
}
|
}
|
||||||
return DBErrors::LOAD_OK;
|
return DBErrors::LOAD_OK;
|
||||||
|
@ -607,7 +607,7 @@ static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch,
|
||||||
key >> vchPubKey;
|
key >> vchPubKey;
|
||||||
CKeyMetadata keyMeta;
|
CKeyMetadata keyMeta;
|
||||||
value >> keyMeta;
|
value >> keyMeta;
|
||||||
pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKeyMetadata(vchPubKey.GetID(), keyMeta);
|
pwallet->GetOrCreateLegacyDataSPKM()->LoadKeyMetadata(vchPubKey.GetID(), keyMeta);
|
||||||
|
|
||||||
// Extract some CHDChain info from this metadata if it has any
|
// Extract some CHDChain info from this metadata if it has any
|
||||||
if (keyMeta.nVersion >= CKeyMetadata::VERSION_WITH_HDDATA && !keyMeta.hd_seed_id.IsNull() && keyMeta.hdKeypath.size() > 0) {
|
if (keyMeta.nVersion >= CKeyMetadata::VERSION_WITH_HDDATA && !keyMeta.hd_seed_id.IsNull() && keyMeta.hdKeypath.size() > 0) {
|
||||||
|
@ -674,7 +674,7 @@ static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch,
|
||||||
|
|
||||||
// Set inactive chains
|
// Set inactive chains
|
||||||
if (!hd_chains.empty()) {
|
if (!hd_chains.empty()) {
|
||||||
LegacyScriptPubKeyMan* legacy_spkm = pwallet->GetLegacyScriptPubKeyMan();
|
LegacyDataSPKM* legacy_spkm = pwallet->GetLegacyDataSPKM();
|
||||||
if (legacy_spkm) {
|
if (legacy_spkm) {
|
||||||
for (const auto& [hd_seed_id, chain] : hd_chains) {
|
for (const auto& [hd_seed_id, chain] : hd_chains) {
|
||||||
if (hd_seed_id != legacy_spkm->GetHDChain().seed_id) {
|
if (hd_seed_id != legacy_spkm->GetHDChain().seed_id) {
|
||||||
|
@ -695,7 +695,7 @@ static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch,
|
||||||
uint8_t fYes;
|
uint8_t fYes;
|
||||||
value >> fYes;
|
value >> fYes;
|
||||||
if (fYes == '1') {
|
if (fYes == '1') {
|
||||||
pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadWatchOnly(script);
|
pwallet->GetOrCreateLegacyDataSPKM()->LoadWatchOnly(script);
|
||||||
}
|
}
|
||||||
return DBErrors::LOAD_OK;
|
return DBErrors::LOAD_OK;
|
||||||
});
|
});
|
||||||
|
@ -708,7 +708,7 @@ static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch,
|
||||||
key >> script;
|
key >> script;
|
||||||
CKeyMetadata keyMeta;
|
CKeyMetadata keyMeta;
|
||||||
value >> keyMeta;
|
value >> keyMeta;
|
||||||
pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadScriptMetadata(CScriptID(script), keyMeta);
|
pwallet->GetOrCreateLegacyDataSPKM()->LoadScriptMetadata(CScriptID(script), keyMeta);
|
||||||
return DBErrors::LOAD_OK;
|
return DBErrors::LOAD_OK;
|
||||||
});
|
});
|
||||||
result = std::max(result, watch_meta_res.m_result);
|
result = std::max(result, watch_meta_res.m_result);
|
||||||
|
@ -720,7 +720,7 @@ static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch,
|
||||||
key >> nIndex;
|
key >> nIndex;
|
||||||
CKeyPool keypool;
|
CKeyPool keypool;
|
||||||
value >> keypool;
|
value >> keypool;
|
||||||
pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKeyPool(nIndex, keypool);
|
pwallet->GetOrCreateLegacyDataSPKM()->LoadKeyPool(nIndex, keypool);
|
||||||
return DBErrors::LOAD_OK;
|
return DBErrors::LOAD_OK;
|
||||||
});
|
});
|
||||||
result = std::max(result, pool_res.m_result);
|
result = std::max(result, pool_res.m_result);
|
||||||
|
@ -763,7 +763,7 @@ static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch,
|
||||||
|
|
||||||
// nTimeFirstKey is only reliable if all keys have metadata
|
// nTimeFirstKey is only reliable if all keys have metadata
|
||||||
if (pwallet->IsLegacy() && (key_res.m_records + ckey_res.m_records + watch_script_res.m_records) != (keymeta_res.m_records + watch_meta_res.m_records)) {
|
if (pwallet->IsLegacy() && (key_res.m_records + ckey_res.m_records + watch_script_res.m_records) != (keymeta_res.m_records + watch_meta_res.m_records)) {
|
||||||
auto spk_man = pwallet->GetOrCreateLegacyScriptPubKeyMan();
|
auto spk_man = pwallet->GetLegacyScriptPubKeyMan();
|
||||||
if (spk_man) {
|
if (spk_man) {
|
||||||
LOCK(spk_man->cs_KeyStore);
|
LOCK(spk_man->cs_KeyStore);
|
||||||
spk_man->UpdateTimeFirstKey(1);
|
spk_man->UpdateTimeFirstKey(1);
|
||||||
|
|
|
@ -205,9 +205,13 @@ class WalletMigrationTest(BitcoinTestFramework):
|
||||||
self.assert_list_txs_equal(basic2.listtransactions(), basic2_txs)
|
self.assert_list_txs_equal(basic2.listtransactions(), basic2_txs)
|
||||||
|
|
||||||
# Now test migration on a descriptor wallet
|
# Now test migration on a descriptor wallet
|
||||||
self.log.info("Test \"nothing to migrate\" when the user tries to migrate a wallet with no legacy data")
|
self.log.info("Test \"nothing to migrate\" when the user tries to migrate a loaded wallet with no legacy data")
|
||||||
assert_raises_rpc_error(-4, "Error: This wallet is already a descriptor wallet", basic2.migratewallet)
|
assert_raises_rpc_error(-4, "Error: This wallet is already a descriptor wallet", basic2.migratewallet)
|
||||||
|
|
||||||
|
self.log.info("Test \"nothing to migrate\" when the user tries to migrate an unloaded wallet with no legacy data")
|
||||||
|
basic2.unloadwallet()
|
||||||
|
assert_raises_rpc_error(-4, "Error: This wallet is already a descriptor wallet", self.nodes[0].migratewallet, "basic2")
|
||||||
|
|
||||||
def test_multisig(self):
|
def test_multisig(self):
|
||||||
default = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
|
default = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
|
||||||
|
|
||||||
|
@ -467,6 +471,12 @@ class WalletMigrationTest(BitcoinTestFramework):
|
||||||
assert_raises_rpc_error(-4, "Error: Wallet decryption failed, the wallet passphrase was not provided or was incorrect", wallet.migratewallet, None, "badpass")
|
assert_raises_rpc_error(-4, "Error: Wallet decryption failed, the wallet passphrase was not provided or was incorrect", wallet.migratewallet, None, "badpass")
|
||||||
assert_raises_rpc_error(-4, "The passphrase contains a null character", wallet.migratewallet, None, "pass\0with\0null")
|
assert_raises_rpc_error(-4, "The passphrase contains a null character", wallet.migratewallet, None, "pass\0with\0null")
|
||||||
|
|
||||||
|
# Check the wallet is still active post-migration failure.
|
||||||
|
# If not, it will throw an exception and abort the test.
|
||||||
|
wallet.walletpassphrase("pass", 99999)
|
||||||
|
wallet.getnewaddress()
|
||||||
|
|
||||||
|
# Verify we can properly migrate the encrypted wallet
|
||||||
self.migrate_wallet(wallet, passphrase="pass")
|
self.migrate_wallet(wallet, passphrase="pass")
|
||||||
|
|
||||||
info = wallet.getwalletinfo()
|
info = wallet.getwalletinfo()
|
||||||
|
|
Loading…
Add table
Reference in a new issue