mirror of
https://github.com/dashpay/dash.git
synced 2024-12-26 12:32:48 +01:00
feat: enable HD wallets by default (#5807)
## Issue being fixed or feature implemented HD wallets are old-existsing feature, appeared in Dash years ago, but enabling HD wallets is not trivial task that requires multiple steps and command line/rpc calls. Let's have them enabled by default. ## What was done? - HD wallets are enabled by default. Currently behavior `dashd`, `dash-qt` are similar to run with option `-usehd=1` - the rpc `upgradewallet` do not let to upgrade from non-HD wallet to HD wallet to don't encourage user use non-crypted wallets (postponed till v21) - the initialization of ScriptPubKey is updated to be sure that encypted HD seed is never written on disk (if passphrase is provided) - enabled and dashified a script `wallet_upgradewallet.py` which test compatibility between different versions of wallet ## What is not done? - wallet tool still does not support passhprase, HD seed can appear on disk - there's no dialog that show user a mnemonic phrase and encourage him to make a paper backup Before removing a command line 'usehd' (backport bitcoin#11250) need to make at least one major release for fail-over option (if someone wish to use non-HD wallets only). ## How Has This Been Tested? Run unit and functional tests. Enabled new functional test `wallet_upgradewallet.py` that has been backported long time ago but waited this PR to be enabled. ## Breaking Changes HD wallets are created by default. ## Checklist: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone --------- Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
This commit is contained in:
parent
19681d0f45
commit
8dba6559f6
7
doc/release-notes-5807.md
Normal file
7
doc/release-notes-5807.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Notable changes
|
||||||
|
|
||||||
|
## HD Wallets Enabled by Default
|
||||||
|
|
||||||
|
In this release, we are taking a significant step towards enhancing the Dash wallet's usability by enabling Hierarchical Deterministic (HD) wallets by default. This change aligns the behavior of `dashd` and `dash-qt` with the previously optional `-usehd=1` flag, making HD wallets the standard for all users.
|
||||||
|
|
||||||
|
While HD wallets are now enabled by default to improve user experience, Dash Core still allows for the creation of non-HD wallets by using the `-usehd=0` flag. However, users should be aware that this option is intended for legacy support and will be removed in future releases. Importantly, even with an HD wallet, users can still import non-HD private keys, ensuring flexibility in managing their funds.
|
@ -50,7 +50,7 @@
|
|||||||
<string>Encrypt Wallet</string>
|
<string>Encrypt Wallet</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="checked">
|
<property name="checked">
|
||||||
<bool>false</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -2681,49 +2681,23 @@ static UniValue upgradetohd(const JSONRPCRequest& request)
|
|||||||
},
|
},
|
||||||
}.Check(request);
|
}.Check(request);
|
||||||
|
|
||||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
|
||||||
if (!wallet) return NullUniValue;
|
if (!pwallet) return NullUniValue;
|
||||||
CWallet* const pwallet = wallet.get();
|
|
||||||
LegacyScriptPubKeyMan* spk_man = pwallet->GetLegacyScriptPubKeyMan();
|
|
||||||
if (!spk_man) {
|
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "This type of wallet does not support this command");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool generate_mnemonic = request.params[0].isNull() || request.params[0].get_str().empty();
|
bool generate_mnemonic = request.params[0].isNull() || request.params[0].get_str().empty();
|
||||||
|
|
||||||
{
|
|
||||||
LOCK(pwallet->cs_wallet);
|
|
||||||
|
|
||||||
// Do not do anything to HD wallets
|
|
||||||
if (pwallet->IsHDEnabled()) {
|
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot upgrade a wallet to HD if it is already upgraded to HD.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pwallet->SetMaxVersion(FEATURE_HD)) {
|
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot downgrade wallet");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
|
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool prev_encrypted = pwallet->IsCrypted();
|
|
||||||
|
|
||||||
SecureString secureWalletPassphrase;
|
SecureString secureWalletPassphrase;
|
||||||
secureWalletPassphrase.reserve(100);
|
secureWalletPassphrase.reserve(100);
|
||||||
// TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
|
// TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
|
||||||
// Alternately, find a way to make request.params[0] mlock()'d to begin with.
|
// Alternately, find a way to make request.params[0] mlock()'d to begin with.
|
||||||
if (request.params[2].isNull()) {
|
if (!request.params[2].isNull()) {
|
||||||
if (prev_encrypted) {
|
|
||||||
throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Cannot upgrade encrypted wallet to HD without the wallet passphrase");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
secureWalletPassphrase = request.params[2].get_str().c_str();
|
secureWalletPassphrase = request.params[2].get_str().c_str();
|
||||||
if (!pwallet->Unlock(secureWalletPassphrase)) {
|
if (!pwallet->Unlock(secureWalletPassphrase)) {
|
||||||
throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "The wallet passphrase entered was incorrect");
|
throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "The wallet passphrase entered was incorrect");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EnsureWalletIsUnlocked(pwallet.get());
|
||||||
|
|
||||||
SecureString secureMnemonic;
|
SecureString secureMnemonic;
|
||||||
secureMnemonic.reserve(256);
|
secureMnemonic.reserve(256);
|
||||||
if (!generate_mnemonic) {
|
if (!generate_mnemonic) {
|
||||||
@ -2735,22 +2709,25 @@ static UniValue upgradetohd(const JSONRPCRequest& request)
|
|||||||
if (!request.params[1].isNull()) {
|
if (!request.params[1].isNull()) {
|
||||||
secureMnemonicPassphrase = request.params[1].get_str().c_str();
|
secureMnemonicPassphrase = request.params[1].get_str().c_str();
|
||||||
}
|
}
|
||||||
|
// TODO: breaking changes kept for v21!
|
||||||
pwallet->WalletLogPrintf("Upgrading wallet to HD\n");
|
// instead upgradetohd let's use more straightforward 'sethdseed'
|
||||||
pwallet->SetMinVersion(FEATURE_HD);
|
constexpr bool is_v21 = false;
|
||||||
|
const int previous_version{pwallet->GetVersion()};
|
||||||
if (prev_encrypted) {
|
if (is_v21 && previous_version >= FEATURE_HD) {
|
||||||
if (!pwallet->GenerateNewHDChainEncrypted(secureMnemonic, secureMnemonicPassphrase, secureWalletPassphrase)) {
|
return JSONRPCError(RPC_WALLET_ERROR, "Already at latest version. Wallet version unchanged.");
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "Failed to generate encrypted HD wallet");
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
spk_man->GenerateNewHDChain(secureMnemonic, secureMnemonicPassphrase);
|
bilingual_str error;
|
||||||
if (!secureWalletPassphrase.empty()) {
|
const bool wallet_upgraded{pwallet->UpgradeToHD(secureMnemonic, secureMnemonicPassphrase, secureWalletPassphrase, error)};
|
||||||
|
|
||||||
|
if (!secureWalletPassphrase.empty() && !pwallet->IsCrypted()) {
|
||||||
if (!pwallet->EncryptWallet(secureWalletPassphrase)) {
|
if (!pwallet->EncryptWallet(secureWalletPassphrase)) {
|
||||||
throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Failed to encrypt HD wallet");
|
throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Failed to encrypt HD wallet");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (!wallet_upgraded) {
|
||||||
|
throw JSONRPCError(RPC_WALLET_ERROR, error.original);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If you are generating new mnemonic it is assumed that the addresses have never gotten a transaction before, so you don't need to rescan for transactions
|
// If you are generating new mnemonic it is assumed that the addresses have never gotten a transaction before, so you don't need to rescan for transactions
|
||||||
|
@ -297,6 +297,10 @@ std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, interfaces::Coin
|
|||||||
status = DatabaseStatus::FAILED_CREATE;
|
status = DatabaseStatus::FAILED_CREATE;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
if (gArgs.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET)) {
|
||||||
|
wallet->WalletLogPrintf("Set HD by default\n");
|
||||||
|
wallet->SetMinVersion(FEATURE_HD);
|
||||||
|
}
|
||||||
|
|
||||||
// Encrypt the wallet
|
// Encrypt the wallet
|
||||||
if (!passphrase.empty() && !(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
|
if (!passphrase.empty() && !(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
|
||||||
@ -306,6 +310,18 @@ std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, interfaces::Coin
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (!create_blank) {
|
if (!create_blank) {
|
||||||
|
{
|
||||||
|
// TODO: drop this condition after removing option to create non-HD wallets
|
||||||
|
// related backport bitcoin#11250
|
||||||
|
if (wallet->GetVersion() >= FEATURE_HD) {
|
||||||
|
if (!wallet->GenerateNewHDChainEncrypted(/*secureMnemonic=*/"", /*secureMnemonicPassphrase=*/"", passphrase)) {
|
||||||
|
error = Untranslated("Error: Failed to generate encrypted HD wallet");
|
||||||
|
status = DatabaseStatus::FAILED_CREATE;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Unlock the wallet
|
// Unlock the wallet
|
||||||
if (!wallet->Unlock(passphrase)) {
|
if (!wallet->Unlock(passphrase)) {
|
||||||
error = Untranslated("Error: Wallet was encrypted but could not be unlocked");
|
error = Untranslated("Error: Wallet was encrypted but could not be unlocked");
|
||||||
@ -313,22 +329,6 @@ std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, interfaces::Coin
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set a HD chain for the wallet
|
|
||||||
// TODO: re-enable this and `keypoolsize_hd_internal` check in `wallet_createwallet.py`
|
|
||||||
// when HD is the default mode (make sure this actually works!)...
|
|
||||||
// if (auto spk_man = wallet->GetLegacyScriptPubKeyMan() {
|
|
||||||
// if (!spk_man->GenerateNewHDChainEncrypted("", "", passphrase)) {
|
|
||||||
// throw JSONRPCError(RPC_WALLET_ERROR, "Failed to generate encrypted HD wallet");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// ... and drop this
|
|
||||||
LOCK(wallet->cs_wallet);
|
|
||||||
wallet->UnsetWalletFlag(WALLET_FLAG_BLANK_WALLET);
|
|
||||||
if (auto spk_man = wallet->GetLegacyScriptPubKeyMan()) {
|
|
||||||
spk_man->NewKeyPool();
|
|
||||||
}
|
|
||||||
// end TODO
|
|
||||||
|
|
||||||
// backup the wallet we just encrypted
|
// backup the wallet we just encrypted
|
||||||
if (!wallet->AutoBackupWallet("", error, warnings) && !error.original.empty()) {
|
if (!wallet->AutoBackupWallet("", error, warnings) && !error.original.empty()) {
|
||||||
status = DatabaseStatus::FAILED_ENCRYPT;
|
status = DatabaseStatus::FAILED_ENCRYPT;
|
||||||
@ -4617,6 +4617,10 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, interfaces::C
|
|||||||
if (gArgs.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET) && !walletInstance->IsHDEnabled()) {
|
if (gArgs.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET) && !walletInstance->IsHDEnabled()) {
|
||||||
std::string strSeed = gArgs.GetArg("-hdseed", "not hex");
|
std::string strSeed = gArgs.GetArg("-hdseed", "not hex");
|
||||||
|
|
||||||
|
// ensure this wallet.dat can only be opened by clients supporting HD
|
||||||
|
walletInstance->WalletLogPrintf("Upgrading wallet to HD\n");
|
||||||
|
walletInstance->SetMinVersion(FEATURE_HD);
|
||||||
|
|
||||||
if (gArgs.IsArgSet("-hdseed") && IsHex(strSeed)) {
|
if (gArgs.IsArgSet("-hdseed") && IsHex(strSeed)) {
|
||||||
CHDChain newHdChain;
|
CHDChain newHdChain;
|
||||||
std::vector<unsigned char> vchSeed = ParseHex(strSeed);
|
std::vector<unsigned char> vchSeed = ParseHex(strSeed);
|
||||||
@ -4646,10 +4650,6 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, interfaces::C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure this wallet.dat can only be opened by clients supporting HD
|
|
||||||
walletInstance->WalletLogPrintf("Upgrading wallet to HD\n");
|
|
||||||
walletInstance->SetMinVersion(FEATURE_HD);
|
|
||||||
|
|
||||||
// clean up
|
// clean up
|
||||||
gArgs.ForceRemoveArg("hdseed");
|
gArgs.ForceRemoveArg("hdseed");
|
||||||
gArgs.ForceRemoveArg("mnemonic");
|
gArgs.ForceRemoveArg("mnemonic");
|
||||||
@ -4913,7 +4913,45 @@ bool CWallet::UpgradeWallet(int version, bilingual_str& error)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: consider discourage users to skip passphrase for HD wallets for v21
|
||||||
|
if (false && nMaxVersion >= FEATURE_HD && !IsHDEnabled()) {
|
||||||
|
error = Untranslated("You should use upgradetohd RPC to upgrade non-HD wallet to HD");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
SetMaxVersion(nMaxVersion);
|
SetMaxVersion(nMaxVersion);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CWallet::UpgradeToHD(const SecureString& secureMnemonic, const SecureString& secureMnemonicPassphrase, const SecureString& secureWalletPassphrase, bilingual_str& error)
|
||||||
|
{
|
||||||
|
LOCK(cs_wallet);
|
||||||
|
|
||||||
|
// Do not do anything to HD wallets
|
||||||
|
if (IsHDEnabled()) {
|
||||||
|
error = Untranslated("Cannot upgrade a wallet to HD if it is already upgraded to HD.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
|
||||||
|
error = Untranslated("Private keys are disabled for this wallet");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
WalletLogPrintf("Upgrading wallet to HD\n");
|
||||||
|
SetMinVersion(FEATURE_HD);
|
||||||
|
|
||||||
|
auto spk_man = GetOrCreateLegacyScriptPubKeyMan();
|
||||||
|
bool prev_encrypted = IsCrypted();
|
||||||
|
if (prev_encrypted) {
|
||||||
|
if (!GenerateNewHDChainEncrypted(secureMnemonic, secureMnemonicPassphrase, secureWalletPassphrase)) {
|
||||||
|
error = Untranslated("Failed to generate encrypted HD wallet");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
spk_man->GenerateNewHDChain(secureMnemonic, secureMnemonicPassphrase);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ static const CAmount HIGH_TX_FEE_PER_KB = COIN / 100;
|
|||||||
static const CAmount HIGH_MAX_TX_FEE = 100 * HIGH_TX_FEE_PER_KB;
|
static const CAmount HIGH_MAX_TX_FEE = 100 * HIGH_TX_FEE_PER_KB;
|
||||||
|
|
||||||
//! if set, all keys will be derived by using BIP39/BIP44
|
//! if set, all keys will be derived by using BIP39/BIP44
|
||||||
static const bool DEFAULT_USE_HD_WALLET = false;
|
static const bool DEFAULT_USE_HD_WALLET = true;
|
||||||
|
|
||||||
class CCoinControl;
|
class CCoinControl;
|
||||||
class CKey;
|
class CKey;
|
||||||
@ -1279,6 +1279,7 @@ public:
|
|||||||
/* Returns true if HD is enabled */
|
/* Returns true if HD is enabled */
|
||||||
bool IsHDEnabled() const;
|
bool IsHDEnabled() const;
|
||||||
|
|
||||||
|
// TODO: move it to scriptpubkeyman
|
||||||
/* Generates a new HD chain */
|
/* Generates a new HD chain */
|
||||||
bool GenerateNewHDChainEncrypted(const SecureString& secureMnemonic, const SecureString& secureMnemonicPassphrase, const SecureString& secureWalletPassphrase);
|
bool GenerateNewHDChainEncrypted(const SecureString& secureMnemonic, const SecureString& secureMnemonicPassphrase, const SecureString& secureWalletPassphrase);
|
||||||
|
|
||||||
@ -1331,6 +1332,9 @@ public:
|
|||||||
/** Upgrade the wallet */
|
/** Upgrade the wallet */
|
||||||
bool UpgradeWallet(int version, bilingual_str& error);
|
bool UpgradeWallet(int version, bilingual_str& error);
|
||||||
|
|
||||||
|
/** Upgrade non-HD wallet to HD wallet */
|
||||||
|
bool UpgradeToHD(const SecureString& secureMnemonic, const SecureString& secureMnemonicPassphrase, const SecureString& secureWalletPassphrase, bilingual_str& error);
|
||||||
|
|
||||||
//! Returns all unique ScriptPubKeyMans in m_internal_spk_managers and m_external_spk_managers
|
//! Returns all unique ScriptPubKeyMans in m_internal_spk_managers and m_external_spk_managers
|
||||||
std::set<ScriptPubKeyMan*> GetActiveScriptPubKeyMans() const;
|
std::set<ScriptPubKeyMan*> GetActiveScriptPubKeyMans() const;
|
||||||
|
|
||||||
|
@ -28,9 +28,11 @@ static void WalletCreate(CWallet* wallet_instance)
|
|||||||
|
|
||||||
// generate a new HD seed
|
// generate a new HD seed
|
||||||
wallet_instance->SetupLegacyScriptPubKeyMan();
|
wallet_instance->SetupLegacyScriptPubKeyMan();
|
||||||
// NOTE: we do not yet create HD wallets by default
|
auto spk_man = wallet_instance->GetLegacyScriptPubKeyMan();
|
||||||
// auto spk_man = wallet_instance->GetLegacyScriptPubKeyMan();
|
// NOTE: drop this condition after removing option to create non-HD wallets
|
||||||
// spk_man->GenerateNewHDChain("", "");
|
if (spk_man->IsHDEnabled()) {
|
||||||
|
spk_man->GenerateNewHDChain(/*secureMnemonic=*/"", /*secureMnemonicPassphrase=*/"");
|
||||||
|
}
|
||||||
|
|
||||||
tfm::format(std::cout, "Topping up keypool...\n");
|
tfm::format(std::cout, "Topping up keypool...\n");
|
||||||
wallet_instance->TopUpKeyPool();
|
wallet_instance->TopUpKeyPool();
|
||||||
|
@ -97,21 +97,6 @@ class ToolWalletTest(BitcoinTestFramework):
|
|||||||
# shasum_before = self.wallet_shasum()
|
# shasum_before = self.wallet_shasum()
|
||||||
timestamp_before = self.wallet_timestamp()
|
timestamp_before = self.wallet_timestamp()
|
||||||
self.log.debug('Wallet file timestamp before calling info: {}'.format(timestamp_before))
|
self.log.debug('Wallet file timestamp before calling info: {}'.format(timestamp_before))
|
||||||
out = textwrap.dedent('''\
|
|
||||||
Wallet info
|
|
||||||
===========
|
|
||||||
Encrypted: no
|
|
||||||
HD (hd seed available): no
|
|
||||||
Keypool Size: 1
|
|
||||||
Transactions: 0
|
|
||||||
Address Book: 1
|
|
||||||
''')
|
|
||||||
self.assert_tool_output(out, '-wallet=' + self.default_wallet_name, 'info')
|
|
||||||
|
|
||||||
self.start_node(0)
|
|
||||||
self.nodes[0].upgradetohd()
|
|
||||||
self.stop_node(0)
|
|
||||||
|
|
||||||
out = textwrap.dedent('''\
|
out = textwrap.dedent('''\
|
||||||
Wallet info
|
Wallet info
|
||||||
===========
|
===========
|
||||||
@ -122,6 +107,7 @@ class ToolWalletTest(BitcoinTestFramework):
|
|||||||
Address Book: 1
|
Address Book: 1
|
||||||
''')
|
''')
|
||||||
self.assert_tool_output(out, '-wallet=' + self.default_wallet_name, 'info')
|
self.assert_tool_output(out, '-wallet=' + self.default_wallet_name, 'info')
|
||||||
|
|
||||||
timestamp_after = self.wallet_timestamp()
|
timestamp_after = self.wallet_timestamp()
|
||||||
self.log.debug('Wallet file timestamp after calling info: {}'.format(timestamp_after))
|
self.log.debug('Wallet file timestamp after calling info: {}'.format(timestamp_after))
|
||||||
self.log_wallet_timestamp_comparison(timestamp_before, timestamp_after)
|
self.log_wallet_timestamp_comparison(timestamp_before, timestamp_after)
|
||||||
|
@ -113,9 +113,7 @@ class CreateWalletTest(BitcoinTestFramework):
|
|||||||
# There should only be 1 key
|
# There should only be 1 key
|
||||||
walletinfo = w6.getwalletinfo()
|
walletinfo = w6.getwalletinfo()
|
||||||
assert_equal(walletinfo['keypoolsize'], 1)
|
assert_equal(walletinfo['keypoolsize'], 1)
|
||||||
# TODO: re-enable this when HD is the default mode
|
assert_equal(walletinfo['keypoolsize_hd_internal'], 1)
|
||||||
# assert_equal(walletinfo['keypoolsize_hd_internal'], 1)
|
|
||||||
# end TODO
|
|
||||||
# Allow empty passphrase, but there should be a warning
|
# Allow empty passphrase, but there should be a warning
|
||||||
resp = self.nodes[0].createwallet(wallet_name='w7', disable_private_keys=False, blank=False, passphrase='')
|
resp = self.nodes[0].createwallet(wallet_name='w7', disable_private_keys=False, blank=False, passphrase='')
|
||||||
assert_equal(resp['warning'], 'Empty string given as passphrase, wallet will not be encrypted.')
|
assert_equal(resp['warning'], 'Empty string given as passphrase, wallet will not be encrypted.')
|
||||||
|
@ -21,12 +21,13 @@ from test_framework.util import (
|
|||||||
class WalletUpgradeToHDTest(BitcoinTestFramework):
|
class WalletUpgradeToHDTest(BitcoinTestFramework):
|
||||||
def set_test_params(self):
|
def set_test_params(self):
|
||||||
self.num_nodes = 1
|
self.num_nodes = 1
|
||||||
|
self.extra_args = [['-usehd=0']]
|
||||||
|
|
||||||
def skip_test_if_missing_module(self):
|
def skip_test_if_missing_module(self):
|
||||||
self.skip_if_no_wallet()
|
self.skip_if_no_wallet()
|
||||||
|
|
||||||
def setup_network(self):
|
def setup_network(self):
|
||||||
self.add_nodes(self.num_nodes)
|
self.add_nodes(self.num_nodes, self.extra_args)
|
||||||
self.start_nodes()
|
self.start_nodes()
|
||||||
self.import_deterministic_coinbase_privkeys()
|
self.import_deterministic_coinbase_privkeys()
|
||||||
|
|
||||||
@ -69,7 +70,8 @@ class WalletUpgradeToHDTest(BitcoinTestFramework):
|
|||||||
self.log.info("Should no longer be able to start it with HD disabled")
|
self.log.info("Should no longer be able to start it with HD disabled")
|
||||||
self.stop_node(0)
|
self.stop_node(0)
|
||||||
node.assert_start_raises_init_error(['-usehd=0'], "Error: Error loading %s: You can't disable HD on an already existing HD wallet" % self.default_wallet_name)
|
node.assert_start_raises_init_error(['-usehd=0'], "Error: Error loading %s: You can't disable HD on an already existing HD wallet" % self.default_wallet_name)
|
||||||
self.start_node(0)
|
self.extra_args = []
|
||||||
|
self.start_node(0, [])
|
||||||
balance_after = node.getbalance()
|
balance_after = node.getbalance()
|
||||||
|
|
||||||
self.recover_non_hd()
|
self.recover_non_hd()
|
||||||
@ -188,7 +190,7 @@ class WalletUpgradeToHDTest(BitcoinTestFramework):
|
|||||||
node.stop()
|
node.stop()
|
||||||
node.wait_until_stopped()
|
node.wait_until_stopped()
|
||||||
self.start_node(0, extra_args=['-rescan'])
|
self.start_node(0, extra_args=['-rescan'])
|
||||||
assert_raises_rpc_error(-14, "Cannot upgrade encrypted wallet to HD without the wallet passphrase", node.upgradetohd, mnemonic)
|
assert_raises_rpc_error(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.", node.upgradetohd, mnemonic)
|
||||||
assert_raises_rpc_error(-14, "The wallet passphrase entered was incorrect", node.upgradetohd, mnemonic, "", "wrongpass")
|
assert_raises_rpc_error(-14, "The wallet passphrase entered was incorrect", node.upgradetohd, mnemonic, "", "wrongpass")
|
||||||
assert node.upgradetohd(mnemonic, "", walletpass)
|
assert node.upgradetohd(mnemonic, "", walletpass)
|
||||||
assert_raises_rpc_error(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.", node.dumphdinfo)
|
assert_raises_rpc_error(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.", node.dumphdinfo)
|
||||||
|
@ -12,10 +12,11 @@ import os
|
|||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
from test_framework.blocktools import COINBASE_MATURITY
|
from test_framework.blocktools import COINBASE_MATURITY
|
||||||
from test_framework.test_framework import (BitcoinTestFramework, SkipTest)
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
from test_framework.util import (
|
from test_framework.util import (
|
||||||
assert_equal,
|
assert_equal,
|
||||||
assert_greater_than,
|
assert_greater_than,
|
||||||
|
assert_greater_than_or_equal,
|
||||||
assert_is_hex_string,
|
assert_is_hex_string,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -26,26 +27,21 @@ class UpgradeWalletTest(BitcoinTestFramework):
|
|||||||
self.num_nodes = 3
|
self.num_nodes = 3
|
||||||
self.extra_args = [
|
self.extra_args = [
|
||||||
[], # current wallet version
|
[], # current wallet version
|
||||||
["-usehd=1"], # v0.16.3 wallet
|
["-usehd=1"], # v18.2.2 wallet
|
||||||
["-usehd=0"] # v0.15.2 wallet
|
["-usehd=0"] # v0.16.1.1 wallet
|
||||||
]
|
]
|
||||||
self.wallet_names = [self.default_wallet_name]
|
self.wallet_names = [self.default_wallet_name, None, None]
|
||||||
|
|
||||||
def skip_test_if_missing_module(self):
|
def skip_test_if_missing_module(self):
|
||||||
self.skip_if_no_wallet()
|
self.skip_if_no_wallet()
|
||||||
self.skip_if_no_bdb()
|
self.skip_if_no_bdb()
|
||||||
self.skip_if_no_previous_releases()
|
self.skip_if_no_previous_releases()
|
||||||
# TODO: this test doesn't work yet
|
|
||||||
raise SkipTest("Test wallet_upgradewallet.py is not adapted for Dash Core yet.")
|
|
||||||
|
|
||||||
def setup_network(self):
|
|
||||||
self.setup_nodes()
|
|
||||||
|
|
||||||
def setup_nodes(self):
|
def setup_nodes(self):
|
||||||
self.add_nodes(self.num_nodes, extra_args=self.extra_args, versions=[
|
self.add_nodes(self.num_nodes, extra_args=self.extra_args, versions=[
|
||||||
None,
|
None,
|
||||||
160300,
|
18020200, # that's last version before `default wallets` are created
|
||||||
150200,
|
160101, # that's oldest version that support `import_deterministic_coinbase_privkeys`
|
||||||
])
|
])
|
||||||
self.start_nodes()
|
self.start_nodes()
|
||||||
self.import_deterministic_coinbase_privkeys()
|
self.import_deterministic_coinbase_privkeys()
|
||||||
@ -61,13 +57,13 @@ class UpgradeWalletTest(BitcoinTestFramework):
|
|||||||
Further info: https://github.com/bitcoin/bitcoin/pull/18774#discussion_r416967844
|
Further info: https://github.com/bitcoin/bitcoin/pull/18774#discussion_r416967844
|
||||||
"""
|
"""
|
||||||
node_from = self.nodes[0]
|
node_from = self.nodes[0]
|
||||||
v16_3_node = self.nodes[1]
|
v18_2_node = self.nodes[1]
|
||||||
to_height = node_from.getblockcount()
|
to_height = node_from.getblockcount()
|
||||||
height = self.nodes[1].getblockcount()
|
height = self.nodes[1].getblockcount()
|
||||||
for i in range(height, to_height+1):
|
for i in range(height, to_height+1):
|
||||||
b = node_from.getblock(blockhash=node_from.getblockhash(i), verbose=0)
|
b = node_from.getblock(blockhash=node_from.getblockhash(i), verbose=0)
|
||||||
v16_3_node.submitblock(b)
|
v18_2_node.submitblock(b)
|
||||||
assert_equal(v16_3_node.getblockcount(), to_height)
|
assert_equal(v18_2_node.getblockcount(), to_height)
|
||||||
|
|
||||||
def run_test(self):
|
def run_test(self):
|
||||||
self.nodes[0].generatetoaddress(COINBASE_MATURITY + 1, self.nodes[0].getnewaddress())
|
self.nodes[0].generatetoaddress(COINBASE_MATURITY + 1, self.nodes[0].getnewaddress())
|
||||||
@ -76,28 +72,28 @@ class UpgradeWalletTest(BitcoinTestFramework):
|
|||||||
res = self.nodes[0].getblockchaininfo()
|
res = self.nodes[0].getblockchaininfo()
|
||||||
assert_equal(res['blocks'], COINBASE_MATURITY + 1)
|
assert_equal(res['blocks'], COINBASE_MATURITY + 1)
|
||||||
node_master = self.nodes[0]
|
node_master = self.nodes[0]
|
||||||
v16_3_node = self.nodes[1]
|
v18_2_node = self.nodes[1]
|
||||||
v15_2_node = self.nodes[2]
|
v16_1_node = self.nodes[2]
|
||||||
|
|
||||||
# Send coins to old wallets for later conversion checks.
|
# Send coins to old wallets for later conversion checks.
|
||||||
v16_3_wallet = v16_3_node.get_wallet_rpc('wallet.dat')
|
v18_2_wallet = v18_2_node.get_wallet_rpc(self.default_wallet_name)
|
||||||
v16_3_address = v16_3_wallet.getnewaddress()
|
v18_2_address = v18_2_wallet.getnewaddress()
|
||||||
node_master.generatetoaddress(COINBASE_MATURITY + 1, v16_3_address)
|
node_master.generatetoaddress(COINBASE_MATURITY + 1, v18_2_address)
|
||||||
self.dumb_sync_blocks()
|
self.dumb_sync_blocks()
|
||||||
v16_3_balance = v16_3_wallet.getbalance()
|
v18_2_balance = v18_2_wallet.getbalance()
|
||||||
|
|
||||||
self.log.info("Test upgradewallet RPC...")
|
self.log.info("Test upgradewallet RPC...")
|
||||||
# Prepare for copying of the older wallet
|
# Prepare for copying of the older wallet
|
||||||
node_master_wallet_dir = os.path.join(node_master.datadir, "regtest/wallets")
|
node_master_wallet_dir = os.path.join(node_master.datadir, "regtest/wallets")
|
||||||
v16_3_wallet = os.path.join(v16_3_node.datadir, "regtest/wallets/wallet.dat")
|
v18_2_wallet = os.path.join(v18_2_node.datadir, "regtest/wallets/wallet.dat")
|
||||||
v15_2_wallet = os.path.join(v15_2_node.datadir, "regtest/wallet.dat")
|
v16_1_wallet = os.path.join(v16_1_node.datadir, "regtest/wallets/wallet.dat")
|
||||||
self.stop_nodes()
|
self.stop_nodes()
|
||||||
|
|
||||||
# Copy the 0.16.3 wallet to the last Dash Core version and open it:
|
# Copy the 0.16.3 wallet to the last Dash Core version and open it:
|
||||||
shutil.rmtree(node_master_wallet_dir)
|
shutil.rmtree(node_master_wallet_dir)
|
||||||
os.mkdir(node_master_wallet_dir)
|
os.mkdir(node_master_wallet_dir)
|
||||||
shutil.copy(
|
shutil.copy(
|
||||||
v16_3_wallet,
|
v18_2_wallet,
|
||||||
node_master_wallet_dir
|
node_master_wallet_dir
|
||||||
)
|
)
|
||||||
self.restart_node(0, ['-nowallet'])
|
self.restart_node(0, ['-nowallet'])
|
||||||
@ -111,16 +107,16 @@ class UpgradeWalletTest(BitcoinTestFramework):
|
|||||||
assert_equal(wallet.upgradewallet(), "")
|
assert_equal(wallet.upgradewallet(), "")
|
||||||
new_version = wallet.getwalletinfo()["walletversion"]
|
new_version = wallet.getwalletinfo()["walletversion"]
|
||||||
# upgraded wallet version should be greater than older one
|
# upgraded wallet version should be greater than older one
|
||||||
assert_greater_than(new_version, old_version)
|
assert_greater_than_or_equal(new_version, old_version)
|
||||||
# wallet should still contain the same balance
|
# wallet should still contain the same balance
|
||||||
assert_equal(wallet.getbalance(), v16_3_balance)
|
assert_equal(wallet.getbalance(), v18_2_balance)
|
||||||
|
|
||||||
self.stop_node(0)
|
self.stop_node(0)
|
||||||
# Copy the 0.15.2 wallet to the last Dash Core version and open it:
|
# Copy the 19.3.0 wallet to the last Dash Core version and open it:
|
||||||
shutil.rmtree(node_master_wallet_dir)
|
shutil.rmtree(node_master_wallet_dir)
|
||||||
os.mkdir(node_master_wallet_dir)
|
os.mkdir(node_master_wallet_dir)
|
||||||
shutil.copy(
|
shutil.copy(
|
||||||
v15_2_wallet,
|
v16_1_wallet,
|
||||||
node_master_wallet_dir
|
node_master_wallet_dir
|
||||||
)
|
)
|
||||||
self.restart_node(0, ['-nowallet'])
|
self.restart_node(0, ['-nowallet'])
|
||||||
@ -131,12 +127,17 @@ class UpgradeWalletTest(BitcoinTestFramework):
|
|||||||
assert_equal('hdseedid' in wallet.getwalletinfo(), False)
|
assert_equal('hdseedid' in wallet.getwalletinfo(), False)
|
||||||
# calling upgradewallet with explicit version number
|
# calling upgradewallet with explicit version number
|
||||||
# should return nothing if successful
|
# should return nothing if successful
|
||||||
assert_equal(wallet.upgradewallet(169900), "")
|
assert_equal(wallet.upgradewallet(120200), "")
|
||||||
|
|
||||||
new_version = wallet.getwalletinfo()["walletversion"]
|
new_version = wallet.getwalletinfo()["walletversion"]
|
||||||
# upgraded wallet should have version 169900
|
# upgraded wallet would not have 120200 version until HD seed actually appeared
|
||||||
assert_equal(new_version, 169900)
|
assert_greater_than(120200, new_version)
|
||||||
# after conversion master key hash should be present
|
# after conversion master key hash should not be present yet
|
||||||
assert_is_hex_string(wallet.getwalletinfo()['hdseedid'])
|
assert 'hdchainid' not in wallet.getwalletinfo()
|
||||||
|
assert_equal(wallet.upgradetohd(), True)
|
||||||
|
new_version = wallet.getwalletinfo()["walletversion"]
|
||||||
|
assert_equal(new_version, 120200)
|
||||||
|
assert_is_hex_string(wallet.getwalletinfo()['hdchainid'])
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
UpgradeWalletTest().main()
|
UpgradeWalletTest().main()
|
||||||
|
Loading…
Reference in New Issue
Block a user