X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fwallet.cpp;h=c585590edf30e8b52d22719f072d0e73dbaab868;hb=8da2beaee1c52c452f2677a970a76787027b1ef2;hp=6cc679be9de69ceae3675a50065acb6aadaa26eb;hpb=fe90b0fc1a93935dc82c1ff511bdd5a0b9cd57eb;p=novacoin.git diff --git a/src/wallet.cpp b/src/wallet.cpp index 6cc679b..c585590 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1203,8 +1203,13 @@ bool CWallet::CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& w } // ppcoin: create coin stake transaction -bool CWallet::CreateCoinStake(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); @@ -1230,6 +1235,7 @@ bool CWallet::CreateCoinStake(unsigned int nBits, CTransaction& txNew) if (setCoins.empty()) return false; int64 nCredit = 0; + CScript scriptPubKeyKernel; BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { CTxDB txdb("r"); @@ -1241,39 +1247,89 @@ bool CWallet::CreateCoinStake(unsigned int nBits, CTransaction& txNew) CBlock block; if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) continue; - if (block.GetBlockTime() + STAKE_MIN_AGE > txNew.nTime) + if (block.GetBlockTime() + nStakeMinAge > txNew.nTime - nMaxClockDrift) 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) + CBigNum bnCoinDay = CBigNum(nValueIn) * min(txNew.nTime-pcoin.first->nTime, (unsigned int)STAKE_MAX_AGE) / COIN / (24 * 60 * 60); + + bool fKernelFound = false; + for (int n=0; nGetHash(), pcoin.second)); - nCredit += pcoin.first->vout[pcoin.second].nValue; - vwtxPrev.push_back(pcoin.first); - // Set output scriptPubKey - txNew.vout.push_back(CTxOut(0, pcoin.first->vout[pcoin.second].scriptPubKey)); - break; + // Randomly pick a timestamp from protocol allowed range + txNew.nTime = GetAdjustedTime() - GetRandInt(nMaxClockDrift - 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 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.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; + } } + 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 of the same key - if (pcoin.first->vout[pcoin.second].scriptPubKey == txNew.vout[1].scriptPubKey && pcoin.first->GetHash() != txNew.vin[0].prevout.hash) + // 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 > MAX_MINT_PROOF_OF_WORK) + 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 > MAX_MINT_PROOF_OF_WORK) + 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; @@ -1288,21 +1344,45 @@ bool CWallet::CreateCoinStake(unsigned int nBits, CTransaction& txNew) return error("CreateCoinStake : failed to calculate coin age"); nCredit += GetProofOfStakeReward(nCoinAge); } - // Set output amount - txNew.vout[1].nValue = nCredit; - // Sign - int nIn = 0; - BOOST_FOREACH(const CWalletTx* pcoin, vwtxPrev) + int64 nMinFee = 0; + loop { - if (!SignSignature(*this, *pcoin, txNew, nIn++)) - return error("CreateCoinStake : failed to sign coinstake"); - } + // 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; - // Limit size - unsigned int nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION); - if (nBytes >= MAX_BLOCK_SIZE_GEN/5) - return false; + // 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; @@ -1671,45 +1751,8 @@ int64 CWallet::GetOldestKeyPoolTime() } // ppcoin: check 'spent' consistency between wallet and txindex -bool CWallet::CheckSpentCoins(int& nMismatchFound, int64& nBalanceInQuestion) -{ - nMismatchFound = 0; - nBalanceInQuestion = 0; - - LOCK(cs_wallet); - vector vCoins; - vCoins.reserve(mapWallet.size()); - for (map::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; @@ -1729,21 +1772,29 @@ void CWallet::FixSpentCoins(int& nMismatchFound, int64& nBalanceInQuestion) 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(); + } } } }