X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fwallet.cpp;h=f070d65faed03f9044cbef08b87926572a1ba5a7;hb=0815e9ac6304c4f1bb98e37558aa438f45dc64dc;hp=bfc57b6975dfbc06b3bcd3a6df2091f5884fbb36;hpb=e46bc223d3239393cb557ae9a768723df2a8aa4a;p=novacoin.git diff --git a/src/wallet.cpp b/src/wallet.cpp index bfc57b6..f070d65 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, 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,21 +1247,60 @@ 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) 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); + 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 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); - // Set output scriptPubKey - txNew.vout.push_back(CTxOut(0, pcoin.first->vout[pcoin.second].scriptPubKey)); + 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; } } @@ -1263,17 +1308,19 @@ bool CWallet::CreateCoinStake(unsigned int nBits, CTransaction& txNew) 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 +1335,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; @@ -1593,7 +1664,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); } } @@ -1630,7 +1702,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) @@ -1689,15 +1762,15 @@ bool CWallet::CheckSpentCoins(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("CheckSpentCoins found lost coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n); + 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 (!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("CheckSpentCoins found spent coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n); + 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; } @@ -1727,17 +1800,17 @@ 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]\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 (!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]\n", FormatMoney(pcoin->vout[n].nValue).c_str(), pcoin->GetHash().ToString().c_str(), n); nMismatchFound++; nBalanceInQuestion += pcoin->vout[n].nValue; pcoin->MarkSpent(n);