From 360cfe142c552ac5c4d904a1e970390188151ca8 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 25 Apr 2013 19:30:28 +0200 Subject: Allow the default key to be unavailable This solves the issue where no default key can be added after -salvagewallet. --- src/wallet.cpp | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) (limited to 'src/wallet.cpp') diff --git a/src/wallet.cpp b/src/wallet.cpp index c7eb4f74e..0672a3669 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -457,17 +457,19 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) return false; #ifndef QT_GUI // If default receiving address gets used, replace it with a new one - CScript scriptDefaultKey; - scriptDefaultKey.SetDestination(vchDefaultKey.GetID()); - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - { - if (txout.scriptPubKey == scriptDefaultKey) + if (vchDefaultKey.IsValid()) { + CScript scriptDefaultKey; + scriptDefaultKey.SetDestination(vchDefaultKey.GetID()); + BOOST_FOREACH(const CTxOut& txout, wtx.vout) { - CPubKey newDefaultKey; - if (GetKeyFromPool(newDefaultKey, false)) + if (txout.scriptPubKey == scriptDefaultKey) { - SetDefaultKey(newDefaultKey); - SetAddressBookName(vchDefaultKey.GetID(), ""); + CPubKey newDefaultKey; + if (GetKeyFromPool(newDefaultKey, false)) + { + SetDefaultKey(newDefaultKey); + SetAddressBookName(vchDefaultKey.GetID(), ""); + } } } } @@ -1199,8 +1201,8 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW // post-backup change. // Reserve a new key pair from key pool - CPubKey vchPubKey = reservekey.GetReservedKey(); - // assert(mapKeys.count(vchPubKey)); + CPubKey vchPubKey; + assert(reservekey.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked // Fill a vout to ourself // TODO: pass in scriptChange instead of reservekey so @@ -1737,7 +1739,7 @@ set< set > CWallet::GetAddressGroupings() return ret; } -CPubKey CReserveKey::GetReservedKey() +bool CReserveKey::GetReservedKey(CPubKey& pubkey) { if (nIndex == -1) { @@ -1745,14 +1747,17 @@ CPubKey CReserveKey::GetReservedKey() pwallet->ReserveKeyFromKeyPool(nIndex, keypool); if (nIndex != -1) vchPubKey = keypool.vchPubKey; - else - { - printf("CReserveKey::GetReservedKey(): Warning: Using default key instead of a new key, top up your keypool!"); - vchPubKey = pwallet->vchDefaultKey; + else { + if (pwallet->vchDefaultKey.IsValid()) { + printf("CReserveKey::GetReservedKey(): Warning: Using default key instead of a new key, top up your keypool!"); + vchPubKey = pwallet->vchDefaultKey; + } else + return false; } } assert(vchPubKey.IsValid()); - return vchPubKey; + pubkey = vchPubKey; + return true; } void CReserveKey::KeepKey() -- cgit v1.2.3 From 2f15e86a68aba56a8ea8e01e58b2fb7b71846b51 Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Thu, 2 May 2013 12:43:07 -0400 Subject: Do not write to wallet during LoadWallet When debugging another issue, I found a hang-during-startup race condition due to LoadWallet calling SetMinVersion (via LoadCryptedKey). Writing to the file that you're in the process of reading is a bad idea. --- src/wallet.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/wallet.cpp') diff --git a/src/wallet.cpp b/src/wallet.cpp index c7eb4f74e..9658dab67 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -71,6 +71,11 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, const vector &vchCryptedSecret) +{ + return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); +} + bool CWallet::AddCScript(const CScript& redeemScript) { if (!CCryptoKeyStore::AddCScript(redeemScript)) -- cgit v1.2.3 From 8de9bb53af32f7f6b09c06f831f2c0a7b4e95303 Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Wed, 24 Apr 2013 18:27:00 -0400 Subject: Define dust transaction outputs, and make them non-standard --- src/wallet.cpp | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) (limited to 'src/wallet.cpp') diff --git a/src/wallet.cpp b/src/wallet.cpp index c7eb4f74e..1554b1576 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1162,7 +1162,12 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW double dPriority = 0; // vouts to the payees BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) - wtxNew.vout.push_back(CTxOut(s.second, s.first)); + { + CTxOut txout(s.second, s.first); + if (txout.IsDust()) + return false; + wtxNew.vout.push_back(txout); + } // Choose coins to use set > setCoins; @@ -1208,9 +1213,21 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW CScript scriptChange; scriptChange.SetDestination(vchPubKey.GetID()); - // Insert change txn at random position: - vector::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()+1); - wtxNew.vout.insert(position, CTxOut(nChange, scriptChange)); + CTxOut newTxOut(nChange, scriptChange); + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust()) + { + nFeeRet += nChange; + reservekey.ReturnKey(); + } + else + { + // Insert change txn at random position: + vector::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()+1); + wtxNew.vout.insert(position, newTxOut); + } } else reservekey.ReturnKey(); -- cgit v1.2.3 From 1f00f4e9c9b4b643da22bb5d9f94d66683fa1a15 Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Thu, 25 Apr 2013 17:31:22 -0400 Subject: CreateTransaction: return strFailReason on failure --- src/wallet.cpp | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) (limited to 'src/wallet.cpp') diff --git a/src/wallet.cpp b/src/wallet.cpp index 1554b1576..65d2c3b68 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1134,17 +1134,24 @@ bool CWallet::SelectCoins(int64 nTargetValue, set >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) +bool CWallet::CreateTransaction(const vector >& vecSend, + CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, std::string& strFailReason) { int64 nValue = 0; BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) { if (nValue < 0) + { + strFailReason = _("Transaction amounts must be positive"); return false; + } nValue += s.second; } if (vecSend.empty() || nValue < 0) + { + strFailReason = _("Transaction amounts must be positive"); return false; + } wtxNew.BindWallet(this); @@ -1165,7 +1172,10 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW { CTxOut txout(s.second, s.first); if (txout.IsDust()) + { + strFailReason = _("Transaction amount too small"); return false; + } wtxNew.vout.push_back(txout); } @@ -1173,7 +1183,10 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW set > setCoins; int64 nValueIn = 0; if (!SelectCoins(nTotalValue, setCoins, nValueIn)) + { + strFailReason = _("Insufficient funds"); return false; + } BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { int64 nCredit = pcoin.first->vout[pcoin.second].nValue; @@ -1240,12 +1253,18 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW int nIn = 0; BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) if (!SignSignature(*this, *coin.first, wtxNew, nIn++)) + { + strFailReason = _("Signing transaction failed"); return false; + } // Limit size unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK, PROTOCOL_VERSION); if (nBytes >= MAX_STANDARD_TX_SIZE) + { + strFailReason = _("Transaction too large"); return false; + } dPriority /= nBytes; // Check that enough fee is included @@ -1269,11 +1288,12 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW return true; } -bool CWallet::CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) +bool CWallet::CreateTransaction(CScript scriptPubKey, int64 nValue, + CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, std::string& strFailReason) { vector< pair > vecSend; vecSend.push_back(make_pair(scriptPubKey, nValue)); - return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet); + return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet, strFailReason); } // Call after CreateTransaction unless you want to abort @@ -1339,14 +1359,12 @@ string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, printf("SendMoney() : %s", strError.c_str()); return strError; } - if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired)) + string strError; + if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired, strError)) { - string strError; if (nValue + nFeeRequired > GetBalance()) strError = strprintf(_("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!"), FormatMoney(nFeeRequired).c_str()); - else - strError = _("Error: Transaction creation failed!"); - printf("SendMoney() : %s", strError.c_str()); + printf("SendMoney() : %s\n", strError.c_str()); return strError; } -- cgit v1.2.3 From 000dc55181f77cd96076c76b2cc13f8bcbe4146e Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Thu, 25 Apr 2013 20:11:27 -0400 Subject: Un-hardcode TX_FEE constants Allow setting of MIN_TX_FEE / MIN_RELAY_TX_FEE with -mintxfee / -mintxrelayfee Default values are the same (0.0001 BTC). --- src/wallet.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/wallet.cpp') diff --git a/src/wallet.cpp b/src/wallet.cpp index 65d2c3b68..532a73201 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1197,12 +1197,12 @@ bool CWallet::CreateTransaction(const vector >& vecSend, } int64 nChange = nValueIn - nValue - nFeeRet; - // if sub-cent change is required, the fee must be raised to at least MIN_TX_FEE + // if sub-cent change is required, the fee must be raised to at least nMinTxFee // or until nChange becomes zero // NOTE: this depends on the exact behaviour of GetMinFee - if (nFeeRet < MIN_TX_FEE && nChange > 0 && nChange < CENT) + if (nFeeRet < CTransaction::nMinTxFee && nChange > 0 && nChange < CENT) { - int64 nMoveToFee = min(nChange, MIN_TX_FEE - nFeeRet); + int64 nMoveToFee = min(nChange, CTransaction::nMinTxFee - nFeeRet); nChange -= nMoveToFee; nFeeRet += nMoveToFee; } -- cgit v1.2.3 From 92f2c1fe0fe2905540b0435188988851145f92be Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Tue, 7 May 2013 10:47:00 -0400 Subject: Use boost::asio::deadline_timer for walletpassphrase timeout New method in bitcoinrpc: RunLater, that uses a map of deadline timers to run a function later. Behavior of walletpassphrase is changed; before, calling walletpassphrase again before the lock timeout passed would result in: Error: Wallet is already unlocked. You would have to call lockwallet before walletpassphrase. Now: the last walletpassphrase with correct password wins, and overrides any previous timeout. Fixes issue# 1961 which was caused by spawning too many threads. Test plan: Start with encrypted wallet, password 'foo' NOTE: python -c 'import time; print("%d"%time.time())' ... will tell you current unix timestamp. Try: walletpassphrase foo 600 getinfo EXPECT: unlocked_until is about 10 minutes in the future walletpassphrase foo 1 sleep 2 sendtoaddress mun74Bvba3B1PF2YkrF4NsgcJwHXXh12LF 11 EXPECT: Error: Please enter the wallet passphrase with walletpassphrase first. walletpassphrase foo 600 walletpassphrase foo 0 getinfo EXPECT: wallet is locked (unlocked_until is 0) walletpassphrase foo 10 walletpassphrase foo 600 getinfo EXPECT: wallet is unlocked until 10 minutes in future walletpassphrase foo 60 walletpassphrase bar 600 EXPECT: Error, incorrect passphrase getinfo EXPECT: wallet still scheduled to lock 60 seconds from first (successful) walletpassphrase --- src/wallet.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'src/wallet.cpp') diff --git a/src/wallet.cpp b/src/wallet.cpp index c70ea20e8..eebd8ac18 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -87,9 +87,6 @@ bool CWallet::AddCScript(const CScript& redeemScript) bool CWallet::Unlock(const SecureString& strWalletPassphrase) { - if (!IsLocked()) - return false; - CCrypter crypter; CKeyingMaterial vMasterKey; @@ -100,7 +97,7 @@ bool CWallet::Unlock(const SecureString& strWalletPassphrase) if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) return false; if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) - return false; + continue; // try another master key if (CCryptoKeyStore::Unlock(vMasterKey)) return true; } -- cgit v1.2.3 From 0fe8010a10bafd67f9131b2da034fb9cd7fc5024 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 12 May 2013 15:50:22 +0200 Subject: Make FindBlockByHeight constant-time. Remove the pnext pointer in CBlockIndex, and replace it with a vBlockIndexByHeight vector (no effect on memory usage). pnext can now be replaced by vBlockIndexByHeight[nHeight+1], but FindBlockByHeight becomes constant-time. This also means the entire mapBlockIndex structure and the block index entries in it become purely blocktree-related data, and independent from the currently active chain, potentially allowing them to be protected by separate mutexes in the future. --- src/wallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/wallet.cpp') diff --git a/src/wallet.cpp b/src/wallet.cpp index c70ea20e8..1cc248323 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -780,7 +780,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) if (AddToWalletIfInvolvingMe(tx.GetHash(), tx, &block, fUpdate)) ret++; } - pindex = pindex->pnext; + pindex = pindex->GetNextInMainChain(); } } return ret; -- cgit v1.2.3 From dfa23b94c24aae6466152fccbe896ba5dc0e97b4 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 1 May 2013 06:52:05 +0200 Subject: CSecret/CKey -> CKey/CPubKey split/refactor --- src/wallet.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'src/wallet.cpp') diff --git a/src/wallet.cpp b/src/wallet.cpp index c70ea20e8..5efc45583 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -32,26 +32,28 @@ CPubKey CWallet::GenerateNewKey() bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets RandAddSeedPerfmon(); - CKey key; - key.MakeNewKey(fCompressed); + CKey secret; + secret.MakeNewKey(fCompressed); // Compressed public keys were introduced in version 0.6.0 if (fCompressed) SetMinVersion(FEATURE_COMPRPUBKEY); - if (!AddKey(key)) + CPubKey pubkey = secret.GetPubKey(); + if (!AddKeyPubKey(secret, pubkey)) throw std::runtime_error("CWallet::GenerateNewKey() : AddKey failed"); - return key.GetPubKey(); + return pubkey; } -bool CWallet::AddKey(const CKey& key) +bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) { - if (!CCryptoKeyStore::AddKey(key)) + if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) return false; if (!fFileBacked) return true; - if (!IsCrypted()) - return CWalletDB(strWalletFile).WriteKey(key.GetPubKey(), key.GetPrivKey()); + if (!IsCrypted()) { + return CWalletDB(strWalletFile).WriteKey(pubkey, secret.GetPrivKey()); + } return true; } -- cgit v1.2.3