// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
-// Copyright (c) 2011-2012 The PPCoin 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.
#include "wallet.h"
#include "walletdb.h"
#include "crypter.h"
-#include "checkpoints.h"
#include "ui_interface.h"
+#include "kernel.h"
using namespace std;
// mapWallet
//
-std::vector<unsigned char> CWallet::GenerateNewKey()
+std::vector<unsigned char> 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;
return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript);
}
-// ppcoin: optional setting to create coinstake only when unlocked;
+// ppcoin: optional setting to unlock wallet for block minting only;
// serves to disable the trivial sendmoney when OS account compromised
bool fWalletUnlockMintOnly = false;
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());
}
}
}
}
// ppcoin: create coin stake transaction
-bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, CTransaction& txNew)
+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);
CBlock block;
if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
continue;
- if (block.GetBlockTime() + nStakeMinAge > txNew.nTime)
+ static int nMaxStakeSearchInterval = 60;
+ if (block.GetBlockTime() + nStakeMinAge > txNew.nTime - nMaxStakeSearchInterval)
continue; // only count coins meeting min age requirement
- int64 nValueIn = pcoin.first->vout[pcoin.second].nValue;
- CBigNum bnCoinDay = CBigNum(nValueIn) * (txNew.nTime-pcoin.first->nTime) / 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)
+ bool fKernelFound = false;
+ for (unsigned int n=0; n<min(nSearchInterval,(int64)nMaxStakeSearchInterval) && !fKernelFound && !fShutdown; n++)
{
- // 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))
+ // Search backward in time from the given txNew timestamp
+ // Search nSearchInterval seconds back up to nMaxStakeSearchInterval
+ uint256 hashProofOfStake = 0;
+ COutPoint prevoutStake = COutPoint(pcoin.first->GetHash(), 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 : 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)
- {
+ 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);
+ break;
+ }
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))
+ printf("CreateCoinStake : parsed kernel type=%d\n", whichType);
+ if (whichType != TX_PUBKEY && whichType != TX_PUBKEYHASH)
{
if (fDebug && GetBoolArg("-printcoinstake"))
- printf("CreateCoinStake : failed to get key for kernel type=%d\n", whichType);
- continue; // unable to find corresponding public key
+ 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;
}
- 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;
}
- 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 (fDebug && GetBoolArg("-printcoinstake"))
- printf("CreateCoinStake : added kernel type=%d\n", whichType);
- break;
}
+ if (fKernelFound || fShutdown)
+ break; // if kernel is found stop searching
}
if (nCredit == 0 || nCredit > nBalance - nReserveBalance)
return false;
- // The following combine threshold is important to network security
- // Should not be adjusted if you don't understand the consequences
- int64 nCombineThreshold = GetProofOfWorkReward(GetLastBlockIndex(pindexBest, false)->nBits);
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 ((pcoin.first->vout[pcoin.second].scriptPubKey == scriptPubKeyKernel || pcoin.first->vout[pcoin.second].scriptPubKey == txNew.vout[1].scriptPubKey)
+ 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;
// 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);
loop
{
// Set output amount
- txNew.vout[1].nValue = nCredit - nMinFee;
+ 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;
// Limit size
unsigned int nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION);
if (nBytes >= MAX_BLOCK_SIZE_GEN/5)
- return false;
+ return error("CreateCoinStake : exceeded coinstake size limit");
// Check enough fee is paid
if (nMinFee < txNew.GetMinFee() - MIN_TX_FEE)
}
// 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 (pcoin->IsSpent(n) && (txindex.vSpent.size() <= n || txindex.vSpent[n].IsNull()))
- {
- printf("CheckSpentCoins found lost coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n);
- nMismatchFound++;
- nBalanceInQuestion += pcoin->vout[n].nValue;
- }
- else if (!pcoin->IsSpent(n) && (txindex.vSpent.size() > n && !txindex.vSpent[n].IsNull()))
- {
- printf("CheckSpentCoins found spent coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).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)
+void CWallet::FixSpentCoins(int& nMismatchFound, int64& nBalanceInQuestion, bool fCheckOnly)
{
nMismatchFound = 0;
nBalanceInQuestion = 0;
continue;
for (int n=0; n < pcoin->vout.size(); n++)
{
- if (pcoin->IsSpent(n) && (txindex.vSpent.size() <= n || txindex.vSpent[n].IsNull()))
+ 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->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n);
+ 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;
- pcoin->MarkUnspent(n);
- pcoin->WriteToDisk();
+ if (!fCheckOnly)
+ {
+ pcoin->MarkUnspent(n);
+ pcoin->WriteToDisk();
+ }
}
- else if (!pcoin->IsSpent(n) && (txindex.vSpent.size() > n && !txindex.vSpent[n].IsNull()))
+ 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->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n);
+ 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;
- pcoin->MarkSpent(n);
- pcoin->WriteToDisk();
+ if (!fCheckOnly)
+ {
+ pcoin->MarkSpent(n);
+ pcoin->WriteToDisk();
+ }
}
}
}