Derive transparent keys from mnemonic seed.

This commit is contained in:
Kris Nuttycombe 2021-10-18 19:47:28 -06:00
parent 666d135e2e
commit 2885ae7643
22 changed files with 270 additions and 137 deletions

View File

@ -25,8 +25,7 @@ static void ECDSA(benchmark::State& state)
mtx.nVersion = SAPLING_TX_VERSION;
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
CKey key;
key.MakeNewKey(false);
CKey key = CKey::TestOnlyRandomKey(false);
CBasicKeyStore keystore;
keystore.AddKeyPubKey(key, key.GetPubKey());
CKeyID hash = key.GetPubKey().GetID();

View File

@ -684,7 +684,7 @@ void ThreadImport(std::vector<fs::path> vImportFiles, const CChainParams& chainp
*/
bool InitSanityCheck(void)
{
if(!ECC_InitSanityCheck()) {
if(!CKey::ECC_InitSanityCheck()) {
InitError("Elliptic curve cryptography sanity check failure. Aborting.");
return false;
}

View File

@ -156,12 +156,14 @@ bool CKey::Check(const unsigned char *vch) {
return secp256k1_ec_seckey_verify(secp256k1_context_sign, vch);
}
void CKey::MakeNewKey(bool fCompressedIn) {
CKey CKey::TestOnlyRandomKey(bool fCompressedIn) {
CKey key;
do {
GetRandBytes(keydata.data(), keydata.size());
} while (!Check(keydata.data()));
fValid = true;
fCompressed = fCompressedIn;
GetRandBytes(key.keydata.data(), key.keydata.size());
} while (!Check(key.keydata.data()));
key.fValid = true;
key.fCompressed = fCompressedIn;
return key;
}
bool CKey::SetPrivKey(const CPrivKey &privkey, bool fCompressedIn) {
@ -330,9 +332,8 @@ void CExtKey::Decode(const unsigned char code[BIP32_EXTKEY_SIZE]) {
key.Set(code+42, code+BIP32_EXTKEY_SIZE, true);
}
bool ECC_InitSanityCheck() {
CKey key;
key.MakeNewKey(true);
bool CKey::ECC_InitSanityCheck() {
CKey key = CKey::TestOnlyRandomKey(true);
CPubKey pubkey = key.GetPubKey();
return key.VerifyPubKey(pubkey);
}

View File

@ -68,7 +68,17 @@ public:
keydata.resize(32);
}
static std::optional<CKey> FromEntropy(std::vector<unsigned char, secure_allocator<unsigned char>> keydata);
/**
* Construct a random key. This is used only for internal sanity checks;
* all keys that actually control live funds should be derived from the
* wallet's mnemonic seed.
*/
static CKey TestOnlyRandomKey(bool fCompressedIn);
static std::optional<CKey> FromEntropy(std::vector<unsigned char, secure_allocator<unsigned char>> keydata, bool fComporessedIn);
/** Check that required EC support is available at runtime. */
static bool ECC_InitSanityCheck();
friend bool operator==(const CKey& a, const CKey& b)
{
@ -106,9 +116,6 @@ public:
//! Initialize from a CPrivKey (serialized OpenSSL-format private key data).
bool SetPrivKey(const CPrivKey& vchPrivKey, bool fCompressed);
//! Generate a new private key using a cryptographic PRNG.
void MakeNewKey(bool fCompressed);
/**
* Convert the private key to a CPrivKey (serialized OpenSSL-format private key data).
* This is expensive.
@ -199,7 +206,5 @@ void ECC_Start(void);
/** Deinitialize the elliptic curve support. No-op if ECC_Start wasn't called first. */
void ECC_Stop(void);
/** Check that required EC support is available at runtime. */
bool ECC_InitSanityCheck(void);
#endif // BITCOIN_KEY_H

View File

@ -126,8 +126,7 @@ BOOST_DATA_TEST_CASE(DoS_mapOrphans, boost::unit_test::data::xrange(static_cast<
{
uint32_t consensusBranchId = NetworkUpgradeInfo[sample].nBranchId;
CKey key;
key.MakeNewKey(true);
CKey key = CKey::TestOnlyRandomKey(true);
CBasicKeyStore keystore;
keystore.AddKey(key);

View File

@ -49,7 +49,7 @@ BOOST_DATA_TEST_CASE(multisig_verify, boost::unit_test::data::xrange(static_cast
CKey key[4];
CAmount amount = 0;
for (int i = 0; i < 4; i++)
key[i].MakeNewKey(true);
key[i] = CKey::TestOnlyRandomKey(true);
CScript a_and_b;
a_and_b << OP_2 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << OP_2 << OP_CHECKMULTISIG;
@ -149,7 +149,7 @@ BOOST_AUTO_TEST_CASE(multisig_IsStandard)
{
CKey key[4];
for (int i = 0; i < 4; i++)
key[i].MakeNewKey(true);
key[i] = CKey::TestOnlyRandomKey(true);
txnouttype whichType;
@ -191,7 +191,7 @@ BOOST_DATA_TEST_CASE(multisig_Sign, boost::unit_test::data::xrange(static_cast<i
CKey key[4];
for (int i = 0; i < 4; i++)
{
key[i].MakeNewKey(true);
key[i] = CKey::TestOnlyRandomKey(true);
keystore.AddKey(key[i]);
}

View File

@ -14,7 +14,7 @@ BOOST_AUTO_TEST_CASE(basic_sanity)
{
BOOST_CHECK_MESSAGE(glibc_sanity_test() == true, "libc sanity test");
BOOST_CHECK_MESSAGE(glibcxx_sanity_test() == true, "stdlib sanity test");
BOOST_CHECK_MESSAGE(ECC_InitSanityCheck() == true, "ECC sanity test");
BOOST_CHECK_MESSAGE(CKey::ECC_InitSanityCheck() == true, "ECC sanity test");
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -67,7 +67,7 @@ BOOST_DATA_TEST_CASE(sign, boost::unit_test::data::xrange(static_cast<int>(Conse
CKey key[4];
for (int i = 0; i < 4; i++)
{
key[i].MakeNewKey(true);
key[i] = CKey::TestOnlyRandomKey(true);
keystore.AddKey(key[i]);
}
@ -170,7 +170,7 @@ BOOST_DATA_TEST_CASE(set, boost::unit_test::data::xrange(static_cast<int>(Consen
std::vector<CPubKey> keys;
for (int i = 0; i < 4; i++)
{
key[i].MakeNewKey(true);
key[i] = CKey::TestOnlyRandomKey(true);
keystore.AddKey(key[i]);
keys.push_back(key[i].GetPubKey());
}
@ -282,7 +282,7 @@ BOOST_DATA_TEST_CASE(AreInputsStandard, boost::unit_test::data::xrange(static_ca
vector<CPubKey> keys;
for (int i = 0; i < 6; i++)
{
key[i].MakeNewKey(true);
key[i] = CKey::TestOnlyRandomKey(true);
keystore.AddKey(key[i]);
}
for (int i = 0; i < 3; i++)

View File

@ -22,7 +22,7 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success)
CKey keys[3];
CPubKey pubkeys[3];
for (int i = 0; i < 3; i++) {
keys[i].MakeNewKey(true);
keys[i] = CKey::TestOnlyRandomKey(true);
pubkeys[i] = keys[i].GetPubKey();
}
@ -103,9 +103,8 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success)
BOOST_AUTO_TEST_CASE(script_standard_Solver_failure)
{
CKey key;
CKey key = CKey::TestOnlyRandomKey(true);
CPubKey pubkey;
key.MakeNewKey(true);
pubkey = key.GetPubKey();
CScript s;
@ -165,9 +164,8 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_failure)
BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
{
CKey key;
CKey key = CKey::TestOnlyRandomKey(true);
CPubKey pubkey;
key.MakeNewKey(true);
pubkey = key.GetPubKey();
CScript s;
@ -221,7 +219,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
CKey keys[3];
CPubKey pubkeys[3];
for (int i = 0; i < 3; i++) {
keys[i].MakeNewKey(true);
keys[i] = CKey::TestOnlyRandomKey(true);
pubkeys[i] = keys[i].GetPubKey();
}
@ -297,7 +295,7 @@ BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_)
CKey keys[3];
CPubKey pubkeys[3];
for (int i = 0; i < 3; i++) {
keys[i].MakeNewKey(true);
keys[i] = CKey::TestOnlyRandomKey(true);
pubkeys[i] = keys[i].GetPubKey();
}
@ -343,12 +341,11 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine)
CKey keys[2];
CPubKey pubkeys[2];
for (int i = 0; i < 2; i++) {
keys[i].MakeNewKey(true);
keys[i] = CKey::TestOnlyRandomKey(true);
pubkeys[i] = keys[i].GetPubKey();
}
CKey uncompressedKey;
uncompressedKey.MakeNewKey(false);
CKey uncompressedKey = CKey::TestOnlyRandomKey(false);
CPubKey uncompressedPubkey = uncompressedKey.GetPubKey();
CScript scriptPubKey;

View File

@ -701,10 +701,9 @@ BOOST_DATA_TEST_CASE(script_CHECKMULTISIG12, boost::unit_test::data::xrange(stat
uint32_t consensusBranchId = NetworkUpgradeInfo[sample].nBranchId;
ScriptError err;
CKey key1, key2, key3;
key1.MakeNewKey(true);
key2.MakeNewKey(false);
key3.MakeNewKey(true);
CKey key1 = CKey::TestOnlyRandomKey(true);
CKey key2 = CKey::TestOnlyRandomKey(false);
CKey key3 = CKey::TestOnlyRandomKey(true);
CScript scriptPubKey12;
scriptPubKey12 << OP_1 << ToByteVector(key1.GetPubKey()) << ToByteVector(key2.GetPubKey()) << OP_2 << OP_CHECKMULTISIG;
@ -734,11 +733,10 @@ BOOST_DATA_TEST_CASE(script_CHECKMULTISIG23, boost::unit_test::data::xrange(stat
uint32_t consensusBranchId = NetworkUpgradeInfo[sample].nBranchId;
ScriptError err;
CKey key1, key2, key3, key4;
key1.MakeNewKey(true);
key2.MakeNewKey(false);
key3.MakeNewKey(true);
key4.MakeNewKey(false);
CKey key1 = CKey::TestOnlyRandomKey(true);
CKey key2 = CKey::TestOnlyRandomKey(false);
CKey key3 = CKey::TestOnlyRandomKey(true);
CKey key4 = CKey::TestOnlyRandomKey(false);
CScript scriptPubKey23;
scriptPubKey23 << OP_2 << ToByteVector(key1.GetPubKey()) << ToByteVector(key2.GetPubKey()) << ToByteVector(key3.GetPubKey()) << OP_3 << OP_CHECKMULTISIG;
@ -812,8 +810,7 @@ BOOST_DATA_TEST_CASE(script_combineSigs, boost::unit_test::data::xrange(static_c
vector<CPubKey> pubkeys;
for (int i = 0; i < 3; i++)
{
CKey key;
key.MakeNewKey(i%2 == 1);
CKey key = CKey::TestOnlyRandomKey(i%2 == 1);
keys.push_back(key);
pubkeys.push_back(key.GetPubKey());
keystore.AddKey(key);

View File

@ -47,8 +47,7 @@ BOOST_AUTO_TEST_CASE(GetSigOpCount)
std::vector<CPubKey> keys;
for (int i = 0; i < 3; i++)
{
CKey k;
k.MakeNewKey(true);
CKey k = CKey::TestOnlyRandomKey(true);
keys.push_back(k.GetPubKey());
}
CScript s2 = GetScriptForMultisig(1, keys);

View File

@ -130,7 +130,7 @@ TestingSetup::~TestingSetup()
TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST)
{
// Generate a 100-block chain:
coinbaseKey.MakeNewKey(true);
coinbaseKey = CKey::TestOnlyRandomKey(true);
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
for (int i = 0; i < COINBASE_MATURITY; i++)
{

View File

@ -270,7 +270,7 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
CKey key[4];
for (int i = 0; i < 4; i++)
{
key[i].MakeNewKey(i % 2);
key[i] = CKey::TestOnlyRandomKey(i % 2);
keystoreRet.AddKey(key[i]);
}
@ -639,8 +639,7 @@ BOOST_AUTO_TEST_CASE(test_big_overwinter_transaction) {
mtx.nVersion = OVERWINTER_TX_VERSION;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
CKey key;
key.MakeNewKey(false);
CKey key = CKey::TestOnlyRandomKey(false);
CBasicKeyStore keystore;
keystore.AddKeyPubKey(key, key.GetPubKey());
CKeyID hash = key.GetPubKey().GetID();
@ -731,8 +730,7 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
t.vin[0].scriptSig << std::vector<unsigned char>(65, 0);
t.vout.resize(1);
t.vout[0].nValue = 90*CENT;
CKey key;
key.MakeNewKey(true);
CKey key = CKey::TestOnlyRandomKey(true);
t.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
string reason;
@ -826,8 +824,7 @@ BOOST_AUTO_TEST_CASE(test_IsStandardV2)
t.vin[0].scriptSig << std::vector<unsigned char>(65, 0);
t.vout.resize(1);
t.vout[0].nValue = 90*CENT;
CKey key;
key.MakeNewKey(true);
CKey key = CKey::TestOnlyRandomKey(true);
t.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
string reason;

View File

@ -865,15 +865,26 @@ bool AsyncRPCOperation_sendmany::find_utxos(bool fAcceptCoinbase, TxValues& txVa
return t_inputs_.size() > 0;
}
/**
* Compute a dust threshold based upon a standard p2pkh txout.
*/
CAmount DefaultDustThreshold(const CFeeRate& minRelayTxFee) {
// Use the all-zeros seed, we're only constructing a key in order
// to get a txout from which we can obtain the dust threshold.
// Master key generation results in a compressed key.
RawHDSeed seed(64, 0x00);
CKey secret = CExtKey::Master(seed.data(), 64).key;
CScript scriptPubKey = GetScriptForDestination(secret.GetPubKey().GetID());
CTxOut txout(CAmount(1), scriptPubKey);
return txout.GetDustThreshold(minRelayTxFee);
}
bool AsyncRPCOperation_sendmany::load_inputs(TxValues& txValues) {
// If from address is a taddr, select UTXOs to spend
CAmount selectedUTXOAmount = 0;
// Get dust threshold
CKey secret;
secret.MakeNewKey(true);
CScript scriptPubKey = GetScriptForDestination(secret.GetPubKey().GetID());
CTxOut out(CAmount(1), scriptPubKey);
CAmount dustThreshold = out.GetDustThreshold(minRelayTxFee);
CAmount dustThreshold = DefaultDustThreshold(minRelayTxFee);
CAmount dustChange = -1;
std::vector<COutput> selectedTInputs;

View File

@ -167,10 +167,10 @@ UniValue getnewaddress(const UniValue& params, bool fHelp)
pwalletMain->TopUpKeyPool();
// Generate a new key that is added to wallet
CPubKey newKey;
if (!pwalletMain->GetKeyFromPool(newKey))
std::optional<CPubKey> newKey = pwalletMain->GetKeyFromPool();
if (!newKey.has_value())
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
CKeyID keyID = newKey.GetID();
CKeyID keyID = newKey.value().GetID();
std::string dummy_account;
pwalletMain->SetAddressBook(keyID, dummy_account, "receive");

View File

@ -134,7 +134,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet)
LOCK2(cs_main, pwalletMain->cs_wallet);
CPubKey demoPubkey = pwalletMain->GenerateNewKey();
CPubKey demoPubkey = pwalletMain->GenerateNewKey().value();
CTxDestination demoAddress(CTxDestination(demoPubkey.GetID()));
UniValue retValue;
string strPurpose = "receive";
@ -143,7 +143,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet)
pwalletMain->SetAddressBook(demoPubkey.GetID(), "", strPurpose);
});
CPubKey setaccountDemoPubkey = pwalletMain->GenerateNewKey();
CPubKey setaccountDemoPubkey = pwalletMain->GenerateNewKey().value();
CTxDestination setaccountDemoAddress(CTxDestination(setaccountDemoPubkey.GetID()));
/*********************************
@ -1469,7 +1469,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_taddr_to_sapling)
KeyIO keyIO(Params());
// add keys manually
auto taddr = pwalletMain->GenerateNewKey().GetID();
auto taddr = pwalletMain->GenerateNewKey().value().GetID();
std::string taddr1 = keyIO.EncodeDestination(taddr);
auto pa = DefaultSaplingAddress(pwalletMain);
std::string zaddr1 = keyIO.EncodePaymentAddress(pa);
@ -2173,7 +2173,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_internals)
void TestWTxStatus(const Consensus::Params consensusParams, const int delta) {
auto AddTrx = [&consensusParams]() {
auto taddr = pwalletMain->GenerateNewKey().GetID();
auto taddr = pwalletMain->GenerateNewKey().value().GetID();
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(consensusParams, 1);
CScript scriptPubKey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(taddr) << OP_EQUALVERIFY << OP_CHECKSIG;
mtx.vout.push_back(CTxOut(5 * COIN, scriptPubKey));

View File

@ -245,30 +245,50 @@ bool CWallet::AddSproutZKey(const libzcash::SproutSpendingKey &key)
return true;
}
CPubKey CWallet::GenerateNewKey()
std::optional<CPubKey> CWallet::GenerateNewKey()
{
AssertLockHeld(cs_wallet); // mapKeyMetadata
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
auto seedOpt = GetMnemonicSeed();
CHDChain& hdChain = mnemonicHDChain.value();
if (seedOpt.has_value()) {
// All mnemonic seeds are checked at construction to ensure that we can obtain
// a valid spending key for the account ZCASH_LEGACY_TRANSPARENT_ACCOUNT;
// therefore, the `value()` call here is safe.
BIP32AccountChains accountChains = BIP32AccountChains::ForAccount(
seedOpt.value(),
BIP44CoinType(),
ZCASH_LEGACY_TRANSPARENT_ACCOUNT).value();
CKey secret;
secret.MakeNewKey(fCompressed);
while (true) {
auto extKey = accountChains.DeriveExternal(hdChain.GetLegacyTKeyCounter());
hdChain.IncrementLegacyTKeyCounter();
// Compressed public keys were introduced in version 0.6.0
if (fCompressed)
SetMinVersion(FEATURE_COMPRPUBKEY);
// if we did not successfully generate a key, try again.
if (extKey.has_value()) {
CKey secret = extKey.value().first.key;
CPubKey pubkey = secret.GetPubKey();
assert(secret.VerifyPubKey(pubkey));
CPubKey pubkey = secret.GetPubKey();
assert(secret.VerifyPubKey(pubkey));
// Create new metadata
const CKeyMetadata& keyMeta = extKey.value().second;
mapKeyMetadata[pubkey.GetID()] = keyMeta;
if (!nTimeFirstKey || keyMeta.nCreateTime < nTimeFirstKey)
nTimeFirstKey = keyMeta.nCreateTime;
// Create new metadata
int64_t nCreationTime = GetTime();
mapKeyMetadata[pubkey.GetID()] = CKeyMetadata(nCreationTime);
if (!nTimeFirstKey || nCreationTime < nTimeFirstKey)
nTimeFirstKey = nCreationTime;
if (!AddKeyPubKey(secret, pubkey))
throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed");
if (!AddKeyPubKey(secret, pubkey))
throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed");
return pubkey;
// Update the persisted chain information
if (fFileBacked && !CWalletDB(strWalletFile).WriteMnemonicHDChain(hdChain)) {
throw std::runtime_error("CWallet::GenerateNewKey(): Writing HD chain model failed");
}
return pubkey;
}
}
} else {
return std::nullopt;
}
}
bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey)
@ -375,9 +395,9 @@ UnifiedSpendingKey CWallet::GenerateNewUnifiedSpendingKey() {
throw std::runtime_error(std::string(__func__) + ": Wallet has no mnemonic HD seed. Please upgrade this wallet.");
}
auto hdChain = GetMnemonicHDChain().value();
CHDChain& hdChain = mnemonicHDChain.value();
while (true) {
auto usk = UnifiedSpendingKey::Derive(seed.value(), BIP44CoinType(), hdChain.GetAccountCounter());
auto usk = UnifiedSpendingKey::ForAccount(seed.value(), BIP44CoinType(), hdChain.GetAccountCounter());
hdChain.IncrementAccountCounter();
if (usk.has_value()) {
@ -397,7 +417,7 @@ std::optional<libzcash::UnifiedSpendingKey> CWallet::GetUnifiedSpendingKeyForAcc
auto seed = GetMnemonicSeed();
assert(seed.has_value());
// TODO: is there any reason to cache and not re-derive this every time?
auto usk = UnifiedSpendingKey::Derive(seed.value(), BIP44CoinType(), accountId);
auto usk = UnifiedSpendingKey::ForAccount(seed.value(), BIP44CoinType(), accountId);
if (usk.has_value()) {
return usk.value().first;
} else {
@ -4185,7 +4205,10 @@ bool CWallet::NewKeyPool()
for (int i = 0; i < nKeys; i++)
{
int64_t nIndex = i+1;
walletdb.WritePool(nIndex, CKeyPool(GenerateNewKey()));
auto key = GenerateNewKey();
if (!key.has_value())
return false; // should have been caught by the `IsLocked` call.
walletdb.WritePool(nIndex, CKeyPool(key.value()));
setKeyPool.insert(nIndex);
}
LogPrintf("CWallet::NewKeyPool wrote %d new keys\n", nKeys);
@ -4215,7 +4238,8 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
int64_t nEnd = 1;
if (!setKeyPool.empty())
nEnd = *(--setKeyPool.end()) + 1;
if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey())))
auto newKey = GenerateNewKey();
if (!newKey.has_value() || !walletdb.WritePool(nEnd, CKeyPool(newKey.value())))
throw runtime_error("TopUpKeyPool(): writing generated key failed");
setKeyPool.insert(nEnd);
LogPrintf("keypool added key %d, size=%u\n", nEnd, setKeyPool.size());
@ -4272,7 +4296,7 @@ void CWallet::ReturnKey(int64_t nIndex)
LogPrintf("keypool return %d\n", nIndex);
}
bool CWallet::GetKeyFromPool(CPubKey& result)
std::optional<CPubKey> CWallet::GetKeyFromPool()
{
int64_t nIndex = 0;
CKeyPool keypool;
@ -4281,14 +4305,12 @@ bool CWallet::GetKeyFromPool(CPubKey& result)
ReserveKeyFromKeyPool(nIndex, keypool);
if (nIndex == -1)
{
if (IsLocked()) return false;
result = GenerateNewKey();
return true;
if (IsLocked()) return std::nullopt;
return GenerateNewKey();
}
KeepKey(nIndex);
result = keypool.vchPubKey;
return keypool.vchPubKey;
}
return true;
}
int64_t CWallet::GetOldestKeyPoolTime()
@ -4858,9 +4880,9 @@ bool CWallet::InitLoadWallet(const CChainParams& params, bool clearWitnessCaches
if (fFirstRun)
{
// Create new keyUser and set as default key
CPubKey newDefaultKey;
if (walletInstance->GetKeyFromPool(newDefaultKey)) {
walletInstance->SetDefaultKey(newDefaultKey);
std::optional<CPubKey> newDefaultKey = walletInstance->GetKeyFromPool();
if (newDefaultKey.has_value()) {
walletInstance->SetDefaultKey(newDefaultKey.value());
if (!walletInstance->SetAddressBook(walletInstance->vchDefaultKey.GetID(), "", "receive"))
return UIError(_("Cannot write default address") += "\n");
}

View File

@ -993,7 +993,7 @@ public:
* keystore implementation
* Generate a new key
*/
CPubKey GenerateNewKey();
std::optional<CPubKey> GenerateNewKey();
//! Adds a key to the store, and saves it to disk.
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey);
//! Adds a key to the store, without saving it to disk (used by LoadWallet)
@ -1161,7 +1161,7 @@ public:
void ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool);
void KeepKey(int64_t nIndex);
void ReturnKey(int64_t nIndex);
bool GetKeyFromPool(CPubKey &key);
std::optional<CPubKey> GetKeyFromPool();
int64_t GetOldestKeyPoolTime();
void GetAllReserveKeys(std::set<CKeyID>& setAddress) const;
@ -1318,7 +1318,7 @@ public:
/* Set the metadata for the mnemonic HD seed (chain child index counters) */
void SetMnemonicHDChain(const CHDChain& chain, bool memonly);
const std::optional<CHDChain> GetMnemonicHDChain() const { return mnemonicHDChain; }
const std::optional<CHDChain>& GetMnemonicHDChain() const { return mnemonicHDChain; }
/* Set the metadata for the legacy HD seed (chain child index counters) */
void SetLegacyHDChain(const CHDChain& chain, bool memonly);

View File

@ -49,6 +49,7 @@ private:
uint256 seedFp;
int64_t nCreateTime; // 0 means unknown
uint32_t accountCounter;
uint32_t legacyTKeyCounter;
CHDChain() { SetNull(); }
@ -58,12 +59,13 @@ private:
seedFp.SetNull();
nCreateTime = 0;
accountCounter = 0;
legacyTKeyCounter = 0;
}
public:
static const int VERSION_HD_BASE = 1;
static const int CURRENT_VERSION = VERSION_HD_BASE;
CHDChain(uint256 seedFpIn, int64_t nCreateTimeIn): nVersion(CHDChain::CURRENT_VERSION), seedFp(seedFpIn), nCreateTime(nCreateTimeIn), accountCounter(0) {}
CHDChain(uint256 seedFpIn, int64_t nCreateTimeIn): nVersion(CHDChain::CURRENT_VERSION), seedFp(seedFpIn), nCreateTime(nCreateTimeIn), accountCounter(0), legacyTKeyCounter(0) {}
ADD_SERIALIZE_METHODS;
@ -74,6 +76,7 @@ public:
READWRITE(seedFp);
READWRITE(nCreateTime);
READWRITE(accountCounter);
READWRITE(legacyTKeyCounter);
}
template <typename Stream>
@ -91,8 +94,16 @@ public:
return accountCounter;
}
uint32_t IncrementAccountCounter() {
return ++accountCounter;
void IncrementAccountCounter() {
accountCounter += 1;
}
uint32_t GetLegacyTKeyCounter() {
return legacyTKeyCounter;
}
void IncrementLegacyTKeyCounter() {
legacyTKeyCounter += 1;
}
};

View File

@ -24,7 +24,7 @@ const libzcash::diversifier_index_t MAX_TRANSPARENT_CHILD_IDX(0x40000000);
MnemonicSeed MnemonicSeed::Random(uint32_t bip44CoinType, Language language, size_t entropyLen)
{
assert(entropyLen >= 32);
while (true) {
while (true) { // loop until we find usable entropy
std::vector<unsigned char> entropy(entropyLen, 0);
GetRandBytes(entropy.data(), entropyLen);
const char* phrase = zip339_entropy_to_phrase(language, entropy.data(), entropyLen);
@ -33,9 +33,9 @@ MnemonicSeed MnemonicSeed::Random(uint32_t bip44CoinType, Language language, siz
MnemonicSeed seed(language, mnemonic);
// Verify that the seed data is valid entropy for unified spending keys at
// account 0 and account 0x7FFFFFFE
if (libzcash::UnifiedSpendingKey::Derive(seed, bip44CoinType, 0).has_value() &&
libzcash::DeriveZip32TransparentSpendingKey(seed, bip44CoinType, ZCASH_LEGACY_TRANSPARENT_ACCOUNT).has_value()) {
// account 0 and at both the public & private chain levels for account 0x7FFFFFFE
if (libzcash::UnifiedSpendingKey::ForAccount(seed, bip44CoinType, 0).has_value() &&
libzcash::BIP32AccountChains::ForAccount(seed, bip44CoinType, ZCASH_LEGACY_TRANSPARENT_ACCOUNT).has_value()) {
return seed;
}
}
@ -68,6 +68,87 @@ uint256 ovkForShieldingFromTaddr(HDSeed& seed) {
namespace libzcash {
//
// Transparent
//
std::optional<std::pair<CExtKey, CKeyMetadata>> DeriveZip32TransparentAccountKey(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) {
auto rawSeed = seed.RawSeed();
auto m = CExtKey::Master(rawSeed.data(), rawSeed.size());
// We use a fixed keypath scheme of m/32'/coin_type'/account'
// Derive m/32'
auto m_32h = m.Derive(32 | ZIP32_HARDENED_KEY_LIMIT);
if (!m_32h.has_value()) return std::nullopt;
// Derive m/32'/coin_type'
auto m_32h_cth = m_32h.value().Derive(bip44CoinType | ZIP32_HARDENED_KEY_LIMIT);
if (!m_32h_cth.has_value()) return std::nullopt;
// Derive m/32'/coin_type'/account_id'
auto result = m_32h_cth.value().Derive(accountId | ZIP32_HARDENED_KEY_LIMIT);
if (!result.has_value()) return std::nullopt;
int64_t nCreationTime = GetTime();
auto keyMeta = CKeyMetadata(nCreationTime);
keyMeta.hdKeypath = "m/32'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'";
keyMeta.seedFp = seed.Fingerprint();
return std::make_pair(result.value(), keyMeta);
}
std::optional<BIP32AccountChains> BIP32AccountChains::ForAccount(
const HDSeed& seed,
uint32_t bip44CoinType,
uint32_t accountId) {
auto accountKeyOpt = DeriveZip32TransparentAccountKey(seed, bip44CoinType, accountId);
if (!accountKeyOpt.has_value()) return std::nullopt;
auto accountKey = accountKeyOpt.value();
auto external = accountKey.first.Derive(0);
auto internal = accountKey.first.Derive(1);
if (!(external.has_value() && internal.has_value())) return std::nullopt;
return BIP32AccountChains(seed.Fingerprint(), bip44CoinType, accountId, external.value(), internal.value());
}
std::optional<std::pair<CExtKey, CKeyMetadata>> BIP32AccountChains::DeriveExternal(uint32_t addrIndex) {
auto childKey = external.Derive(addrIndex);
if (!childKey.has_value()) return std::nullopt;
int64_t nCreationTime = GetTime();
auto keyMeta = CKeyMetadata(nCreationTime);
keyMeta.hdKeypath = "m/32'/"
+ std::to_string(bip44CoinType) + "'/"
+ std::to_string(accountId) + "'/"
+ "0/"
+ std::to_string(addrIndex);
keyMeta.seedFp = seedFp;
return std::make_pair(childKey.value(), keyMeta);
}
std::optional<std::pair<CExtKey, CKeyMetadata>> BIP32AccountChains::DeriveInternal(uint32_t addrIndex) {
auto childKey = internal.Derive(addrIndex);
if (!childKey.has_value()) return std::nullopt;
int64_t nCreationTime = GetTime();
auto keyMeta = CKeyMetadata(nCreationTime);
keyMeta.hdKeypath = "m/32'/"
+ std::to_string(bip44CoinType) + "'/"
+ std::to_string(accountId) + "'/"
+ "1/"
+ std::to_string(addrIndex);
keyMeta.seedFp = seedFp;
return std::make_pair(childKey.value(), keyMeta);
}
//
// Sapling
//
std::optional<SaplingExtendedFullViewingKey> SaplingExtendedFullViewingKey::Derive(uint32_t i) const
{
CDataStream ss_p(SER_NETWORK, PROTOCOL_VERSION);
@ -180,11 +261,11 @@ std::pair<SaplingExtendedSpendingKey, CKeyMetadata> SaplingExtendedSpendingKey::
// Create new metadata
int64_t nCreationTime = GetTime();
CKeyMetadata metadata(nCreationTime);
metadata.hdKeypath = "m/32'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'";
metadata.seedFp = seed.Fingerprint();
CKeyMetadata keyMeta(nCreationTime);
keyMeta.hdKeypath = "m/32'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(accountId) + "'";
keyMeta.seedFp = seed.Fingerprint();
return std::make_pair(xsk, metadata);
return std::make_pair(xsk, keyMeta);
}
SaplingExtendedFullViewingKey SaplingExtendedSpendingKey::ToXFVK() const
@ -199,30 +280,17 @@ SaplingExtendedFullViewingKey SaplingExtendedSpendingKey::ToXFVK() const
return ret;
}
std::optional<CExtKey> DeriveZip32TransparentSpendingKey(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) {
auto rawSeed = seed.RawSeed();
auto m = CExtKey::Master(rawSeed.data(), rawSeed.size());
//
// Unified
//
// We use a fixed keypath scheme of m/32'/coin_type'/account'
// Derive m/32'
auto m_32h = m.Derive(32 | ZIP32_HARDENED_KEY_LIMIT);
if (!m_32h.has_value()) return std::nullopt;
// Derive m/32'/coin_type'
auto m_32h_cth = m_32h.value().Derive(bip44CoinType | ZIP32_HARDENED_KEY_LIMIT);
if (!m_32h_cth.has_value()) return std::nullopt;
// Derive m/32'/coin_type'/account_id'
return m_32h_cth.value().Derive(accountId | ZIP32_HARDENED_KEY_LIMIT);
}
std::optional<std::pair<UnifiedSpendingKey, CKeyMetadata>> UnifiedSpendingKey::Derive(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) {
std::optional<std::pair<UnifiedSpendingKey, CKeyMetadata>> UnifiedSpendingKey::ForAccount(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) {
UnifiedSpendingKey usk;
usk.accountId = accountId;
auto transparentKey = DeriveZip32TransparentSpendingKey(seed, bip44CoinType, accountId);
auto transparentKey = DeriveZip32TransparentAccountKey(seed, bip44CoinType, accountId);
if (!transparentKey.has_value()) return std::nullopt;
usk.transparentKey = transparentKey.value();
usk.transparentKey = transparentKey.value().first;
auto saplingKey = SaplingExtendedSpendingKey::ForAccount(seed, bip44CoinType, accountId);
usk.saplingKey = saplingKey.first;
@ -248,7 +316,9 @@ std::optional<ZcashdUnifiedAddress> UnifiedFullViewingKey::Address(diversifier_i
ZcashdUnifiedAddress ua;
if (transparentKey.has_value()) {
// ensure that the diversifier index is small enough for a t-addr
if (MAX_TRANSPARENT_CHILD_IDX.less_than_le(j)) return std::nullopt;
CExtPubKey changeKey;
if (!transparentKey.value().Derive(changeKey, 0)) {
return std::nullopt;

View File

@ -351,7 +351,10 @@ private:
UnifiedSpendingKey() {}
public:
static std::optional<std::pair<UnifiedSpendingKey, CKeyMetadata>> Derive(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId);
static std::optional<std::pair<UnifiedSpendingKey, CKeyMetadata>> ForAccount(
const HDSeed& seed,
uint32_t bip44CoinType,
uint32_t accountId);
const std::optional<CExtKey>& GetTransparentKey() const {
return transparentKey;
@ -366,7 +369,30 @@ public:
std::optional<unsigned long> ParseZip32KeypathAccount(const std::string& keyPath);
std::optional<CExtKey> DeriveZip32TransparentSpendingKey(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId);
std::optional<std::pair<CExtKey, CKeyMetadata>> DeriveZip32TransparentMasterKey(
const HDSeed& seed,
uint32_t bip44CoinType,
uint32_t accountId);
class BIP32AccountChains {
private:
uint256 seedFp;
uint32_t accountId;
uint32_t bip44CoinType;
CExtKey external;
CExtKey internal;
BIP32AccountChains(uint256 seedFpIn, uint32_t bip44CoinTypeIn, uint32_t accountIdIn, CExtKey externalIn, CExtKey internalIn):
seedFp(seedFpIn), accountId(accountIdIn), bip44CoinType(bip44CoinTypeIn), external(externalIn), internal(internalIn) {}
public:
static std::optional<BIP32AccountChains> ForAccount(
const HDSeed& seed,
uint32_t bip44CoinType,
uint32_t accountId);
std::optional<std::pair<CExtKey, CKeyMetadata>> DeriveExternal(uint32_t addrIndex);
std::optional<std::pair<CExtKey, CKeyMetadata>> DeriveInternal(uint32_t addrIndex);
};
}

View File

@ -212,8 +212,7 @@ double benchmark_verify_equihash()
double benchmark_large_tx(size_t nInputs)
{
// Create priv/pub key
CKey priv;
priv.MakeNewKey(false);
CKey priv = CKey::TestOnlyRandomKey(true);
auto pub = priv.GetPubKey();
CBasicKeyStore tempKeystore;
tempKeystore.AddKey(priv);