// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
-// file license.txt or http://www.opensource.org/licenses/mit-license.php.
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "headers.h"
-#include "db.h"
+#include "wallet.h"
+#include "walletdb.h"
#include "crypter.h"
+#include "checkpoints.h"
+#include "ui_interface.h"
using namespace std;
return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript);
}
+// ppcoin: optional setting to create coinstake only when unlocked;
+// serves to disable the trivial sendmoney when OS account compromised
+bool fWalletUnlockMintOnly = false;
+
bool CWallet::Unlock(const SecureString& strWalletPassphrase)
{
if (!IsLocked())
return false;
}
+void CWallet::SetBestChain(const CBlockLocator& loc)
+{
+ CWalletDB walletdb(strWalletFile);
+ walletdb.WriteBestBlock(loc);
+}
// This class implements an addrIncoming entry that causes pre-0.4
// clients to crash on startup if reading a private-key-encrypted wallet.
if (fFileBacked)
{
pwalletdbEncryption = new CWalletDB(strWalletFile);
- pwalletdbEncryption->TxnBegin();
+ if (!pwalletdbEncryption->TxnBegin())
+ return false;
pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey);
}
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);
int nRequests = -1;
{
LOCK(pwallet->cs_wallet);
- if (IsCoinBase())
+ if (IsCoinBase() || IsCoinStake())
{
// Generated block
if (hashBlock != 0)
listSent.clear();
strSentAccount = strFromAccount;
- if (IsCoinBase())
+ if (IsCoinBase() || IsCoinStake())
{
if (GetBlocksToMaturity() > 0)
nGeneratedImmature = pwallet->GetCredit(*this);
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;
}
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();
}
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);
}
}
{
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))
return nTotal;
}
-bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
+// ppcoin: total coins staked (non-spendable until maturity)
+int64 CWallet::GetStake() const
+{
+ int64 nTotal = 0;
+ LOCK(cs_wallet);
+ for (map<uint256, CWalletTx>::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<uint256, CWalletTx>::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<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
{
setCoinsRet.clear();
nValueRet = 0;
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();
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)
}
//// 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<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
+bool CWallet::SelectCoins(int64 nTargetValue, unsigned int nSpendTime, set<pair<const CWalletTx*,unsigned int> >& 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));
}
// Choose coins to use
set<pair<const CWalletTx*,unsigned int> > 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)
{
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.
return false;
// Limit size
- unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK);
+ unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK, PROTOCOL_VERSION);
if (nBytes >= MAX_BLOCK_SIZE_GEN/5)
return false;
dPriority /= nBytes;
// 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);
return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet);
}
+// ppcoin: create coin stake transaction
+bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, 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<pair<const CWalletTx*,unsigned int> > setCoins;
+ vector<const CWalletTx*> 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;
+ if (block.GetBlockTime() + nStakeMinAge > txNew.nTime)
+ continue; // only count coins meeting min age requirement
+
+ int64 nValueIn = pcoin.first->vout[pcoin.second].nValue;
+ CBigNum bnCoinDay = CBigNum(nValueIn) * min(txNew.nTime-pcoin.first->nTime, (unsigned int)STAKE_MAX_AGE) / COIN / (24 * 60 * 60);
+ // Calculate hash
+ CDataStream ss(SER_GETHASH, 0);
+ ss << nBits << block.nTime << (txindex.pos.nTxPos - txindex.pos.nBlockPos) << pcoin.first->nTime << pcoin.second << txNew.nTime;
+ if (CBigNum(Hash(ss.begin(), ss.end())) <= bnCoinDay * bnTargetPerCoinDay)
+ {
+ // Found a kernel
+ if (fDebug && GetBoolArg("-printcoinstake"))
+ printf("CreateCoinStake : kernel found\n");
+ vector<valtype> 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);
+ continue;
+ }
+ 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);
+ continue; // 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);
+ continue; // unable to find corresponding public key
+ }
+ scriptPubKeyOut << key.GetPubKey() << OP_CHECKSIG;
+ }
+ else
+ scriptPubKeyOut = scriptPubKeyKernel;
+
+ 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);
+ break;
+ }
+ }
+ 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 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;
+ 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 false;
+
+ // 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)
{
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;
{
{
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");
CWalletDB walletdb(strWalletFile);
// Top up key pool
- int64 nTargetSize = max(GetArg("-keypool", 100), (int64)0);
- while (setKeyPool.size() < nTargetSize+1)
+ unsigned int nTargetSize = max(GetArg("-keypool", 100), 0LL);
+ while (setKeyPool.size() < (nTargetSize + 1))
{
int64 nEnd = 1;
if (!setKeyPool.empty())
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);
}
}
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<unsigned char>& result, bool fAllowReuse)
return keypool.nTime;
}
+// ppcoin: check 'spent' consistency between wallet and txindex
+bool CWallet::CheckSpentCoins(int& nMismatchFound, int64& nBalanceInQuestion)
+{
+ nMismatchFound = 0;
+ nBalanceInQuestion = 0;
+
+ LOCK(cs_wallet);
+ vector<const CWalletTx*> vCoins;
+ vCoins.reserve(mapWallet.size());
+ for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ vCoins.push_back(&(*it).second);
+
+ CTxDB txdb("r");
+ BOOST_FOREACH(const 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("CheckSpentCoins found lost coin %sppc %s[%d]\n", FormatMoney(pcoin->vout[n].nValue).c_str(), pcoin->GetHash().ToString().c_str(), n);
+ nMismatchFound++;
+ nBalanceInQuestion += pcoin->vout[n].nValue;
+ }
+ else if (IsMine(pcoin->vout[n]) && !pcoin->IsSpent(n) && (txindex.vSpent.size() > n && !txindex.vSpent[n].IsNull()))
+ {
+ printf("CheckSpentCoins found spent coin %sppc %s[%d]\n", FormatMoney(pcoin->vout[n].nValue).c_str(), pcoin->GetHash().ToString().c_str(), n);
+ nMismatchFound++;
+ nBalanceInQuestion += pcoin->vout[n].nValue;
+ }
+ }
+ }
+ return (nMismatchFound == 0);
+}
+
+// ppcoin: fix wallet spent state according to txindex
+void CWallet::FixSpentCoins(int& nMismatchFound, int64& nBalanceInQuestion)
+{
+ nMismatchFound = 0;
+ nBalanceInQuestion = 0;
+
+ LOCK(cs_wallet);
+ vector<CWalletTx*> vCoins;
+ vCoins.reserve(mapWallet.size());
+ for (map<uint256, CWalletTx>::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]\n", FormatMoney(pcoin->vout[n].nValue).c_str(), pcoin->GetHash().ToString().c_str(), n);
+ nMismatchFound++;
+ nBalanceInQuestion += pcoin->vout[n].nValue;
+ 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]\n", FormatMoney(pcoin->vout[n].nValue).c_str(), pcoin->GetHash().ToString().c_str(), n);
+ nMismatchFound++;
+ nBalanceInQuestion += pcoin->vout[n].nValue;
+ 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<uint256, CWalletTx>::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<unsigned char> CReserveKey::GetReservedKey()
{
if (nIndex == -1)