X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fwallet.cpp;h=2645f8cf5c946171ea0f4fb54eb1e4efb45f58b2;hb=3176e0f244d929669aa3e1d81e0787d82d9150d3;hp=d0684130a639f3f6e9659f780149aeb929078796;hpb=e10622d1297e638109bbf58c35ad008f7acbae7c;p=novacoin.git diff --git a/src/wallet.cpp b/src/wallet.cpp index d068413..2645f8c 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1,5 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2011-2013 The PPCoin developers +// Copyright (c) 2013 NovaCoin Developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -7,6 +9,7 @@ #include "walletdb.h" #include "crypter.h" #include "ui_interface.h" +#include "kernel.h" using namespace std; @@ -16,9 +19,9 @@ using namespace std; // mapWallet // -std::vector CWallet::GenerateNewKey() +std::vector CWallet::GenerateNewKey(bool bCompressed = true) { - bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets + bool fCompressed = bCompressed ? CanSupportFeature(FEATURE_COMPRPUBKEY) : false; // default to compressed public keys if we want 0.6.0 wallets RandAddSeedPerfmon(); CKey key; @@ -69,6 +72,10 @@ bool CWallet::AddCScript(const CScript& redeemScript) return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript); } +// ppcoin: optional setting to unlock wallet for block minting only; +// serves to disable the trivial sendmoney when OS account compromised +bool fWalletUnlockMintOnly = false; + bool CWallet::Unlock(const SecureString& strWalletPassphrase) { if (!IsLocked()) @@ -242,7 +249,8 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) if (fFileBacked) { pwalletdbEncryption = new CWalletDB(strWalletFile); - pwalletdbEncryption->TxnBegin(); + if (!pwalletdbEncryption->TxnBegin()) + return false; pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey); } @@ -293,7 +301,7 @@ void CWallet::WalletUpdateSpent(const CTransaction &tx) CWalletTx& wtx = (*mi).second; if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n])) { - printf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); + printf("WalletUpdateSpent found spent coin %sppc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); wtx.MarkSpent(txin.prevout.n); wtx.WriteToDisk(); vWalletUpdated.push_back(txin.prevout.hash); @@ -484,7 +492,7 @@ int CWalletTx::GetRequestCount() const int nRequests = -1; { LOCK(pwallet->cs_wallet); - if (IsCoinBase()) + if (IsCoinBase() || IsCoinStake()) { // Generated block if (hashBlock != 0) @@ -525,7 +533,7 @@ void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, l listSent.clear(); strSentAccount = strFromAccount; - if (IsCoinBase()) + if (IsCoinBase() || IsCoinStake()) { if (GetBlocksToMaturity() > 0) nGeneratedImmature = pwallet->GetCredit(*this); @@ -716,7 +724,7 @@ void CWallet::ReacceptWalletTransactions() BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) { CWalletTx& wtx = item.second; - if (wtx.IsCoinBase() && wtx.IsSpent(0)) + if ((wtx.IsCoinBase() && wtx.IsSpent(0)) || (wtx.IsCoinStake() && wtx.IsSpent(1))) continue; CTxIndex txindex; @@ -742,7 +750,7 @@ void CWallet::ReacceptWalletTransactions() } if (fUpdated) { - printf("ReacceptWalletTransactions found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); + printf("ReacceptWalletTransactions found spent coin %sppc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); wtx.MarkDirty(); wtx.WriteToDisk(); } @@ -750,7 +758,7 @@ void CWallet::ReacceptWalletTransactions() else { // Reaccept any txes of ours that aren't already in a block - if (!wtx.IsCoinBase()) + if (!(wtx.IsCoinBase() || wtx.IsCoinStake())) wtx.AcceptWalletTransaction(txdb, false); } } @@ -767,14 +775,14 @@ void CWalletTx::RelayWalletTransaction(CTxDB& txdb) { BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) { - if (!tx.IsCoinBase()) + if (!(tx.IsCoinBase() || tx.IsCoinStake())) { uint256 hash = tx.GetHash(); if (!txdb.ContainsTx(hash)) RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx); } } - if (!IsCoinBase()) + if (!(IsCoinBase() || IsCoinStake())) { uint256 hash = GetHash(); if (!txdb.ContainsTx(hash)) @@ -827,7 +835,10 @@ void CWallet::ResendWalletTransactions() BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) { CWalletTx& wtx = *item.second; - wtx.RelayWalletTransaction(txdb); + if (wtx.CheckTransaction()) + wtx.RelayWalletTransaction(txdb); + else + printf("ResendWalletTransactions() : CheckTransaction failed for transaction %s\n", wtx.GetHash().ToString().c_str()); } } } @@ -876,7 +887,35 @@ int64 CWallet::GetUnconfirmedBalance() const return nTotal; } -bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set >& setCoinsRet, int64& nValueRet) const +// ppcoin: total coins staked (non-spendable until maturity) +int64 CWallet::GetStake() const +{ + int64 nTotal = 0; + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0) + nTotal += CWallet::GetCredit(*pcoin); + } + return nTotal; +} + +int64 CWallet::GetNewMint() const +{ + int64 nTotal = 0; + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0) + nTotal += CWallet::GetCredit(*pcoin); + } + return nTotal; +} + + +bool CWallet::SelectCoinsMinConf(int64 nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, set >& setCoinsRet, int64& nValueRet) const { setCoinsRet.clear(); nValueRet = 0; @@ -901,7 +940,7 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfThe if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) continue; - if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + if ((pcoin->IsCoinBase() || pcoin->IsCoinStake()) && pcoin->GetBlocksToMaturity() > 0) continue; int nDepth = pcoin->GetDepthInMainChain(); @@ -913,6 +952,9 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfThe if (pcoin->IsSpent(i) || !IsMine(pcoin->vout[i])) continue; + if (pcoin->nTime > nSpendTime) + continue; // ppcoin: timestamp must not exceed spend time + int64 n = pcoin->vout[i].nValue; if (n <= 0) @@ -1011,21 +1053,24 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfThe } //// debug print - printf("SelectCoins() best subset: "); - for (unsigned int i = 0; i < vValue.size(); i++) - if (vfBest[i]) - printf("%s ", FormatMoney(vValue[i].first).c_str()); - printf("total %s\n", FormatMoney(nBest).c_str()); + if (fDebug && GetBoolArg("-printselectcoin")) + { + printf("SelectCoins() best subset: "); + for (unsigned int i = 0; i < vValue.size(); i++) + if (vfBest[i]) + printf("%s ", FormatMoney(vValue[i].first).c_str()); + printf("total %s\n", FormatMoney(nBest).c_str()); + } } return true; } -bool CWallet::SelectCoins(int64 nTargetValue, set >& setCoinsRet, int64& nValueRet) const +bool CWallet::SelectCoins(int64 nTargetValue, unsigned int nSpendTime, set >& setCoinsRet, int64& nValueRet) const { - return (SelectCoinsMinConf(nTargetValue, 1, 6, setCoinsRet, nValueRet) || - SelectCoinsMinConf(nTargetValue, 1, 1, setCoinsRet, nValueRet) || - SelectCoinsMinConf(nTargetValue, 0, 1, setCoinsRet, nValueRet)); + return (SelectCoinsMinConf(nTargetValue, nSpendTime, 1, 6, setCoinsRet, nValueRet) || + SelectCoinsMinConf(nTargetValue, nSpendTime, 1, 1, setCoinsRet, nValueRet) || + SelectCoinsMinConf(nTargetValue, nSpendTime, 0, 1, setCoinsRet, nValueRet)); } @@ -1066,7 +1111,7 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW // Choose coins to use set > setCoins; int64 nValueIn = 0; - if (!SelectCoins(nTotalValue, setCoins, nValueIn)) + if (!SelectCoins(nTotalValue, wtxNew.nTime, setCoins, nValueIn)) return false; BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { @@ -1085,6 +1130,13 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW nFeeRet += nMoveToFee; } + // ppcoin: sub-cent change is moved to fee + if (nChange > 0 && nChange < MIN_TXOUT_AMOUNT) + { + nFeeRet += nChange; + nChange = 0; + } + if (nChange > 0) { // Note: We use a new key here to keep it from being obvious which side is the change. @@ -1129,8 +1181,7 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW // Check that enough fee is included int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000); - bool fAllowFree = CTransaction::AllowFree(dPriority); - int64 nMinFee = wtxNew.GetMinFee(1, fAllowFree, GMF_SEND); + int64 nMinFee = wtxNew.GetMinFee(1, false, GMF_SEND); if (nFeeRet < max(nPayFee, nMinFee)) { nFeeRet = max(nPayFee, nMinFee); @@ -1155,6 +1206,197 @@ bool CWallet::CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& w return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet); } +// ppcoin: create coin stake transaction +bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int64 nSearchInterval, CTransaction& txNew) +{ + // The following split & combine thresholds are important to security + // Should not be adjusted if you don't understand the consequences + static unsigned int nStakeSplitAge = (60 * 60 * 24 * 90); + int64 nCombineThreshold = GetProofOfWorkReward(GetLastBlockIndex(pindexBest, false)->nBits) / 3; + + CBigNum bnTargetPerCoinDay; + bnTargetPerCoinDay.SetCompact(nBits); + + LOCK2(cs_main, cs_wallet); + txNew.vin.clear(); + txNew.vout.clear(); + // Mark coin stake transaction + CScript scriptEmpty; + scriptEmpty.clear(); + txNew.vout.push_back(CTxOut(0, scriptEmpty)); + // Choose coins to use + int64 nBalance = GetBalance(); + int64 nReserveBalance = 0; + if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance)) + return error("CreateCoinStake : invalid reserve balance amount"); + if (nBalance <= nReserveBalance) + return false; + set > setCoins; + vector vwtxPrev; + int64 nValueIn = 0; + if (!SelectCoins(nBalance - nReserveBalance, txNew.nTime, setCoins, nValueIn)) + return false; + if (setCoins.empty()) + return false; + int64 nCredit = 0; + CScript scriptPubKeyKernel; + BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) + { + CTxDB txdb("r"); + CTxIndex txindex; + if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex)) + continue; + + // Read block header + CBlock block; + if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) + continue; + static int nMaxStakeSearchInterval = 60; + if (block.GetBlockTime() + nStakeMinAge > txNew.nTime - nMaxStakeSearchInterval) + continue; // only count coins meeting min age requirement + + bool fKernelFound = false; + for (unsigned int n=0; nGetHash(), pcoin.second); + if (CheckStakeKernelHash(nBits, block, txindex.pos.nTxPos - txindex.pos.nBlockPos, *pcoin.first, prevoutStake, txNew.nTime - n, hashProofOfStake)) + { + // Found a kernel + if (fDebug && GetBoolArg("-printcoinstake")) + printf("CreateCoinStake : kernel found\n"); + vector vSolutions; + txnouttype whichType; + CScript scriptPubKeyOut; + scriptPubKeyKernel = pcoin.first->vout[pcoin.second].scriptPubKey; + if (!Solver(scriptPubKeyKernel, whichType, vSolutions)) + { + if (fDebug && GetBoolArg("-printcoinstake")) + printf("CreateCoinStake : failed to parse kernel\n", whichType); + break; + } + if (fDebug && GetBoolArg("-printcoinstake")) + printf("CreateCoinStake : parsed kernel type=%d\n", whichType); + if (whichType != TX_PUBKEY && whichType != TX_PUBKEYHASH) + { + if (fDebug && GetBoolArg("-printcoinstake")) + printf("CreateCoinStake : no support for kernel type=%d\n", whichType); + break; // only support pay to public key and pay to address + } + if (whichType == TX_PUBKEYHASH) // pay to address type + { + // convert to pay to public key type + CKey key; + if (!keystore.GetKey(uint160(vSolutions[0]), key)) + { + if (fDebug && GetBoolArg("-printcoinstake")) + printf("CreateCoinStake : failed to get key for kernel type=%d\n", whichType); + break; // unable to find corresponding public key + } + scriptPubKeyOut << key.GetPubKey() << OP_CHECKSIG; + } + else + scriptPubKeyOut = scriptPubKeyKernel; + + txNew.nTime -= n; + txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second)); + nCredit += pcoin.first->vout[pcoin.second].nValue; + vwtxPrev.push_back(pcoin.first); + txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); + if (block.GetBlockTime() + nStakeSplitAge > txNew.nTime) + txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); //split stake + if (fDebug && GetBoolArg("-printcoinstake")) + printf("CreateCoinStake : added kernel type=%d\n", whichType); + fKernelFound = true; + break; + } + } + if (fKernelFound || fShutdown) + break; // if kernel is found stop searching + } + if (nCredit == 0 || nCredit > nBalance - nReserveBalance) + return false; + BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) + { + // Attempt to add more inputs + // Only add coins of the same key/address as kernel + if (txNew.vout.size() == 2 && ((pcoin.first->vout[pcoin.second].scriptPubKey == scriptPubKeyKernel || pcoin.first->vout[pcoin.second].scriptPubKey == txNew.vout[1].scriptPubKey)) + && pcoin.first->GetHash() != txNew.vin[0].prevout.hash) + { + // Stop adding more inputs if already too many inputs + if (txNew.vin.size() >= 100) + break; + // Stop adding more inputs if value is already pretty significant + if (nCredit > nCombineThreshold) + break; + // Stop adding inputs if reached reserve limit + if (nCredit + pcoin.first->vout[pcoin.second].nValue > nBalance - nReserveBalance) + break; + // Do not add additional significant input + if (pcoin.first->vout[pcoin.second].nValue > nCombineThreshold) + continue; + // Do not add input that is still too young + if (pcoin.first->nTime + STAKE_MAX_AGE > txNew.nTime) + continue; + txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second)); + nCredit += pcoin.first->vout[pcoin.second].nValue; + vwtxPrev.push_back(pcoin.first); + } + } + // Calculate coin age reward + { + uint64 nCoinAge; + CTxDB txdb("r"); + if (!txNew.GetCoinAge(txdb, nCoinAge)) + return error("CreateCoinStake : failed to calculate coin age"); + nCredit += GetProofOfStakeReward(nCoinAge); + } + + int64 nMinFee = 0; + loop + { + // Set output amount + if (txNew.vout.size() == 3) + { + txNew.vout[1].nValue = ((nCredit - nMinFee) / 2 / CENT) * CENT; + txNew.vout[2].nValue = nCredit - nMinFee - txNew.vout[1].nValue; + } + else + txNew.vout[1].nValue = nCredit - nMinFee; + + // Sign + int nIn = 0; + BOOST_FOREACH(const CWalletTx* pcoin, vwtxPrev) + { + if (!SignSignature(*this, *pcoin, txNew, nIn++)) + return error("CreateCoinStake : failed to sign coinstake"); + } + + // Limit size + unsigned int nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION); + if (nBytes >= MAX_BLOCK_SIZE_GEN/5) + return error("CreateCoinStake : exceeded coinstake size limit"); + + // Check enough fee is paid + if (nMinFee < txNew.GetMinFee() - MIN_TX_FEE) + { + nMinFee = txNew.GetMinFee() - MIN_TX_FEE; + continue; // try signing again + } + else + { + if (fDebug && GetBoolArg("-printfee")) + printf("CreateCoinStake : fee for coinstake %s\n", FormatMoney(nMinFee).c_str()); + break; + } + } + + // Successfully generated coinstake + return true; +} + // Call after CreateTransaction unless you want to abort bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) { @@ -1219,6 +1461,12 @@ string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, printf("SendMoney() : %s", strError.c_str()); return strError; } + if (fWalletUnlockMintOnly) + { + string strError = _("Error: Wallet unlocked for block minting only, unable to create transaction."); + printf("SendMoney() : %s", strError.c_str()); + return strError; + } if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired)) { string strError; @@ -1310,10 +1558,15 @@ void CWallet::PrintWallet(const CBlock& block) { { LOCK(cs_wallet); - if (mapWallet.count(block.vtx[0].GetHash())) + if (block.IsProofOfWork() && mapWallet.count(block.vtx[0].GetHash())) { CWalletTx& wtx = mapWallet[block.vtx[0].GetHash()]; - printf(" mine: %d %d %d", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit()); + printf(" mine: %d %d %s", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), FormatMoney(wtx.GetCredit()).c_str()); + } + if (block.IsProofOfStake() && mapWallet.count(block.vtx[1].GetHash())) + { + CWalletTx& wtx = mapWallet[block.vtx[1].GetHash()]; + printf(" stake: %d %d %s", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), FormatMoney(wtx.GetCredit()).c_str()); } } printf("\n"); @@ -1429,7 +1682,8 @@ void CWallet::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool) if (!HaveKey(Hash160(keypool.vchPubKey))) throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool"); assert(!keypool.vchPubKey.empty()); - printf("keypool reserve %"PRI64d"\n", nIndex); + if (fDebug && GetBoolArg("-printkeypool")) + printf("keypool reserve %"PRI64d"\n", nIndex); } } @@ -1466,7 +1720,8 @@ void CWallet::ReturnKey(int64 nIndex) LOCK(cs_wallet); setKeyPool.insert(nIndex); } - printf("keypool return %"PRI64d"\n", nIndex); + if (fDebug && GetBoolArg("-printkeypool")) + printf("keypool return %"PRI64d"\n", nIndex); } bool CWallet::GetKeyFromPool(vector& result, bool fAllowReuse) @@ -1504,6 +1759,78 @@ int64 CWallet::GetOldestKeyPoolTime() return keypool.nTime; } +// ppcoin: check 'spent' consistency between wallet and txindex +// ppcoin: fix wallet spent state according to txindex +void CWallet::FixSpentCoins(int& nMismatchFound, int64& nBalanceInQuestion, bool fCheckOnly) +{ + nMismatchFound = 0; + nBalanceInQuestion = 0; + + LOCK(cs_wallet); + vector vCoins; + vCoins.reserve(mapWallet.size()); + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + vCoins.push_back(&(*it).second); + + CTxDB txdb("r"); + BOOST_FOREACH(CWalletTx* pcoin, vCoins) + { + // Find the corresponding transaction index + CTxIndex txindex; + if (!txdb.ReadTxIndex(pcoin->GetHash(), txindex)) + continue; + for (int n=0; n < pcoin->vout.size(); n++) + { + if (IsMine(pcoin->vout[n]) && pcoin->IsSpent(n) && (txindex.vSpent.size() <= n || txindex.vSpent[n].IsNull())) + { + printf("FixSpentCoins found lost coin %sppc %s[%d], %s\n", + FormatMoney(pcoin->vout[n].nValue).c_str(), pcoin->GetHash().ToString().c_str(), n, fCheckOnly? "repair not attempted" : "repairing"); + nMismatchFound++; + nBalanceInQuestion += pcoin->vout[n].nValue; + if (!fCheckOnly) + { + pcoin->MarkUnspent(n); + pcoin->WriteToDisk(); + } + } + else if (IsMine(pcoin->vout[n]) && !pcoin->IsSpent(n) && (txindex.vSpent.size() > n && !txindex.vSpent[n].IsNull())) + { + printf("FixSpentCoins found spent coin %sppc %s[%d], %s\n", + FormatMoney(pcoin->vout[n].nValue).c_str(), pcoin->GetHash().ToString().c_str(), n, fCheckOnly? "repair not attempted" : "repairing"); + nMismatchFound++; + nBalanceInQuestion += pcoin->vout[n].nValue; + if (!fCheckOnly) + { + pcoin->MarkSpent(n); + pcoin->WriteToDisk(); + } + } + } + } +} + +// ppcoin: disable transaction (only for coinstake) +void CWallet::DisableTransaction(const CTransaction &tx) +{ + if (!tx.IsCoinStake() || !IsFromMe(tx)) + return; // only disconnecting coinstake requires marking input unspent + + LOCK(cs_wallet); + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + map::iterator mi = mapWallet.find(txin.prevout.hash); + if (mi != mapWallet.end()) + { + CWalletTx& prev = (*mi).second; + if (txin.prevout.n < prev.vout.size() && IsMine(prev.vout[txin.prevout.n])) + { + prev.MarkUnspent(txin.prevout.n); + prev.WriteToDisk(); + } + } + } +} + vector CReserveKey::GetReservedKey() { if (nIndex == -1)