[Wallet] Add simplest BIP32/deterministic key generation implementation
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user