Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/key.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ bool CExtKey::Derive(CExtKey &out, unsigned int _nChild) const {
return key.Derive(out.key, out.chaincode, _nChild, chaincode);
}

void CExtKey::SetMaster(const unsigned char *seed, unsigned int nSeedLen) {
void CExtKey::SetSeed(const unsigned char *seed, unsigned int nSeedLen) {
static const unsigned char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'};
std::vector<unsigned char, secure_allocator<unsigned char>> vout(64);
CHMAC_SHA512(hashkey, sizeof(hashkey)).Write(seed, nSeedLen).Finalize(vout.data());
Expand Down
2 changes: 1 addition & 1 deletion src/key.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ struct CExtKey {
void Decode(const unsigned char code[BIP32_EXTKEY_SIZE]);
bool Derive(CExtKey& out, unsigned int nChild) const;
CExtPubKey Neuter() const;
void SetMaster(const unsigned char* seed, unsigned int nSeedLen);
void SetSeed(const unsigned char *seed, unsigned int nSeedLen);
template <typename Stream>
void Serialize(Stream& s) const
{
Expand Down
4 changes: 2 additions & 2 deletions src/rpc/misc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ UniValue validateaddress(const JSONRPCRequest& request)
" \"account\" : \"account\" (string) DEPRECATED. The account associated with the address, \"\" is the default account\n"
" \"timestamp\" : timestamp, (number, optional) The creation time of the key if available in seconds since epoch (Jan 1 1970 GMT)\n"
" \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath if the key is HD and available\n"
" \"hdmasterkeyid\" : \"<hash160>\" (string, optional) The Hash160 of the HD master pubkey\n"
" \"hdseedid\" : \"<hash160>\" (string, optional) The Hash160 of the HD master pubkey\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")
Expand Down Expand Up @@ -287,7 +287,7 @@ UniValue validateaddress(const JSONRPCRequest& request)
ret.push_back(Pair("timestamp", it->second.nCreateTime));
if (!it->second.hdKeypath.empty()) {
ret.push_back(Pair("hdkeypath", it->second.hdKeypath));
ret.push_back(Pair("hdmasterkeyid", it->second.hdMasterKeyID.GetHex()));
ret.push_back(Pair("hdseedid", it->second.hd_seed_id.GetHex()));
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/bip32_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ void RunTest(const TestVector &test) {
std::vector<unsigned char> seed = ParseHex(test.strHexMaster);
CExtKey key;
CExtPubKey pubkey;
key.SetMaster(seed.data(), seed.size());
key.SetSeed(seed.data(), seed.size());
pubkey = key.Neuter();
for (const TestDerivation &derive : test.vDerive) {
unsigned char data[74];
Expand Down
14 changes: 7 additions & 7 deletions src/wallet/rpcdump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -656,13 +656,13 @@ UniValue dumpwallet(const JSONRPCRequest& request)
file << "\n";

// add the base58check encoded extended master if the wallet uses HD
CKeyID masterKeyID = pwallet->GetHDChain().masterKeyID;
if (!masterKeyID.IsNull())
CKeyID seed_id = pwallet->GetHDChain().seed_id;
if (!seed_id.IsNull())
{
CKey key;
if (pwallet->GetKey(masterKeyID, key)) {
if (pwallet->GetKey(seed_id, key)) {
CExtKey masterKey;
masterKey.SetMaster(key.begin(), key.size());
masterKey.SetSeed(key.begin(), key.size());

CRavenExtKey b58extkey;
b58extkey.SetKey(masterKey);
Expand All @@ -679,12 +679,12 @@ UniValue dumpwallet(const JSONRPCRequest& request)
file << strprintf("%s %s ", CRavenSecret(key).ToString(), strTime);
if (pwallet->mapAddressBook.count(keyid)) {
file << strprintf("label=%s", EncodeDumpString(pwallet->mapAddressBook[keyid].name));
} else if (keyid == masterKeyID) {
file << "hdmaster=1";
} else if (keyid == seed_id) {
file << "hdseed=1";
} else if (mapKeyPool.count(keyid)) {
file << "reserve=1";
} else if (pwallet->mapKeyMetadata[keyid].hdKeypath == "m") {
file << "inactivehdmaster=1";
file << "inactivehdseed=1";
} else {
file << "change=1";
}
Expand Down
10 changes: 5 additions & 5 deletions src/wallet/rpcwallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2586,7 +2586,7 @@ UniValue getwalletinfo(const JSONRPCRequest& request)
" \"keypoolsize_hd_internal\": xxxx, (numeric) how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)\n"
" \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n"
" \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n"
" \"hdmasterkeyid\": \"<hash160>\" (string) the Hash160 of the HD master pubkey\n"
" \"hdseedid\": \"<hash160>\" (string) the Hash160 of the HD master pubkey\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getwalletinfo", "")
Expand All @@ -2607,16 +2607,16 @@ UniValue getwalletinfo(const JSONRPCRequest& request)
obj.push_back(Pair("txcount", (int)pwallet->mapWallet.size()));
obj.push_back(Pair("keypoololdest", pwallet->GetOldestKeyPoolTime()));
obj.push_back(Pair("keypoolsize", (int64_t)kpExternalSize));
CKeyID masterKeyID = pwallet->GetHDChain().masterKeyID;
if (!masterKeyID.IsNull() && pwallet->CanSupportFeature(FEATURE_HD_SPLIT)) {
CKeyID seed_id = pwallet->GetHDChain().seed_id;
if (!seed_id.IsNull() && pwallet->CanSupportFeature(FEATURE_HD_SPLIT)) {
obj.push_back(Pair("keypoolsize_hd_internal", (int64_t)(pwallet->GetKeyPoolSize() - kpExternalSize)));
}
if (pwallet->IsCrypted()) {
obj.push_back(Pair("unlocked_until", pwallet->nRelockTime));
}
obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK())));
if (!masterKeyID.IsNull())
obj.push_back(Pair("hdmasterkeyid", masterKeyID.GetHex()));
if (!seed_id.IsNull())
obj.push_back(Pair("hdseedid", seed_id.GetHex()));
return obj;
}

Expand Down
45 changes: 25 additions & 20 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,17 +181,17 @@ CPubKey CWallet::GenerateNewKey(CWalletDB &walletdb, bool internal)
void CWallet::DeriveNewChildKey(CWalletDB &walletdb, CKeyMetadata& metadata, CKey& secret, bool internal)
{
// for now we use a fixed keypath scheme of m/0'/0'/k
CKey key; //master key seed (256bit)
CKey key; //seed (256bit)
CExtKey masterKey; //hd master key
CExtKey accountKey; //key at m/0'
CExtKey chainChildKey; //key at m/0'/0' (external) or m/0'/1' (internal)
CExtKey childKey; //key at m/0'/0'/<n>'

// try to get the master key
if (!GetKey(hdChain.masterKeyID, key))
if (!GetKey(hdChain.seed_id, key))
throw std::runtime_error(std::string(__func__) + ": Master key not found");

masterKey.SetMaster(key.begin(), key.size());
masterKey.SetSeed(key.begin(), key.size());

// derive m/0'
// use hardened derivation (child keys >= 0x80000000 are hardened after bip32)
Expand All @@ -218,7 +218,7 @@ void CWallet::DeriveNewChildKey(CWalletDB &walletdb, CKeyMetadata& metadata, CKe
}
} while (HaveKey(childKey.key.GetPubKey().GetID()));
secret = childKey.key;
metadata.hdMasterKeyID = hdChain.masterKeyID;
metadata.hd_seed_id = hdChain.seed_id;
// update the chain model in the database
if (!walletdb.WriteHDChain(hdChain))
throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed");
Expand Down Expand Up @@ -671,7 +671,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)

// if we are using HD, replace the HD master key (seed) with a new one
if (IsHDEnabled()) {
if (!SetHDMasterKey(GenerateNewHDMasterKey())) {
if (!SetHDSeed(GenerateNewSeed())) {
return false;
}
}
Expand Down Expand Up @@ -1389,45 +1389,50 @@ CAmount CWallet::GetChange(const CTransaction& tx) const
return nChange;
}

CPubKey CWallet::GenerateNewHDMasterKey()
CPubKey CWallet::GenerateNewSeed()
{
assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
CKey key;
key.MakeNewKey(true);
return DeriveNewSeed(key);
}

CPubKey CWallet::DeriveNewSeed(const CKey& key)
{
int64_t nCreationTime = GetTime();
CKeyMetadata metadata(nCreationTime);

// calculate the pubkey
CPubKey pubkey = key.GetPubKey();
assert(key.VerifyPubKey(pubkey));
// calculate the seed
CPubKey seed = key.GetPubKey();
assert(key.VerifyPubKey(seed));

// set the hd keypath to "m" -> Master, refers the masterkeyid to itself
metadata.hdKeypath = "m";
metadata.hdMasterKeyID = pubkey.GetID();
// set the hd keypath to "s" -> Seed, refers the seed to itself
metadata.hdKeypath = "s";
metadata.hd_seed_id = seed.GetID();

{
LOCK(cs_wallet);

// mem store the metadata
mapKeyMetadata[pubkey.GetID()] = metadata;
mapKeyMetadata[seed.GetID()] = metadata;

// write the key&metadata to the database
if (!AddKeyPubKey(key, pubkey))
if (!AddKeyPubKey(key, seed))
throw std::runtime_error(std::string(__func__) + ": AddKeyPubKey failed");
}

return pubkey;
return seed;
}

bool CWallet::SetHDMasterKey(const CPubKey& pubkey)
bool CWallet::SetHDSeed(const CPubKey &pubkey)
{
LOCK(cs_wallet);
// store the keyid (hash160) together with
// the child index counter in the database
// as a hdchain object
CHDChain newHdChain;
newHdChain.nVersion = CanSupportFeature(FEATURE_HD_SPLIT) ? CHDChain::VERSION_HD_CHAIN_SPLIT : CHDChain::VERSION_HD_BASE;
newHdChain.masterKeyID = pubkey.GetID();
newHdChain.seed_id = pubkey.GetID();
SetHDChain(newHdChain, false);

return true;
Expand All @@ -1445,7 +1450,7 @@ bool CWallet::SetHDChain(const CHDChain& chain, bool memonly)

bool CWallet::IsHDEnabled() const
{
return !hdChain.masterKeyID.IsNull();
return !hdChain.seed_id.IsNull();
}

int64_t CWalletTx::GetTxTime() const
Expand Down Expand Up @@ -3877,8 +3882,8 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
walletInstance->SetMinVersion(FEATURE_NO_DEFAULT_KEY);

// generate a new master key
CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey();
if (!walletInstance->SetHDMasterKey(masterPubKey))
CPubKey masterPubKey = walletInstance->GenerateNewSeed();
if (!walletInstance->SetHDSeed(masterPubKey))
throw std::runtime_error(std::string(__func__) + ": Storing master key failed");

// Top up the keypool
Expand Down
7 changes: 5 additions & 2 deletions src/wallet/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -1100,13 +1100,16 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface
bool IsHDEnabled() const;

/* Generates a new HD master key (will not be activated) */
CPubKey GenerateNewHDMasterKey();
CPubKey GenerateNewSeed();

/* Derives a new HD seed (will not be activated) */
CPubKey DeriveNewSeed(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
this function). */
bool SetHDMasterKey(const CPubKey& key);
bool SetHDSeed(const CPubKey &key);
};

/** A key allocated from the key pool. */
Expand Down
12 changes: 6 additions & 6 deletions src/wallet/walletdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class CHDChain
public:
uint32_t nExternalChainCounter;
uint32_t nInternalChainCounter;
CKeyID masterKeyID; //!< master key hash160
CKeyID seed_id; //!< master key hash160

static const int VERSION_HD_BASE = 1;
static const int VERSION_HD_CHAIN_SPLIT = 2;
Expand All @@ -77,7 +77,7 @@ class CHDChain
{
READWRITE(this->nVersion);
READWRITE(nExternalChainCounter);
READWRITE(masterKeyID);
READWRITE(seed_id);
if (this->nVersion >= VERSION_HD_CHAIN_SPLIT)
READWRITE(nInternalChainCounter);
}
Expand All @@ -87,7 +87,7 @@ class CHDChain
nVersion = CHDChain::CURRENT_VERSION;
nExternalChainCounter = 0;
nInternalChainCounter = 0;
masterKeyID.SetNull();
seed_id.SetNull();
}
};

Expand All @@ -100,7 +100,7 @@ class CKeyMetadata
int nVersion;
int64_t nCreateTime; // 0 means unknown
std::string hdKeypath; //optional HD/bip32 keypath
CKeyID hdMasterKeyID; //id of the HD masterkey used to derive this key
CKeyID hd_seed_id; //id of the HD masterkey used to derive this key

CKeyMetadata()
{
Expand All @@ -121,7 +121,7 @@ class CKeyMetadata
if (this->nVersion >= VERSION_WITH_HDDATA)
{
READWRITE(hdKeypath);
READWRITE(hdMasterKeyID);
READWRITE(hd_seed_id);
}
}

Expand All @@ -130,7 +130,7 @@ class CKeyMetadata
nVersion = CKeyMetadata::CURRENT_VERSION;
nCreateTime = 0;
hdKeypath.clear();
hdMasterKeyID.SetNull();
hd_seed_id.SetNull();
}
};

Expand Down
6 changes: 3 additions & 3 deletions test/functional/keypool.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def run_test(self):
addr_before_encrypting = nodes[0].getnewaddress()
addr_before_encrypting_data = nodes[0].validateaddress(addr_before_encrypting)
wallet_info_old = nodes[0].getwalletinfo()
assert(addr_before_encrypting_data['hdmasterkeyid'] == wallet_info_old['hdmasterkeyid'])
assert(addr_before_encrypting_data['hdseedid'] == wallet_info_old['hdseedid'])

# Encrypt wallet and wait to terminate
nodes[0].node_encrypt_wallet('test')
Expand All @@ -27,8 +27,8 @@ def run_test(self):
addr = nodes[0].getnewaddress()
addr_data = nodes[0].validateaddress(addr)
wallet_info = nodes[0].getwalletinfo()
assert(addr_before_encrypting_data['hdmasterkeyid'] != wallet_info['hdmasterkeyid'])
assert(addr_data['hdmasterkeyid'] == wallet_info['hdmasterkeyid'])
assert(addr_before_encrypting_data['hdseedid'] != wallet_info['hdseedid'])
assert(addr_data['hdseedid'] == wallet_info['hdseedid'])
assert_raises_rpc_error(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress)

# put six (plus 2) new keys in the keypool (100% external-, +100% internal-keys, 1 in min)
Expand Down
4 changes: 2 additions & 2 deletions test/functional/wallet-dump.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ def read_dump(file_name, addrs, hd_master_addr_old):
addr_keypath = comment.split(" addr=")[1]
addr = addr_keypath.split(" ")[0]
keypath = None
if keytype == "inactivehdmaster=1":
if keytype == "inactivehdseed=1":
# ensure the old master is still available
assert(hd_master_addr_old == addr)
elif keytype == "hdmaster=1":
elif keytype == "hdseed=1":
# ensure we have generated a new hd master key
assert(hd_master_addr_old != addr)
hd_master_addr_ret = addr
Expand Down
8 changes: 4 additions & 4 deletions test/functional/wallet-hd.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ def run_test (self):
self.start_node(1)
connect_nodes_bi(self.nodes, 0, 1)

# Make sure we use hd, keep masterkeyid
masterkeyid = self.nodes[1].getwalletinfo()['hdmasterkeyid']
# Make sure we use hd, keep seedid
masterkeyid = self.nodes[1].getwalletinfo()['hdseedid']
assert_equal(len(masterkeyid), 40)

# create an internal key
Expand All @@ -54,7 +54,7 @@ def run_test (self):
hd_add = self.nodes[1].getnewaddress()
hd_info = self.nodes[1].validateaddress(hd_add)
assert_equal(hd_info["hdkeypath"], "m/0'/0'/"+str(i)+"'")
assert_equal(hd_info["hdmasterkeyid"], masterkeyid)
assert_equal(hd_info["hdseedid"], masterkeyid)
self.nodes[0].sendtoaddress(hd_add, 1)
self.nodes[0].generate(1)
self.nodes[0].sendtoaddress(non_hd_add, 1)
Expand Down Expand Up @@ -83,7 +83,7 @@ def run_test (self):
hd_add_2 = self.nodes[1].getnewaddress()
hd_info_2 = self.nodes[1].validateaddress(hd_add_2)
assert_equal(hd_info_2["hdkeypath"], "m/0'/0'/"+str(_)+"'")
assert_equal(hd_info_2["hdmasterkeyid"], masterkeyid)
assert_equal(hd_info_2["hdseedid"], masterkeyid)
assert_equal(hd_add, hd_add_2)
connect_nodes_bi(self.nodes, 0, 1)
self.sync_all()
Expand Down