[Wallet] Add simplest BIP32/deterministic key generation implementation

This commit is contained in:
janko33bd
2017-08-30 22:35:59 +01:00
parent 96b46824b5
commit 17f0b66a8e
5 changed files with 165 additions and 4 deletions

View File

@@ -410,6 +410,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-sendfreetransactions", strprintf(_("Send transactions as zero-fee transactions if possible (default: %u)"), DEFAULT_SEND_FREE_TRANSACTIONS));
strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE));
strUsage += HelpMessageOpt("-txconfirmtarget=<n>", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET));
strUsage += HelpMessageOpt("-usehd", _("Use hierarchical deterministic key generation (HD) after bip32. Only has effect during wallet creation/first start") + " " + strprintf(_("(default: %u)"), true));
strUsage += HelpMessageOpt("-maxtxfee=<amt>", strprintf(_("Maximum total fees (in %s) to use in a single wallet transaction; setting this too low may abort large transactions (default: %s)"),
CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE)));
strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup"));
@@ -1538,6 +1539,14 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// Create new keyUser and set as default key
RandAddSeedPerfmon();
// Create new keyUser and set as default key
if (GetBoolArg("-usehd", true)) {
// generate a new master key
CKey key;
key.MakeNewKey(true);
if (!pwalletMain->SetHDMasterKey(key))
throw std::runtime_error("CWallet::GenerateNewKey(): Storing master key failed");
}
CPubKey newDefaultKey;
if (pwalletMain->GetKeyFromPool(newDefaultKey)) {
pwalletMain->SetDefaultKey(newDefaultKey);

View File

@@ -94,7 +94,48 @@ CPubKey CWallet::GenerateNewKey()
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
CKey secret;
secret.MakeNewKey(fCompressed);
// Create new metadata
int64_t nCreationTime = GetTime();
CKeyMetadata metadata(nCreationTime);
// use HD key derivation if HD was enabled during wallet creation
if (!hdChain.masterKeyID.IsNull()) {
// for now we use a fixed keypath scheme of m/0'/0'/k
CKey key; //master key seed (256bit)
CExtKey masterKey; //hd master key
CExtKey accountKey; //key at m/0'
CExtKey externalChainChildKey; //key at m/0'/0'
CExtKey childKey; //key at m/0'/0'/<n>'
// try to get the master key
if (!GetKey(hdChain.masterKeyID, key))
throw std::runtime_error("CWallet::GenerateNewKey(): Master key not found");
masterKey.SetMaster(key.begin(), key.size());
// derive m/0'
// use hardened derivation (child keys > 0x80000000 are hardened after bip32)
masterKey.Derive(accountKey, 0 | 0x80000000);
// derive m/0'/0'
accountKey.Derive(externalChainChildKey, 0 | 0x80000000);
// derive child key at next index, skip keys already known to the wallet
do
{
externalChainChildKey.Derive(childKey, hdChain.nExternalChainCounter | 0x80000000);
// increment childkey index
hdChain.nExternalChainCounter++;
} while(HaveKey(childKey.key.GetPubKey().GetID()));
secret = childKey.key;
// update the chain model in the database
if (!CWalletDB(strWalletFile).WriteHDChain(hdChain))
throw std::runtime_error("CWallet::GenerateNewKey(): Writing HD chain model failed");
} else {
secret.MakeNewKey(fCompressed);
}
// Compressed public keys were introduced in version 0.6.0
if (fCompressed)
@@ -104,8 +145,7 @@ CPubKey CWallet::GenerateNewKey()
assert(secret.VerifyPubKey(pubkey));
// Create new metadata
int64_t nCreationTime = GetTime();
mapKeyMetadata[pubkey.GetID()] = CKeyMetadata(nCreationTime);
mapKeyMetadata[pubkey.GetID()] = metadata;
if (!nTimeFirstKey || nCreationTime < nTimeFirstKey)
nTimeFirstKey = nCreationTime;
@@ -1333,6 +1373,37 @@ CAmount CWallet::GetChange(const CTxOut& txout) const
return (IsChange(txout) ? txout.nValue : 0);
}
bool CWallet::SetHDMasterKey(const CKey& key)
{
LOCK(cs_wallet);
// store the key as normal "key"/"ckey" object
// in the database
// key metadata is not required
CPubKey pubkey = key.GetPubKey();
if (!AddKeyPubKey(key, pubkey))
throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed");
// store the keyid (hash160) together with
// the child index counter in the database
// as a hdchain object
CHDChain newHdChain;
newHdChain.masterKeyID = pubkey.GetID();
SetHDChain(newHdChain, false);
return true;
}
bool CWallet::SetHDChain(const CHDChain& chain, bool memonly)
{
LOCK(cs_wallet);
if (!memonly && !CWalletDB(strWalletFile).WriteHDChain(chain))
throw runtime_error("AddHDChain(): writing chain failed");
hdChain = chain;
return true;
}
bool CWallet::IsMine(const CTransaction& tx) const
{
BOOST_FOREACH(const CTxOut& txout, tx.vout)

View File

@@ -85,6 +85,35 @@ enum WalletFeature
};
/* simple hd chain data model */
class CHDChain
{
public:
uint32_t nExternalChainCounter;
CKeyID masterKeyID; //!< master key hash160
static const int CURRENT_VERSION = 1;
int nVersion;
CHDChain() { SetNull(); }
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
{
READWRITE(this->nVersion);
nVersion = this->nVersion;
READWRITE(nExternalChainCounter);
READWRITE(masterKeyID);
}
void SetNull()
{
nVersion = CHDChain::CURRENT_VERSION;
nExternalChainCounter = 0;
masterKeyID.SetNull();
}
};
/** A key pool entry */
class CKeyPool
{
@@ -509,6 +538,9 @@ private:
void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>);
/* the hd chain data model (external chain counters) */
CHDChain hdChain;
public:
/*
* Main wallet lock.
@@ -814,6 +846,11 @@ public:
bool SelectCoinsForStaking(CAmount& nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const;
void AvailableCoinsForStaking(std::vector<COutput>& vCoins) const;
uint64_t GetStakeWeight() const;
/* Set the hd chain model (chain child index counters) */
bool SetHDChain(const CHDChain& chain, bool memonly);
/* Set the current hd master key (will reset the chain child index counters) */
bool SetHDMasterKey(const CKey& key);
};
/** A key allocated from the key pool. */

View File

@@ -598,6 +598,16 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
return false;
}
}
else if (strType == "hdchain")
{
CHDChain chain;
ssValue >> chain;
if (!pwallet->SetHDChain(chain, true))
{
strErr = "Error reading wallet database: SetHDChain failed";
return false;
}
}
} catch (...)
{
return false;
@@ -998,6 +1008,12 @@ bool CWalletDB::WriteDestData(const std::string &address, const std::string &key
return Write(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value);
}
bool CWalletDB::WriteHDChain(const CHDChain& chain)
{
nWalletDBUpdated++;
return Write(std::string("hdchain"), chain);
}
bool CWalletDB::EraseDestData(const std::string &address, const std::string &key)
{
nWalletDBUpdated++;

View File

@@ -46,6 +46,8 @@ public:
static const int CURRENT_VERSION=1;
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
CKeyMetadata()
{
@@ -73,6 +75,32 @@ public:
}
};
/* simple hd chain data model */
class CHDChain
{
public:
uint32_t nExternalChainCounter;
CKeyID masterKeyID; //!< master key hash160
static const int CURRENT_VERSION = 1;
int nVersion;
CHDChain() { SetNull(); }
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
{
READWRITE(this->nVersion);
nVersion = this->nVersion;
READWRITE(nExternalChainCounter);
READWRITE(masterKeyID);
}
void SetNull()
{
nVersion = CHDChain::CURRENT_VERSION;
nExternalChainCounter = 0;
masterKeyID.SetNull();
}
};
/** Access to the wallet database (wallet.dat) */
class CWalletDB : public CDB
{
@@ -120,9 +148,9 @@ public:
/// Write destination data key,value tuple to database
bool WriteDestData(const std::string &address, const std::string &key, const std::string &value);
bool WriteHDChain(const CHDChain& chain);
/// Erase destination data tuple from wallet database
bool EraseDestData(const std::string &address, const std::string &key);
CAmount GetAccountCreditDebit(const std::string& strAccount);
void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& acentries);