diff options
Diffstat (limited to 'src/wallet/wallet.cpp')
-rw-r--r-- | src/wallet/wallet.cpp | 228 |
1 files changed, 162 insertions, 66 deletions
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index abb1ea7e76..c2bac6e330 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -8,7 +8,7 @@ #include "base58.h" #include "checkpoints.h" #include "chain.h" -#include "coincontrol.h" +#include "wallet/coincontrol.h" #include "consensus/consensus.h" #include "consensus/validation.h" #include "key.h" @@ -100,43 +100,7 @@ CPubKey CWallet::GenerateNewKey() // use HD key derivation if HD was enabled during wallet creation if (IsHDEnabled()) { - // for now we use a fixed keypath scheme of m/0'/0'/k - CKey key; //master key seed (256bit) - CExtKey masterKey; //hd master key - CExtKey accountKey; //key at m/0' - CExtKey externalChainChildKey; //key at m/0'/0' - CExtKey childKey; //key at m/0'/0'/<n>' - - // try to get the master key - if (!GetKey(hdChain.masterKeyID, key)) - throw std::runtime_error(std::string(__func__) + ": Master key not found"); - - masterKey.SetMaster(key.begin(), key.size()); - - // derive m/0' - // use hardened derivation (child keys >= 0x80000000 are hardened after bip32) - masterKey.Derive(accountKey, BIP32_HARDENED_KEY_LIMIT); - - // derive m/0'/0' - accountKey.Derive(externalChainChildKey, BIP32_HARDENED_KEY_LIMIT); - - // derive child key at next index, skip keys already known to the wallet - do - { - // always derive hardened keys - // childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened child-index-range - // example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649 - externalChainChildKey.Derive(childKey, hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT); - metadata.hdKeypath = "m/0'/0'/"+std::to_string(hdChain.nExternalChainCounter)+"'"; - metadata.hdMasterKeyID = hdChain.masterKeyID; - // increment childkey index - hdChain.nExternalChainCounter++; - } while(HaveKey(childKey.key.GetPubKey().GetID())); - secret = childKey.key; - - // update the chain model in the database - if (!CWalletDB(strWalletFile).WriteHDChain(hdChain)) - throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed"); + DeriveNewChildKey(metadata, secret); } else { secret.MakeNewKey(fCompressed); } @@ -157,6 +121,46 @@ CPubKey CWallet::GenerateNewKey() return pubkey; } +void CWallet::DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret) +{ + // for now we use a fixed keypath scheme of m/0'/0'/k + CKey key; //master key seed (256bit) + CExtKey masterKey; //hd master key + CExtKey accountKey; //key at m/0' + CExtKey externalChainChildKey; //key at m/0'/0' + CExtKey childKey; //key at m/0'/0'/<n>' + + // try to get the master key + if (!GetKey(hdChain.masterKeyID, key)) + throw std::runtime_error(std::string(__func__) + ": Master key not found"); + + masterKey.SetMaster(key.begin(), key.size()); + + // derive m/0' + // use hardened derivation (child keys >= 0x80000000 are hardened after bip32) + masterKey.Derive(accountKey, BIP32_HARDENED_KEY_LIMIT); + + // derive m/0'/0' + accountKey.Derive(externalChainChildKey, BIP32_HARDENED_KEY_LIMIT); + + // derive child key at next index, skip keys already known to the wallet + do { + // always derive hardened keys + // childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened child-index-range + // example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649 + externalChainChildKey.Derive(childKey, hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT); + metadata.hdKeypath = "m/0'/0'/" + std::to_string(hdChain.nExternalChainCounter) + "'"; + metadata.hdMasterKeyID = hdChain.masterKeyID; + // increment childkey index + hdChain.nExternalChainCounter++; + } while (HaveKey(childKey.key.GetPubKey().GetID())); + secret = childKey.key; + + // update the chain model in the database + if (!CWalletDB(strWalletFile).WriteHDChain(hdChain)) + throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed"); +} + bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) { AssertLockHeld(cs_wallet); // mapKeyMetadata @@ -401,8 +405,8 @@ set<uint256> CWallet::GetConflicts(const uint256& txid) const if (mapTxSpends.count(txin.prevout) <= 1) continue; // No conflict if zero or one spends range = mapTxSpends.equal_range(txin.prevout); - for (TxSpends::const_iterator it = range.first; it != range.second; ++it) - result.insert(it->second); + for (TxSpends::const_iterator _it = range.first; _it != range.second; ++_it) + result.insert(_it->second); } return result; } @@ -654,8 +658,79 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) DBErrors CWallet::ReorderTransactions() { + LOCK(cs_wallet); CWalletDB walletdb(strWalletFile); - return walletdb.ReorderTransactions(this); + + // Old wallets didn't have any defined order for transactions + // Probably a bad idea to change the output of this + + // First: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap. + typedef pair<CWalletTx*, CAccountingEntry*> TxPair; + typedef multimap<int64_t, TxPair > TxItems; + TxItems txByTime; + + for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + CWalletTx* wtx = &((*it).second); + txByTime.insert(make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0))); + } + list<CAccountingEntry> acentries; + walletdb.ListAccountCreditDebit("", acentries); + BOOST_FOREACH(CAccountingEntry& entry, acentries) + { + txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry))); + } + + nOrderPosNext = 0; + std::vector<int64_t> nOrderPosOffsets; + for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it) + { + CWalletTx *const pwtx = (*it).second.first; + CAccountingEntry *const pacentry = (*it).second.second; + int64_t& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos; + + if (nOrderPos == -1) + { + nOrderPos = nOrderPosNext++; + nOrderPosOffsets.push_back(nOrderPos); + + if (pwtx) + { + if (!walletdb.WriteTx(*pwtx)) + return DB_LOAD_FAIL; + } + else + if (!walletdb.WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) + return DB_LOAD_FAIL; + } + else + { + int64_t nOrderPosOff = 0; + BOOST_FOREACH(const int64_t& nOffsetStart, nOrderPosOffsets) + { + if (nOrderPos >= nOffsetStart) + ++nOrderPosOff; + } + nOrderPos += nOrderPosOff; + nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1); + + if (!nOrderPosOff) + continue; + + // Since we're changing the order, write it back + if (pwtx) + { + if (!walletdb.WriteTx(*pwtx)) + return DB_LOAD_FAIL; + } + else + if (!walletdb.WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) + return DB_LOAD_FAIL; + } + } + walletdb.WriteOrderPosNext(nOrderPosNext); + + return DB_LOAD_OK; } int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb) @@ -1281,9 +1356,9 @@ int CWalletTx::GetRequestCount() const // How about the block it's in? if (nRequests == 0 && !hashUnset()) { - map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); - if (mi != pwallet->mapRequestCount.end()) - nRequests = (*mi).second; + map<uint256, int>::const_iterator _mi = pwallet->mapRequestCount.find(hashBlock); + if (_mi != pwallet->mapRequestCount.end()) + nRequests = (*_mi).second; else nRequests = 1; // If it's in someone else's block it must have got out } @@ -1459,7 +1534,8 @@ void CWallet::ReacceptWalletTransactions() CWalletTx& wtx = *(item.second); LOCK(mempool.cs); - wtx.AcceptToMemoryPool(false, maxTxFee); + CValidationState state; + wtx.AcceptToMemoryPool(maxTxFee, state); } } @@ -1903,7 +1979,7 @@ static void ApproximateBestSubset(vector<pair<CAmount, pair<const CWalletTx*,uns vfBest.assign(vValue.size(), true); nBest = nTotalLower; - seed_insecure_rand(); + FastRandomContext insecure_rand; for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++) { @@ -1920,7 +1996,7 @@ static void ApproximateBestSubset(vector<pair<CAmount, pair<const CWalletTx*,uns //that the rng is fast. We do not use a constant random sequence, //because there may be some privacy improvement by making //the selection random. - if (nPass == 0 ? insecure_rand()&1 : !vfIncluded[i]) + if (nPass == 0 ? insecure_rand.rand32()&1 : !vfIncluded[i]) { nTotal += vValue[i].first; vfIncluded[i] = true; @@ -2429,17 +2505,22 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt dPriority = wtxNew.ComputePriority(dPriority, nBytes); + // Allow to override the default confirmation target over the CoinControl instance + int currentConfirmationTarget = nTxConfirmTarget; + if (coinControl && coinControl->nConfirmTarget > 0) + currentConfirmationTarget = coinControl->nConfirmTarget; + // Can we complete this as a free transaction? if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) { // Not enough fee: enough priority? - double dPriorityNeeded = mempool.estimateSmartPriority(nTxConfirmTarget); + double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget); // Require at least hard-coded AllowFree. if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) break; } - CAmount nFeeNeeded = GetMinimumFee(nBytes, nTxConfirmTarget, mempool); + CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool); if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { nFeeNeeded = coinControl->nMinimumTotalFee; } @@ -2470,7 +2551,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt /** * Call after CreateTransaction unless you want to abort */ -bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman) +bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman, CValidationState& state) { { LOCK2(cs_main, cs_wallet); @@ -2498,10 +2579,9 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon if (fBroadcastTransactions) { // Broadcast - if (!wtxNew.AcceptToMemoryPool(false, maxTxFee)) - { + if (!wtxNew.AcceptToMemoryPool(maxTxFee, state)) { // This must not fail. The transaction has already been signed and recorded. - LogPrintf("CommitTransaction(): Error: Transaction not valid\n"); + LogPrintf("CommitTransaction(): Error: Transaction not valid, %s\n", state.GetRejectReason()); return false; } wtxNew.RelayWalletTransaction(connman); @@ -2938,17 +3018,17 @@ set< set<CTxDestination> > CWallet::GetAddressGroupings() set< set<CTxDestination>* > uniqueGroupings; // a set of pointers to groups of addresses map< CTxDestination, set<CTxDestination>* > setmap; // map addresses to the unique group containing it - BOOST_FOREACH(set<CTxDestination> grouping, groupings) + BOOST_FOREACH(set<CTxDestination> _grouping, groupings) { // make a set of all the groups hit by this new group set< set<CTxDestination>* > hits; map< CTxDestination, set<CTxDestination>* >::iterator it; - BOOST_FOREACH(CTxDestination address, grouping) + BOOST_FOREACH(CTxDestination address, _grouping) if ((it = setmap.find(address)) != setmap.end()) hits.insert((*it).second); // merge all hit groups into a new single group and delete old groups - set<CTxDestination>* merged = new set<CTxDestination>(grouping); + set<CTxDestination>* merged = new set<CTxDestination>(_grouping); BOOST_FOREACH(set<CTxDestination>* hit, hits) { merged->insert(hit->begin(), hit->end()); @@ -3463,14 +3543,22 @@ bool CWallet::InitLoadWallet() LogPrintf("mapWallet.size() = %u\n", walletInstance->mapWallet.size()); LogPrintf("mapAddressBook.size() = %u\n", walletInstance->mapAddressBook.size()); } - // Add wallet transactions that aren't already in a block to mapTransactions - walletInstance->ReacceptWalletTransactions(); pwalletMain = walletInstance; return true; } +void CWallet::postInitProcess(boost::thread_group& threadGroup) +{ + // Add wallet transactions that aren't already in a block to mempool + // Do this here as mempool requires genesis block to be loaded + ReacceptWalletTransactions(); + + // Run a thread to flush wallet periodically + threadGroup.create_thread(boost::bind(&ThreadFlushWalletDB, boost::ref(this->strWalletFile))); +} + bool CWallet::ParameterInteraction() { if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) @@ -3485,13 +3573,19 @@ bool CWallet::ParameterInteraction() if (GetArg("-prune", 0) && GetBoolArg("-rescan", false)) return InitError(_("Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.")); + if (::minRelayTxFee.GetFeePerK() > HIGH_TX_FEE_PER_KB) + InitWarning(AmountHighWarn("-minrelaytxfee") + " " + + _("The wallet will avoid paying less than the minimum relay fee.")); + if (mapArgs.count("-mintxfee")) { CAmount n = 0; - if (ParseMoney(mapArgs["-mintxfee"], n) && n > 0) - CWallet::minTxFee = CFeeRate(n); - else + if (!ParseMoney(mapArgs["-mintxfee"], n) || 0 == n) return InitError(AmountErrMsg("mintxfee", mapArgs["-mintxfee"])); + if (n > HIGH_TX_FEE_PER_KB) + InitWarning(AmountHighWarn("-mintxfee") + " " + + _("This is the minimum transaction fee you pay on every transaction.")); + CWallet::minTxFee = CFeeRate(n); } if (mapArgs.count("-fallbackfee")) { @@ -3499,7 +3593,8 @@ bool CWallet::ParameterInteraction() if (!ParseMoney(mapArgs["-fallbackfee"], nFeePerK)) return InitError(strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), mapArgs["-fallbackfee"])); if (nFeePerK > HIGH_TX_FEE_PER_KB) - InitWarning(_("-fallbackfee is set very high! This is the transaction fee you may pay when fee estimates are not available.")); + InitWarning(AmountHighWarn("-fallbackfee") + " " + + _("This is the transaction fee you may pay when fee estimates are not available.")); CWallet::fallbackFee = CFeeRate(nFeePerK); } if (mapArgs.count("-paytxfee")) @@ -3508,7 +3603,9 @@ bool CWallet::ParameterInteraction() if (!ParseMoney(mapArgs["-paytxfee"], nFeePerK)) return InitError(AmountErrMsg("paytxfee", mapArgs["-paytxfee"])); if (nFeePerK > HIGH_TX_FEE_PER_KB) - InitWarning(_("-paytxfee is set very high! This is the transaction fee you will pay if you send a transaction.")); + InitWarning(AmountHighWarn("-paytxfee") + " " + + _("This is the transaction fee you will pay if you send a transaction.")); + payTxFee = CFeeRate(nFeePerK, 1000); if (payTxFee < ::minRelayTxFee) { @@ -3639,8 +3736,7 @@ int CMerkleTx::GetBlocksToMaturity() const } -bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, CAmount nAbsurdFee) +bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state) { - CValidationState state; - return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, false, nAbsurdFee); + return ::AcceptToMemoryPool(mempool, state, *this, true, NULL, false, nAbsurdFee); } |