mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-10 20:03:34 -03:00
Merge #12560: [wallet] Upgrade path for non-HD wallets to HD
a8da482
Bump wallet version for pre split keypool (Andrew Chow)dfcd9f3
Use a keypool of presplit keys after upgrading to hd chain split (Andrew Chow)5c50e93
Allow -upgradewallet to upgradewallets to HD (Andrew Chow)2bcf2b5
Test sethdseed (Andrew Chow)b5ba01a
Add 'sethdseed' RPC to initialize or replace HD seed (Chris Moore)dd3c07a
Separate HaveKey function that checks whether a key is in a keystore (Andrew Chow) Pull request description: Revival/rebase of #11085 Adds a new command `sethdseed` which allows you to either set or generate a new HD seed to be used. A new keypool can be generated or the original one kept and new keys added to the keypool will come from the new HD seed. Wallets that are not HD will be upgraded to be version FEATURE_HD_SPLIT when the `sethdseed` RPC command is used. I have also add some tests for this. Additionally `-upgradewallet` can now be used to upgrade a wallet from non-HD to HD. When it is used for such an upgrade, the keypool will be regenerated. Tree-SHA512: e56c792e150590429ac4a1061e8d6f7b20cca06366e184eb9bbade4cd6ae82699a28fe84f87031eadba97ad2c1606517a105f00fb7b45779c979243020071adb
This commit is contained in:
commit
e03c0db08f
8 changed files with 225 additions and 9 deletions
|
@ -195,3 +195,10 @@ CKeyID GetKeyForDestination(const CKeyStore& store, const CTxDestination& dest)
|
|||
}
|
||||
return CKeyID();
|
||||
}
|
||||
|
||||
bool HaveKey(const CKeyStore& store, const CKey& key)
|
||||
{
|
||||
CKey key2;
|
||||
key2.Set(key.begin(), key.end(), !key.IsCompressed());
|
||||
return store.HaveKey(key.GetPubKey().GetID()) || store.HaveKey(key2.GetPubKey().GetID());
|
||||
}
|
||||
|
|
|
@ -80,4 +80,7 @@ typedef std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char> > > Crypt
|
|||
/** Return the CKeyID of the key involved in a script (if there is a unique one). */
|
||||
CKeyID GetKeyForDestination(const CKeyStore& store, const CTxDestination& dest);
|
||||
|
||||
/** Checks if a CKey is in the given CKeyStore compressed or otherwise*/
|
||||
bool HaveKey(const CKeyStore& store, const CKey& key);
|
||||
|
||||
#endif // BITCOIN_KEYSTORE_H
|
||||
|
|
|
@ -70,6 +70,7 @@ namespace {
|
|||
const QStringList historyFilter = QStringList()
|
||||
<< "importprivkey"
|
||||
<< "importmulti"
|
||||
<< "sethdseed"
|
||||
<< "signmessagewithprivkey"
|
||||
<< "signrawtransaction"
|
||||
<< "signrawtransactionwithkey"
|
||||
|
|
|
@ -38,6 +38,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||
{ "sendtoaddress", 5 , "replaceable" },
|
||||
{ "sendtoaddress", 6 , "conf_target" },
|
||||
{ "settxfee", 0, "amount" },
|
||||
{ "sethdseed", 0, "newkeypool" },
|
||||
{ "getreceivedbyaddress", 1, "minconf" },
|
||||
{ "getreceivedbyaccount", 1, "minconf" },
|
||||
{ "getreceivedbylabel", 1, "minconf" },
|
||||
|
|
|
@ -4079,6 +4079,76 @@ static UniValue listlabels(const JSONRPCRequest& request)
|
|||
return ret;
|
||||
}
|
||||
|
||||
UniValue sethdseed(const JSONRPCRequest& request)
|
||||
{
|
||||
CWallet* const pwallet = GetWalletForJSONRPCRequest(request);
|
||||
|
||||
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
if (request.fHelp || request.params.size() > 2) {
|
||||
throw std::runtime_error(
|
||||
"sethdseed ( \"newkeypool\" \"seed\" )\n"
|
||||
"\nSet or generate a new HD wallet seed. Non-HD wallets will not be upgraded to being a HD wallet. Wallets that are already\n"
|
||||
"HD will have a new HD seed set so that new keys added to the keypool will be derived from this new seed.\n"
|
||||
"\nNote that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed.\n"
|
||||
+ HelpRequiringPassphrase(pwallet) +
|
||||
"\nArguments:\n"
|
||||
"1. \"newkeypool\" (boolean, optional, default=true) Whether to flush old unused addresses, including change addresses, from the keypool and regenerate it.\n"
|
||||
" If true, the next address from getnewaddress and change address from getrawchangeaddress will be from this new seed.\n"
|
||||
" If false, addresses (including change addresses if the wallet already had HD Chain Split enabled) from the existing\n"
|
||||
" keypool will be used until it has been depleted.\n"
|
||||
"2. \"seed\" (string, optional) The WIF private key to use as the new HD seed; if not provided a random seed will be used.\n"
|
||||
" The seed value can be retrieved using the dumpwallet command. It is the private key marked hdmaster=1\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("sethdseed", "")
|
||||
+ HelpExampleCli("sethdseed", "false")
|
||||
+ HelpExampleCli("sethdseed", "true \"wifkey\"")
|
||||
+ HelpExampleRpc("sethdseed", "true, \"wifkey\"")
|
||||
);
|
||||
}
|
||||
|
||||
if (IsInitialBlockDownload()) {
|
||||
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot set a new HD seed while still in Initial Block Download");
|
||||
}
|
||||
|
||||
LOCK2(cs_main, pwallet->cs_wallet);
|
||||
|
||||
// Do not do anything to non-HD wallets
|
||||
if (!pwallet->IsHDEnabled()) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set a HD seed on a non-HD wallet. Start with -upgradewallet in order to upgrade a non-HD wallet to HD");
|
||||
}
|
||||
|
||||
EnsureWalletIsUnlocked(pwallet);
|
||||
|
||||
bool flush_key_pool = true;
|
||||
if (!request.params[0].isNull()) {
|
||||
flush_key_pool = request.params[0].get_bool();
|
||||
}
|
||||
|
||||
CPubKey master_pub_key;
|
||||
if (request.params[1].isNull()) {
|
||||
master_pub_key = pwallet->GenerateNewHDMasterKey();
|
||||
} else {
|
||||
CKey key = DecodeSecret(request.params[1].get_str());
|
||||
if (!key.IsValid()) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
|
||||
}
|
||||
|
||||
if (HaveKey(*pwallet, key)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key (either as an HD seed or as a loose private key)");
|
||||
}
|
||||
|
||||
master_pub_key = pwallet->DeriveNewMasterHDKey(key);
|
||||
}
|
||||
|
||||
pwallet->SetHDMasterKey(master_pub_key);
|
||||
if (flush_key_pool) pwallet->NewKeyPool();
|
||||
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
extern UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
|
||||
extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
|
||||
extern UniValue importprivkey(const JSONRPCRequest& request);
|
||||
|
@ -4139,6 +4209,7 @@ static const CRPCCommand commands[] =
|
|||
{ "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout"} },
|
||||
{ "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} },
|
||||
{ "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} },
|
||||
{ "wallet", "sethdseed", &sethdseed, {"newkeypool","seed"} },
|
||||
|
||||
/** Account functions (deprecated) */
|
||||
{ "wallet", "getaccountaddress", &getlabeladdress, {"account"} },
|
||||
|
|
|
@ -1452,7 +1452,11 @@ CPubKey CWallet::GenerateNewHDMasterKey()
|
|||
{
|
||||
CKey key;
|
||||
key.MakeNewKey(true);
|
||||
return DeriveNewMasterHDKey(key);
|
||||
}
|
||||
|
||||
CPubKey CWallet::DeriveNewMasterHDKey(const CKey& key)
|
||||
{
|
||||
int64_t nCreationTime = GetTime();
|
||||
CKeyMetadata metadata(nCreationTime);
|
||||
|
||||
|
@ -3326,6 +3330,11 @@ bool CWallet::NewKeyPool()
|
|||
}
|
||||
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()) {
|
||||
|
@ -3339,13 +3348,15 @@ bool CWallet::NewKeyPool()
|
|||
size_t CWallet::KeypoolCountExternalKeys()
|
||||
{
|
||||
AssertLockHeld(cs_wallet); // setExternalKeyPool
|
||||
return setExternalKeyPool.size();
|
||||
return setExternalKeyPool.size() + set_pre_split_keypool.size();
|
||||
}
|
||||
|
||||
void CWallet::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool)
|
||||
{
|
||||
AssertLockHeld(cs_wallet);
|
||||
if (keypool.fInternal) {
|
||||
if (keypool.m_pre_split) {
|
||||
set_pre_split_keypool.insert(nIndex);
|
||||
} else if (keypool.fInternal) {
|
||||
setInternalKeyPool.insert(nIndex);
|
||||
} else {
|
||||
setExternalKeyPool.insert(nIndex);
|
||||
|
@ -3410,7 +3421,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
|
|||
m_pool_key_to_index[pubkey.GetID()] = index;
|
||||
}
|
||||
if (missingInternal + missingExternal > 0) {
|
||||
LogPrintf("keypool added %d keys (%d internal), size=%u (%u internal)\n", missingInternal + missingExternal, missingInternal, setInternalKeyPool.size() + setExternalKeyPool.size(), setInternalKeyPool.size());
|
||||
LogPrintf("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());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -3427,7 +3438,7 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe
|
|||
TopUpKeyPool();
|
||||
|
||||
bool fReturningInternal = IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT) && fRequestedInternal;
|
||||
std::set<int64_t>& setKeyPool = fReturningInternal ? setInternalKeyPool : setExternalKeyPool;
|
||||
std::set<int64_t>& setKeyPool = set_pre_split_keypool.empty() ? (fReturningInternal ? setInternalKeyPool : setExternalKeyPool) : set_pre_split_keypool;
|
||||
|
||||
// Get the oldest key
|
||||
if(setKeyPool.empty())
|
||||
|
@ -3444,7 +3455,8 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe
|
|||
if (!HaveKey(keypool.vchPubKey.GetID())) {
|
||||
throw std::runtime_error(std::string(__func__) + ": unknown key in key pool");
|
||||
}
|
||||
if (keypool.fInternal != fReturningInternal) {
|
||||
// If the key was pre-split keypool, we don't care about what type it is
|
||||
if (set_pre_split_keypool.size() == 0 && keypool.fInternal != fReturningInternal) {
|
||||
throw std::runtime_error(std::string(__func__) + ": keypool entry misclassified");
|
||||
}
|
||||
|
||||
|
@ -3469,6 +3481,8 @@ void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey)
|
|||
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);
|
||||
}
|
||||
|
@ -3521,6 +3535,9 @@ int64_t CWallet::GetOldestKeyPoolTime()
|
|||
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;
|
||||
|
@ -3718,8 +3735,8 @@ void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id)
|
|||
{
|
||||
AssertLockHeld(cs_wallet);
|
||||
bool internal = setInternalKeyPool.count(keypool_id);
|
||||
if (!internal) assert(setExternalKeyPool.count(keypool_id));
|
||||
std::set<int64_t> *setKeyPool = internal ? &setInternalKeyPool : &setExternalKeyPool;
|
||||
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);
|
||||
|
@ -3955,6 +3972,24 @@ std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
|
|||
return values;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& path)
|
||||
{
|
||||
const std::string& walletFile = name;
|
||||
|
@ -4006,6 +4041,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path&
|
|||
}
|
||||
}
|
||||
|
||||
int prev_version = walletInstance->nWalletVersion;
|
||||
if (gArgs.GetBoolArg("-upgradewallet", fFirstRun))
|
||||
{
|
||||
int nMaxVersion = gArgs.GetArg("-upgradewallet", 0);
|
||||
|
@ -4025,6 +4061,49 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path&
|
|||
walletInstance->SetMaxVersion(nMaxVersion);
|
||||
}
|
||||
|
||||
// Upgrade to HD if explicit upgrade
|
||||
if (gArgs.GetBoolArg("-upgradewallet", false)) {
|
||||
LOCK(walletInstance->cs_wallet);
|
||||
|
||||
// Do not upgrade versions to any version between HD_SPLIT and FEATURE_PRE_SPLIT_KEYPOOL unless already supporting HD_SPLIT
|
||||
int max_version = walletInstance->nWalletVersion;
|
||||
if (!walletInstance->CanSupportFeature(FEATURE_HD_SPLIT) && max_version >=FEATURE_HD_SPLIT && max_version < FEATURE_PRE_SPLIT_KEYPOOL) {
|
||||
InitError(_("Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified."));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool hd_upgrade = false;
|
||||
bool split_upgrade = false;
|
||||
if (walletInstance->CanSupportFeature(FEATURE_HD) && !walletInstance->IsHDEnabled()) {
|
||||
LogPrintf("Upgrading wallet to HD\n");
|
||||
walletInstance->SetMinVersion(FEATURE_HD);
|
||||
|
||||
// generate a new master key
|
||||
CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey();
|
||||
if (!walletInstance->SetHDMasterKey(masterPubKey)) {
|
||||
throw std::runtime_error(std::string(__func__) + ": Storing master key failed");
|
||||
}
|
||||
hd_upgrade = true;
|
||||
}
|
||||
// Upgrade to HD chain split if necessary
|
||||
if (walletInstance->CanSupportFeature(FEATURE_HD_SPLIT)) {
|
||||
LogPrintf("Upgrading wallet to use HD chain split\n");
|
||||
walletInstance->SetMinVersion(FEATURE_PRE_SPLIT_KEYPOOL);
|
||||
split_upgrade = FEATURE_HD_SPLIT > prev_version;
|
||||
}
|
||||
// Mark all keys currently in the keypool as pre-split
|
||||
if (split_upgrade) {
|
||||
walletInstance->MarkPreSplitKeys();
|
||||
}
|
||||
// Regenerate the keypool if upgraded to HD
|
||||
if (hd_upgrade) {
|
||||
if (!walletInstance->TopUpKeyPool()) {
|
||||
InitError(_("Unable to generate keys") += "\n");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fFirstRun)
|
||||
{
|
||||
// ensure this wallet.dat can only be opened by clients supporting HD with chain split and expects no default key
|
||||
|
@ -4032,7 +4111,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path&
|
|||
InitError(strprintf(_("Error creating %s: You can't create non-HD wallets with this version."), walletFile));
|
||||
return nullptr;
|
||||
}
|
||||
walletInstance->SetMinVersion(FEATURE_NO_DEFAULT_KEY);
|
||||
walletInstance->SetMinVersion(FEATURE_LATEST);
|
||||
|
||||
// generate a new master key
|
||||
CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey();
|
||||
|
@ -4246,6 +4325,7 @@ CKeyPool::CKeyPool()
|
|||
{
|
||||
nTime = GetTime();
|
||||
fInternal = false;
|
||||
m_pre_split = false;
|
||||
}
|
||||
|
||||
CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn)
|
||||
|
@ -4253,6 +4333,7 @@ CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn)
|
|||
nTime = GetTime();
|
||||
vchPubKey = vchPubKeyIn;
|
||||
fInternal = internalIn;
|
||||
m_pre_split = false;
|
||||
}
|
||||
|
||||
CWalletKey::CWalletKey(int64_t nExpires)
|
||||
|
|
|
@ -89,7 +89,9 @@ enum WalletFeature
|
|||
|
||||
FEATURE_NO_DEFAULT_KEY = 159900, // Wallet without a default key written
|
||||
|
||||
FEATURE_LATEST = FEATURE_COMPRPUBKEY // HD is optional, use FEATURE_COMPRPUBKEY as latest version
|
||||
FEATURE_PRE_SPLIT_KEYPOOL = 169900, // Upgraded to HD SPLIT and can have a pre-split keypool
|
||||
|
||||
FEATURE_LATEST = FEATURE_PRE_SPLIT_KEYPOOL
|
||||
};
|
||||
|
||||
enum class OutputType {
|
||||
|
@ -119,6 +121,7 @@ public:
|
|||
int64_t nTime;
|
||||
CPubKey vchPubKey;
|
||||
bool fInternal; // for change outputs
|
||||
bool m_pre_split; // For keys generated before keypool split upgrade
|
||||
|
||||
CKeyPool();
|
||||
CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn);
|
||||
|
@ -141,9 +144,18 @@ public:
|
|||
(this will be the case for any wallet before the HD chain split version) */
|
||||
fInternal = false;
|
||||
}
|
||||
try {
|
||||
READWRITE(m_pre_split);
|
||||
}
|
||||
catch (std::ios_base::failure&) {
|
||||
/* flag as postsplit address if we can't read the m_pre_split boolean
|
||||
(this will be the case for any wallet that upgrades to HD chain split)*/
|
||||
m_pre_split = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
READWRITE(fInternal);
|
||||
READWRITE(m_pre_split);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -708,6 +720,7 @@ private:
|
|||
|
||||
std::set<int64_t> setInternalKeyPool;
|
||||
std::set<int64_t> setExternalKeyPool;
|
||||
std::set<int64_t> set_pre_split_keypool;
|
||||
int64_t m_max_keypool_index = 0;
|
||||
std::map<CKeyID, int64_t> m_pool_key_to_index;
|
||||
|
||||
|
@ -774,6 +787,7 @@ public:
|
|||
const std::string& GetName() const { return m_name; }
|
||||
|
||||
void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool);
|
||||
void MarkPreSplitKeys();
|
||||
|
||||
// Map from Key ID to key metadata.
|
||||
std::map<CKeyID, CKeyMetadata> mapKeyMetadata;
|
||||
|
@ -1127,6 +1141,9 @@ public:
|
|||
/* Generates a new HD master key (will not be activated) */
|
||||
CPubKey GenerateNewHDMasterKey();
|
||||
|
||||
/* Derives a new HD master key (will not be activated) */
|
||||
CPubKey DeriveNewMasterHDKey(const CKey& key);
|
||||
|
||||
/* Set the current HD master key (will reset the chain child index counters)
|
||||
Sets the master key's version based on the current wallet version (so the
|
||||
caller must ensure the current wallet version is correct before calling
|
||||
|
|
|
@ -11,6 +11,7 @@ from test_framework.test_framework import BitcoinTestFramework
|
|||
from test_framework.util import (
|
||||
assert_equal,
|
||||
connect_nodes_bi,
|
||||
assert_raises_rpc_error
|
||||
)
|
||||
|
||||
|
||||
|
@ -120,5 +121,39 @@ class WalletHDTest(BitcoinTestFramework):
|
|||
|
||||
assert_equal(keypath[0:7], "m/0'/1'")
|
||||
|
||||
# Generate a new HD seed on node 1 and make sure it is set
|
||||
orig_masterkeyid = self.nodes[1].getwalletinfo()['hdmasterkeyid']
|
||||
self.nodes[1].sethdseed()
|
||||
new_masterkeyid = self.nodes[1].getwalletinfo()['hdmasterkeyid']
|
||||
assert orig_masterkeyid != new_masterkeyid
|
||||
addr = self.nodes[1].getnewaddress()
|
||||
assert_equal(self.nodes[1].getaddressinfo(addr)['hdkeypath'], 'm/0\'/0\'/0\'') # Make sure the new address is the first from the keypool
|
||||
self.nodes[1].keypoolrefill(1) # Fill keypool with 1 key
|
||||
|
||||
# Set a new HD seed on node 1 without flushing the keypool
|
||||
new_seed = self.nodes[0].dumpprivkey(self.nodes[0].getnewaddress())
|
||||
orig_masterkeyid = new_masterkeyid
|
||||
self.nodes[1].sethdseed(False, new_seed)
|
||||
new_masterkeyid = self.nodes[1].getwalletinfo()['hdmasterkeyid']
|
||||
assert orig_masterkeyid != new_masterkeyid
|
||||
addr = self.nodes[1].getnewaddress()
|
||||
assert_equal(orig_masterkeyid, self.nodes[1].getaddressinfo(addr)['hdmasterkeyid'])
|
||||
assert_equal(self.nodes[1].getaddressinfo(addr)['hdkeypath'], 'm/0\'/0\'/1\'') # Make sure the new address continues previous keypool
|
||||
|
||||
# Check that the next address is from the new seed
|
||||
self.nodes[1].keypoolrefill(1)
|
||||
next_addr = self.nodes[1].getnewaddress()
|
||||
assert_equal(new_masterkeyid, self.nodes[1].getaddressinfo(next_addr)['hdmasterkeyid'])
|
||||
assert_equal(self.nodes[1].getaddressinfo(next_addr)['hdkeypath'], 'm/0\'/0\'/0\'') # Make sure the new address is not from previous keypool
|
||||
assert next_addr != addr
|
||||
|
||||
# Sethdseed parameter validity
|
||||
assert_raises_rpc_error(-1, 'sethdseed', self.nodes[0].sethdseed, False, new_seed, 0)
|
||||
assert_raises_rpc_error(-5, "Invalid private key", self.nodes[1].sethdseed, False, "not_wif")
|
||||
assert_raises_rpc_error(-1, "JSON value is not a boolean as expected", self.nodes[1].sethdseed, "Not_bool")
|
||||
assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[1].sethdseed, False, True)
|
||||
assert_raises_rpc_error(-5, "Already have this key", self.nodes[1].sethdseed, False, new_seed)
|
||||
assert_raises_rpc_error(-5, "Already have this key", self.nodes[1].sethdseed, False, self.nodes[1].dumpprivkey(self.nodes[1].getnewaddress()))
|
||||
|
||||
if __name__ == '__main__':
|
||||
WalletHDTest().main ()
|
||||
|
|
Loading…
Reference in a new issue