diff options
Diffstat (limited to 'src/wallet/wallet.cpp')
-rw-r--r-- | src/wallet/wallet.cpp | 171 |
1 files changed, 132 insertions, 39 deletions
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index f4472d5bf4..07fc8c35d5 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -160,6 +160,70 @@ std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& return LoadWallet(chain, WalletLocation(name), error, warning); } +std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::string& name, std::string& error, std::string& warning, WalletCreationStatus& status, const SecureString& passphrase, uint64_t wallet_creation_flags) +{ + // Indicate that the wallet is actually supposed to be blank and not just blank to make it encrypted + bool create_blank = (wallet_creation_flags & WALLET_FLAG_BLANK_WALLET); + + // Born encrypted wallets need to be created blank first. + if (!passphrase.empty()) { + wallet_creation_flags |= WALLET_FLAG_BLANK_WALLET; + } + + // Check the wallet file location + WalletLocation location(name); + if (location.Exists()) { + error = "Wallet " + location.GetName() + " already exists."; + status = WalletCreationStatus::CREATION_FAILED; + return nullptr; + } + + // Wallet::Verify will check if we're trying to create a wallet with a duplicate name. + std::string wallet_error; + if (!CWallet::Verify(chain, location, false, wallet_error, warning)) { + error = "Wallet file verification failed: " + wallet_error; + status = WalletCreationStatus::CREATION_FAILED; + return nullptr; + } + + // Make the wallet + std::shared_ptr<CWallet> wallet = CWallet::CreateWalletFromFile(chain, location, wallet_creation_flags); + if (!wallet) { + error = "Wallet creation failed"; + status = WalletCreationStatus::CREATION_FAILED; + return nullptr; + } + + // Encrypt the wallet + if (!passphrase.empty() && !(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { + if (!wallet->EncryptWallet(passphrase)) { + error = "Error: Wallet created but failed to encrypt."; + status = WalletCreationStatus::ENCRYPTION_FAILED; + return nullptr; + } + if (!create_blank) { + // Unlock the wallet + if (!wallet->Unlock(passphrase)) { + error = "Error: Wallet was encrypted but could not be unlocked"; + status = WalletCreationStatus::ENCRYPTION_FAILED; + return nullptr; + } + + // Set a seed for the wallet + CPubKey master_pub_key = wallet->GenerateNewSeed(); + wallet->SetHDSeed(master_pub_key); + wallet->NewKeyPool(); + + // Relock the wallet + wallet->Lock(); + } + } + AddWallet(wallet); + wallet->postInitProcess(); + status = WalletCreationStatus::SUCCESS; + return wallet; +} + const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); @@ -1099,6 +1163,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) // Notify UI of new or updated transaction NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); +#if HAVE_SYSTEM // notify an external script when a wallet transaction comes in or is updated std::string strCmd = gArgs.GetArg("-walletnotify", ""); @@ -1108,6 +1173,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) std::thread t(runCommand, strCmd); t.detach(); // thread runs free } +#endif return true; } @@ -2713,9 +2779,9 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC auto locked_chain = chain().lock(); LOCK(cs_wallet); - CReserveKey reservekey(this); + ReserveDestination reservedest(this); CTransactionRef tx_new; - if (!CreateTransaction(*locked_chain, vecSend, tx_new, reservekey, nFeeRet, nChangePosInOut, strFailReason, coinControl, false)) { + if (!CreateTransaction(*locked_chain, vecSend, tx_new, reservedest, nFeeRet, nChangePosInOut, strFailReason, coinControl, false)) { return false; } @@ -2723,7 +2789,7 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC tx.vout.insert(tx.vout.begin() + nChangePosInOut, tx_new->vout[nChangePosInOut]); // We don't have the normal Create/Commit cycle, and don't want to risk // reusing change, so just remove the key from the keypool here. - reservekey.KeepKey(); + reservedest.KeepDestination(); } // Copy output sizes from new transaction; they may have had the fee @@ -2743,11 +2809,6 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC } } - if (nFeeRet > this->m_default_max_tx_fee) { - strFailReason = TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED); - return false; - } - return true; } @@ -2839,7 +2900,7 @@ OutputType CWallet::TransactionChangeType(OutputType change_type, const std::vec return m_default_address_type; } -bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CReserveKey& reservekey, CAmount& nFeeRet, +bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, ReserveDestination& reservedest, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign) { CAmount nValue = 0; @@ -2880,7 +2941,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy // Create change script that will be used if we need change - // TODO: pass in scriptChange instead of reservekey so + // TODO: pass in scriptChange instead of reservedest so // change transaction isn't always pay-to-bitcoin-address CScript scriptChange; @@ -2900,19 +2961,16 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std strFailReason = _("Can't generate a change-address key. No keys in the internal keypool and can't generate any keys."); return false; } - CPubKey vchPubKey; - bool ret; - ret = reservekey.GetReservedKey(vchPubKey, true); + CTxDestination dest; + const OutputType change_type = TransactionChangeType(coin_control.m_change_type ? *coin_control.m_change_type : m_default_change_type, vecSend); + bool ret = reservedest.GetReservedDestination(change_type, dest, true); if (!ret) { - strFailReason = _("Keypool ran out, please call keypoolrefill first"); + strFailReason = "Keypool ran out, please call keypoolrefill first"; return false; } - const OutputType change_type = TransactionChangeType(coin_control.m_change_type ? *coin_control.m_change_type : m_default_change_type, vecSend); - - LearnRelatedScripts(vchPubKey, change_type); - scriptChange = GetScriptForDestination(GetDestinationForKey(vchPubKey, change_type)); + scriptChange = GetScriptForDestination(dest); } CTxOut change_prototype_txout(0, scriptChange); coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout); @@ -3060,14 +3118,6 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std return false; } - // If we made it here and we aren't even able to meet the relay fee on the next pass, give up - // because we must be at the maximum allowed fee. - if (nFeeNeeded < chain().relayMinFee().GetFee(nBytes)) - { - strFailReason = _("Transaction too large for fee policy"); - return false; - } - if (nFeeRet >= nFeeNeeded) { // Reduce fee to only the needed amount if possible. This // prevents potential overpayment in fees if the coins @@ -3133,7 +3183,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std } } - if (nChangePosInOut == -1) reservekey.ReturnKey(); // Return any reserved key if we don't have change + if (nChangePosInOut == -1) reservedest.ReturnDestination(); // Return any reserved address if we don't have change // Shuffle selected coins and fill in final vin txNew.vin.clear(); @@ -3184,6 +3234,11 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std } } + if (nFeeRet > m_default_max_tx_fee) { + strFailReason = TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED); + return false; + } + if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { // Lastly, ensure this tx will pass the mempool's chain limits if (!chain().checkChainLimits(tx)) { @@ -3206,7 +3261,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std /** * Call after CreateTransaction unless you want to abort */ -bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, CReserveKey& reservekey, CValidationState& state) +bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, ReserveDestination& reservedest, CValidationState& state) { { auto locked_chain = chain().lock(); @@ -3221,7 +3276,7 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve WalletLogPrintf("CommitTransaction:\n%s", wtxNew.tx->ToString()); /* Continued */ { // Take key pair from key pool so it won't be used again - reservekey.KeepKey(); + reservedest.KeepDestination(); // Add tx to wallet, because if it has change it's also ours, // otherwise just for transaction history. @@ -3472,8 +3527,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) { LOCK(cs_wallet); - if (IsLocked()) - return false; + if (IsLocked()) return false; // Top up key pool unsigned int nTargetSize; @@ -3534,8 +3588,7 @@ bool CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe { LOCK(cs_wallet); - if (!IsLocked()) - TopUpKeyPool(); + TopUpKeyPool(); bool fReturningInternal = fRequestedInternal; fReturningInternal &= (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) || IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); @@ -3622,6 +3675,42 @@ bool CWallet::GetKeyFromPool(CPubKey& result, bool internal) return true; } +bool CWallet::GetNewDestination(const OutputType type, const std::string label, CTxDestination& dest, std::string& error) +{ + LOCK(cs_wallet); + error.clear(); + + TopUpKeyPool(); + + // Generate a new key that is added to wallet + CPubKey new_key; + if (!GetKeyFromPool(new_key)) { + error = "Error: Keypool ran out, please call keypoolrefill first"; + return false; + } + LearnRelatedScripts(new_key, type); + dest = GetDestinationForKey(new_key, type); + + SetAddressBook(dest, label, "receive"); + return true; +} + +bool CWallet::GetNewChangeDestination(const OutputType type, CTxDestination& dest, std::string& error) +{ + error.clear(); + + TopUpKeyPool(); + + ReserveDestination reservedest(this); + if (!reservedest.GetReservedDestination(type, dest, true)) { + error = "Error: Keypool ran out, please call keypoolrefill first"; + return false; + } + + reservedest.KeepDestination(); + return true; +} + static int64_t GetOldestKeyTimeInPool(const std::set<int64_t>& setKeyPool, WalletBatch& batch) { if (setKeyPool.empty()) { return GetTime(); @@ -3801,7 +3890,7 @@ std::set<CTxDestination> CWallet::GetLabelAddresses(const std::string& label) co return result; } -bool CReserveKey::GetReservedKey(CPubKey& pubkey, bool internal) +bool ReserveDestination::GetReservedDestination(const OutputType type, CTxDestination& dest, bool internal) { if (!pwallet->CanGetAddresses(internal)) { return false; @@ -3817,25 +3906,29 @@ bool CReserveKey::GetReservedKey(CPubKey& pubkey, bool internal) fInternal = keypool.fInternal; } assert(vchPubKey.IsValid()); - pubkey = vchPubKey; + pwallet->LearnRelatedScripts(vchPubKey, type); + address = GetDestinationForKey(vchPubKey, type); + dest = address; return true; } -void CReserveKey::KeepKey() +void ReserveDestination::KeepDestination() { if (nIndex != -1) pwallet->KeepKey(nIndex); nIndex = -1; vchPubKey = CPubKey(); + address = CNoDestination(); } -void CReserveKey::ReturnKey() +void ReserveDestination::ReturnDestination() { if (nIndex != -1) { pwallet->ReturnKey(nIndex, fInternal, vchPubKey); } nIndex = -1; vchPubKey = CPubKey(); + address = CNoDestination(); } void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id) @@ -4166,12 +4259,12 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, walletFile)); } else if (nLoadWalletRet == DBErrors::TOO_NEW) { - chain.initError(strprintf(_("Error loading %s: Wallet requires newer version of %s"), walletFile, _(PACKAGE_NAME))); + chain.initError(strprintf(_("Error loading %s: Wallet requires newer version of %s"), walletFile, PACKAGE_NAME)); return nullptr; } else if (nLoadWalletRet == DBErrors::NEED_REWRITE) { - chain.initError(strprintf(_("Wallet needed to be rewritten: restart %s to complete"), _(PACKAGE_NAME))); + chain.initError(strprintf(_("Wallet needed to be rewritten: restart %s to complete"), PACKAGE_NAME)); return nullptr; } else { |