From 82ae02b1656819f4bd5023b8955447e1d4ea8692 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Thu, 11 Jul 2019 18:21:21 -0400 Subject: Be able to create new wallets with DescriptorScriptPubKeyMans as backing --- src/wallet/rpcwallet.cpp | 6 ++++- src/wallet/wallet.cpp | 59 +++++++++++++++++++++++++++++++++++++++++------- src/wallet/wallet.h | 3 +++ 3 files changed, 59 insertions(+), 9 deletions(-) (limited to 'src/wallet') diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index c5aca12f76..50aee04d8e 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2706,6 +2706,7 @@ static UniValue createwallet(const JSONRPCRequest& request) {"blank", RPCArg::Type::BOOL, /* default */ "false", "Create a blank wallet. A blank wallet has no keys or HD seed. One can be set using sethdseed."}, {"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Encrypt the wallet with this passphrase."}, {"avoid_reuse", RPCArg::Type::BOOL, /* default */ "false", "Keep track of coin reuse, and treat dirty and clean coins differently with privacy considerations in mind."}, + {"descriptors", RPCArg::Type::BOOL, /* default */ "false", "Create a native descriptor wallet. The wallet will use descriptors internally to handle address creation"}, }, RPCResult{ RPCResult::Type::OBJ, "", "", @@ -2742,6 +2743,9 @@ static UniValue createwallet(const JSONRPCRequest& request) if (!request.params[4].isNull() && request.params[4].get_bool()) { flags |= WALLET_FLAG_AVOID_REUSE; } + if (!request.params[5].isNull() && request.params[5].get_bool()) { + flags |= WALLET_FLAG_DESCRIPTORS; + } std::string error; std::shared_ptr wallet; @@ -4298,7 +4302,7 @@ static const CRPCCommand commands[] = { "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","label","address_type"} }, { "wallet", "backupwallet", &backupwallet, {"destination"} }, { "wallet", "bumpfee", &bumpfee, {"txid", "options"} }, - { "wallet", "createwallet", &createwallet, {"wallet_name", "disable_private_keys", "blank", "passphrase", "avoid_reuse"} }, + { "wallet", "createwallet", &createwallet, {"wallet_name", "disable_private_keys", "blank", "passphrase", "avoid_reuse", "descriptors"} }, { "wallet", "dumpprivkey", &dumpprivkey, {"address"} }, { "wallet", "dumpwallet", &dumpwallet, {"filename"} }, { "wallet", "encryptwallet", &encryptwallet, {"passphrase"} }, diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 90507a9415..bef470bd8d 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -228,10 +228,14 @@ WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& // Set a seed for the wallet { LOCK(wallet->cs_wallet); - for (auto spk_man : wallet->GetActiveScriptPubKeyMans()) { - if (!spk_man->SetupGeneration()) { - error = "Unable to generate initial keys"; - return WalletCreationStatus::CREATION_FAILED; + if (wallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) { + wallet->SetupDescriptorScriptPubKeyMans(); + } else { + for (auto spk_man : wallet->GetActiveScriptPubKeyMans()) { + if (!spk_man->SetupGeneration()) { + error = "Unable to generate initial keys"; + return WalletCreationStatus::CREATION_FAILED; + } } } } @@ -3783,10 +3787,16 @@ std::shared_ptr CWallet::CreateWalletFromFile(interfaces::Chain& chain, if (!(wallet_creation_flags & (WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET))) { LOCK(walletInstance->cs_wallet); - for (auto spk_man : walletInstance->GetActiveScriptPubKeyMans()) { - if (!spk_man->SetupGeneration()) { - error = _("Unable to generate initial keys").translated; - return nullptr; + if (walletInstance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) { + walletInstance->SetupDescriptorScriptPubKeyMans(); + // SetupDescriptorScriptPubKeyMans already calls SetupGeneration for us so we don't need to call SetupGeneration separately + } else { + // Legacy wallets need SetupGeneration here. + for (auto spk_man : walletInstance->GetActiveScriptPubKeyMans()) { + if (!spk_man->SetupGeneration()) { + error = _("Unable to generate initial keys").translated; + return nullptr; + } } } } @@ -4351,6 +4361,39 @@ void CWallet::LoadDescriptorScriptPubKeyMan(uint256 id, WalletDescriptor& desc) m_spk_managers[id] = std::move(spk_manager); } +void CWallet::SetupDescriptorScriptPubKeyMans() +{ + AssertLockHeld(cs_wallet); + + // Make a seed + CKey seed_key; + seed_key.MakeNewKey(true); + CPubKey seed = seed_key.GetPubKey(); + assert(seed_key.VerifyPubKey(seed)); + + // Get the extended key + CExtKey master_key; + master_key.SetSeed(seed_key.begin(), seed_key.size()); + + for (bool internal : {false, true}) { + for (OutputType t : OUTPUT_TYPES) { + auto spk_manager = std::unique_ptr(new DescriptorScriptPubKeyMan(*this, t, internal)); + if (IsCrypted()) { + if (IsLocked()) { + throw std::runtime_error(std::string(__func__) + ": Wallet is locked, cannot setup new descriptors"); + } + if (!spk_manager->CheckDecryptionKey(vMasterKey) && !spk_manager->Encrypt(vMasterKey, nullptr)) { + throw std::runtime_error(std::string(__func__) + ": Could not encrypt new descriptors"); + } + } + spk_manager->SetupDescriptorGeneration(master_key); + uint256 id = spk_manager->GetID(); + m_spk_managers[id] = std::move(spk_manager); + SetActiveScriptPubKeyMan(id, t, internal); + } + } +} + void CWallet::SetActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal, bool memonly) { auto& spk_mans = internal ? m_internal_spk_managers : m_external_spk_managers; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index c7d5e66c0a..29c6bced95 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1244,6 +1244,9 @@ public: //! @param[in] internal Whether this ScriptPubKeyMan provides change addresses //! @param[in] memonly Whether to record this update to the database. Set to true for wallet loading, normally false when actually updating the wallet. void SetActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal, bool memonly = false); + + //! Create new DescriptorScriptPubKeyMans and add them to the wallet + void SetupDescriptorScriptPubKeyMans(); }; /** -- cgit v1.2.3