diff options
Diffstat (limited to 'src/wallet/wallet.cpp')
-rw-r--r-- | src/wallet/wallet.cpp | 109 |
1 files changed, 79 insertions, 30 deletions
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 13da7bb2c2..223790aa40 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -57,6 +57,8 @@ CFeeRate CWallet::minTxFee = CFeeRate(DEFAULT_TRANSACTION_MINFEE); */ CFeeRate CWallet::fallbackFee = CFeeRate(DEFAULT_FALLBACK_FEE); +CFeeRate CWallet::m_discard_rate = CFeeRate(DEFAULT_DISCARD_FEE); + const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); /** @defgroup mapWallet @@ -87,7 +89,7 @@ const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const return &(it->second); } -CPubKey CWallet::GenerateNewKey(bool internal) +CPubKey CWallet::GenerateNewKey(CWalletDB &walletdb, bool internal) { AssertLockHeld(cs_wallet); // mapKeyMetadata bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets @@ -100,14 +102,15 @@ CPubKey CWallet::GenerateNewKey(bool internal) // use HD key derivation if HD was enabled during wallet creation if (IsHDEnabled()) { - DeriveNewChildKey(metadata, secret, (CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false)); + DeriveNewChildKey(walletdb, metadata, secret, (CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false)); } else { secret.MakeNewKey(fCompressed); } // Compressed public keys were introduced in version 0.6.0 - if (fCompressed) + if (fCompressed) { SetMinVersion(FEATURE_COMPRPUBKEY); + } CPubKey pubkey = secret.GetPubKey(); assert(secret.VerifyPubKey(pubkey)); @@ -115,12 +118,13 @@ CPubKey CWallet::GenerateNewKey(bool internal) mapKeyMetadata[pubkey.GetID()] = metadata; UpdateTimeFirstKey(nCreationTime); - if (!AddKeyPubKey(secret, pubkey)) + if (!AddKeyPubKeyWithDB(walletdb, secret, pubkey)) { throw std::runtime_error(std::string(__func__) + ": AddKey failed"); + } return pubkey; } -void CWallet::DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret, bool internal) +void CWallet::DeriveNewChildKey(CWalletDB &walletdb, CKeyMetadata& metadata, CKey& secret, bool internal) { // for now we use a fixed keypath scheme of m/0'/0'/k CKey key; //master key seed (256bit) @@ -162,33 +166,52 @@ void CWallet::DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret, bool inter secret = childKey.key; metadata.hdMasterKeyID = hdChain.masterKeyID; // update the chain model in the database - if (!CWalletDB(*dbw).WriteHDChain(hdChain)) + if (!walletdb.WriteHDChain(hdChain)) throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed"); } -bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) +bool CWallet::AddKeyPubKeyWithDB(CWalletDB &walletdb, const CKey& secret, const CPubKey &pubkey) { AssertLockHeld(cs_wallet); // mapKeyMetadata - if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) + + // CCryptoKeyStore has no concept of wallet databases, but calls AddCryptedKey + // which is overridden below. To avoid flushes, the database handle is + // tunneled through to it. + bool needsDB = !pwalletdbEncryption; + if (needsDB) { + pwalletdbEncryption = &walletdb; + } + if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) { + if (needsDB) pwalletdbEncryption = NULL; return false; + } + if (needsDB) pwalletdbEncryption = NULL; // check if we need to remove from watch-only CScript script; script = GetScriptForDestination(pubkey.GetID()); - if (HaveWatchOnly(script)) + if (HaveWatchOnly(script)) { RemoveWatchOnly(script); + } script = GetScriptForRawPubKey(pubkey); - if (HaveWatchOnly(script)) + if (HaveWatchOnly(script)) { RemoveWatchOnly(script); + } if (!IsCrypted()) { - return CWalletDB(*dbw).WriteKey(pubkey, + return walletdb.WriteKey(pubkey, secret.GetPrivKey(), mapKeyMetadata[pubkey.GetID()]); } return true; } +bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) +{ + CWalletDB walletdb(*dbw); + return CWallet::AddKeyPubKeyWithDB(walletdb, secret, pubkey); +} + bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) { @@ -2450,7 +2473,7 @@ bool CWallet::SignTransaction(CMutableTransaction &tx) return true; } -bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl coinControl, bool keepReserveKey) +bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl coinControl) { std::vector<CRecipient> vecSend; @@ -2472,8 +2495,13 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosInOut, strFailReason, coinControl, false)) { return false; } - if (nChangePosInOut != -1) + + if (nChangePosInOut != -1) { tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.tx->vout[nChangePosInOut]); + // we dont have the normal Create/Commit cycle, and dont want to risk reusing change, + // so just remove the key from the keypool here. + reservekey.KeepKey(); + } // Copy output sizes from new transaction; they may have had the fee subtracted from them for (unsigned int idx = 0; idx < tx.vout.size(); idx++) @@ -2494,13 +2522,21 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC } } - // optionally keep the change output key - if (keepReserveKey) - reservekey.KeepKey(); return true; } +static CFeeRate GetDiscardRate(const CBlockPolicyEstimator& estimator) +{ + unsigned int highest_target = estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE); + CFeeRate discard_rate = estimator.estimateSmartFee(highest_target, nullptr /* FeeCalculation */, false /* conservative */); + // Don't let discard_rate be greater than longest possible fee estimate if we get a valid fee estimate + discard_rate = (discard_rate == CFeeRate(0)) ? CWallet::m_discard_rate : std::min(discard_rate, CWallet::m_discard_rate); + // Discard rate must be at least dustRelayFee + discard_rate = std::max(discard_rate, ::dustRelayFee); + return discard_rate; +} + bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign) { @@ -2600,6 +2636,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT CTxOut change_prototype_txout(0, scriptChange); size_t change_prototype_size = GetSerializeSize(change_prototype_txout, SER_DISK, 0); + CFeeRate discard_rate = GetDiscardRate(::feeEstimator); nFeeRet = 0; bool pick_new_inputs = true; CAmount nValueIn = 0; @@ -2667,7 +2704,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT // Never create dust outputs; if we would, just // add the dust to the fee. - if (IsDust(newTxOut, ::dustRelayFee)) + if (IsDust(newTxOut, discard_rate)) { nChangePosInOut = -1; nFeeRet += nChange; @@ -2747,7 +2784,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT // (because of reduced tx size) and so we should add a // change output. Only try this once. CAmount fee_needed_for_change = GetMinimumFee(change_prototype_size, coin_control, ::mempool, ::feeEstimator, nullptr); - CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, ::dustRelayFee); + CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, discard_rate); CAmount max_excess_fee = fee_needed_for_change + minimum_value_for_change; if (nFeeRet > nFeeNeeded + max_excess_fee && nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) { pick_new_inputs = false; @@ -3185,27 +3222,25 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) CWalletDB walletdb(*dbw); for (int64_t i = missingInternal + missingExternal; i--;) { - int64_t nEnd = 1; if (i < missingInternal) { internal = true; } - if (!setInternalKeyPool.empty()) { - nEnd = *(setInternalKeyPool.rbegin()) + 1; - } - if (!setExternalKeyPool.empty()) { - nEnd = std::max(nEnd, *(setExternalKeyPool.rbegin()) + 1); - } + assert(m_max_keypool_index < std::numeric_limits<int64_t>::max()); // How in the hell did you use so many keys? + int64_t index = ++m_max_keypool_index; - if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey(internal), internal))) + if (!walletdb.WritePool(index, CKeyPool(GenerateNewKey(walletdb, internal), internal))) { throw std::runtime_error(std::string(__func__) + ": writing generated key failed"); + } if (internal) { - setInternalKeyPool.insert(nEnd); + setInternalKeyPool.insert(index); } else { - setExternalKeyPool.insert(nEnd); + setExternalKeyPool.insert(index); } - LogPrintf("keypool added key %d, size=%u (%u internal), new key is %s\n", nEnd, setInternalKeyPool.size() + setExternalKeyPool.size(), setInternalKeyPool.size(), internal ? "internal" : "external"); + } + if (missingInternal + missingExternal > 0) { + LogPrintf("keypool added %d keys (%d internal), size=%u (%u internal)\n", missingInternal + missingExternal, missingInternal, setInternalKeyPool.size() + setExternalKeyPool.size(), setInternalKeyPool.size()); } } return true; @@ -3280,7 +3315,8 @@ bool CWallet::GetKeyFromPool(CPubKey& result, bool internal) if (nIndex == -1) { if (IsLocked()) return false; - result = GenerateNewKey(internal); + CWalletDB walletdb(*dbw); + result = GenerateNewKey(walletdb, internal); return true; } KeepKey(nIndex); @@ -3788,6 +3824,9 @@ std::string CWallet::GetWalletHelpString(bool showDebug) strUsage += HelpMessageOpt("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), DEFAULT_KEYPOOL_SIZE)); strUsage += HelpMessageOpt("-fallbackfee=<amt>", strprintf(_("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE))); + strUsage += HelpMessageOpt("-discardfee=<amt>", strprintf(_("The fee rate (in %s/kB) used to discard change (to fee) if it would be dust at this fee rate (default: %s) " + "Note: We will always discard up to the dust relay fee and a discard fee above that is limited by the longest target fee estimate"), + CURRENCY_UNIT, FormatMoney(DEFAULT_DISCARD_FEE))); strUsage += HelpMessageOpt("-mintxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE))); strUsage += HelpMessageOpt("-paytxfee=<amt>", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"), @@ -4113,6 +4152,16 @@ bool CWallet::ParameterInteraction() _("This is the transaction fee you may pay when fee estimates are not available.")); CWallet::fallbackFee = CFeeRate(nFeePerK); } + if (IsArgSet("-discardfee")) + { + CAmount nFeePerK = 0; + if (!ParseMoney(GetArg("-discardfee", ""), nFeePerK)) + return InitError(strprintf(_("Invalid amount for -discardfee=<amount>: '%s'"), GetArg("-discardfee", ""))); + if (nFeePerK > HIGH_TX_FEE_PER_KB) + InitWarning(AmountHighWarn("-discardfee") + " " + + _("This is the transaction fee you may discard if change is smaller than dust at this level")); + CWallet::m_discard_rate = CFeeRate(nFeePerK); + } if (IsArgSet("-paytxfee")) { CAmount nFeePerK = 0; |