X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fmain.cpp;h=cb85469af597e9411513ce10d5808439ab4f670f;hb=4f6b3161b2dc18c9a3d4143d63fbbd0737296eb0;hp=d1fa019475ace348616ddcdca8c72fadff94cd2a;hpb=b572a18b13d573e4dabc861cc6fa0699b2d22c96;p=novacoin.git diff --git a/src/main.cpp b/src/main.cpp index d1fa019..cb85469 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,11 +32,13 @@ map mapBlockIndex; set > setStakeSeen; uint256 hashGenesisBlock = hashGenesisBlockOfficial; static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32); -static CBigNum bnInitialHashTarget(~uint256(0) >> 36); +static CBigNum bnInitialHashTarget(~uint256(0) >> 40); +unsigned int nStakeMinAge = STAKE_MIN_AGE; +int nCoinbaseMaturity = COINBASE_MATURITY_PPC; CBlockIndex* pindexGenesisBlock = NULL; int nBestHeight = -1; -uint64 nBestChainTrust = 0; -uint64 nBestInvalidTrust = 0; +CBigNum bnBestChainTrust = 0; +CBigNum bnBestInvalidTrust = 0; uint256 hashBestChain = 0; CBlockIndex* pindexBest = NULL; int64 nTimeBestReceived = 0; @@ -709,7 +711,7 @@ int CMerkleTx::GetBlocksToMaturity() const { if (!(IsCoinBase() || IsCoinStake())) return 0; - return max(0, (COINBASE_MATURITY+20) - GetDepthInMainChain()); + return max(0, (nCoinbaseMaturity+20) - GetDepthInMainChain()); } @@ -822,7 +824,7 @@ uint256 WantedByOrphan(const CBlock* pblockOrphan) return pblockOrphan->hashPrevBlock; } -int64 static GetProofOfWorkReward(unsigned int nBits) +int64 GetProofOfWorkReward(unsigned int nBits) { CBigNum bnSubsidyLimit = MAX_MINT_PROOF_OF_WORK; CBigNum bnTarget; @@ -965,14 +967,14 @@ bool IsInitialBlockDownload() void static InvalidChainFound(CBlockIndex* pindexNew) { - if (pindexNew->nChainTrust > nBestInvalidTrust) + if (pindexNew->bnChainTrust > bnBestInvalidTrust) { - nBestInvalidTrust = pindexNew->nChainTrust; - CTxDB().WriteBestInvalidTrust(nBestInvalidTrust); + bnBestInvalidTrust = pindexNew->bnChainTrust; + CTxDB().WriteBestInvalidTrust(bnBestInvalidTrust); MainFrameRepaint(); } - printf("InvalidChainFound: invalid block=%s height=%d trust=%s\n", pindexNew->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->nHeight, CBigNum(pindexNew->nChainTrust).ToString().c_str()); - printf("InvalidChainFound: current best=%s height=%d trust=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, CBigNum(nBestChainTrust).ToString().c_str()); + printf("InvalidChainFound: invalid block=%s height=%d trust=%s\n", pindexNew->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->nHeight, CBigNum(pindexNew->bnChainTrust).ToString().c_str()); + printf("InvalidChainFound: current best=%s height=%d trust=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, CBigNum(bnBestChainTrust).ToString().c_str()); // ppcoin: should not enter safe mode for longer invalid chain } @@ -1168,7 +1170,7 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, MapPrevTx inputs, // If prev is coinbase/coinstake, check that it's matured if (txPrev.IsCoinBase() || txPrev.IsCoinStake()) - for (const CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < COINBASE_MATURITY; pindex = pindex->pprev) + for (const CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < nCoinbaseMaturity; pindex = pindex->pprev) if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile) return error("ConnectInputs() : tried to spend coinbase/coinstake at depth %d", pindexBlock->nHeight - pindex->nHeight); @@ -1232,7 +1234,7 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, MapPrevTx inputs, if (!GetCoinAge(txdb, nCoinAge)) return error("ConnectInputs() : %s unable to get coin age for coinstake", GetHash().ToString().substr(0,10).c_str()); int64 nStakeReward = GetValueOut() - nValueIn; - if (nStakeReward > GetProofOfStakeReward(nCoinAge)) + if (nStakeReward > GetProofOfStakeReward(nCoinAge) - GetMinFee() + MIN_TX_FEE) return DoS(100, error("ConnectInputs() : %s stake reward exceeded", GetHash().ToString().substr(0,10).c_str())); } else @@ -1369,6 +1371,8 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) map mapQueuedChanges; int64 nFees = 0; + int64 nValueIn = 0; + int64 nValueOut = 0; unsigned int nSigOps = 0; BOOST_FOREACH(CTransaction& tx, vtx) { @@ -1380,7 +1384,9 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) nTxPos += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); MapPrevTx mapInputs; - if (!tx.IsCoinBase()) + if (tx.IsCoinBase()) + nValueOut += tx.GetValueOut(); + else { bool fInvalid; if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs, fInvalid)) @@ -1396,8 +1402,12 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) return DoS(100, error("ConnectBlock() : too many sigops")); } + int64 nTxValueIn = tx.GetValueIn(mapInputs); + int64 nTxValueOut = tx.GetValueOut(); + nValueIn += nTxValueIn; + nValueOut += nTxValueOut; if (!tx.IsCoinStake()) - nFees += tx.GetValueIn(mapInputs)-tx.GetValueOut(); + nFees += nTxValueIn - nTxValueOut; if (!tx.ConnectInputs(txdb, mapInputs, mapQueuedChanges, posThisTx, pindex, true, false, fStrictPayToScriptHash)) return false; @@ -1406,6 +1416,10 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) mapQueuedChanges[tx.GetHash()] = CTxIndex(posThisTx, tx.vout.size()); } + // ppcoin: track money supply + pindex->nMint = nValueOut - nValueIn + nFees; + pindex->nMoneySupply = (pindex->pprev? pindex->pprev->nMoneySupply : 0) + nValueOut - nValueIn; + // Write queued txindex changes for (map::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi) { @@ -1415,8 +1429,6 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) // ppcoin: fees are not collected by miners as in bitcoin // ppcoin: fees are destroyed to compensate the entire network - if (IsProofOfWork() && vtx[0].GetValueOut() > GetProofOfWorkReward(nBits)) - return false; if (fDebug && GetBoolArg("-printcreation")) printf("ConnectBlock() : destroy=%s nFees=%"PRI64d"\n", FormatMoney(nFees).c_str(), nFees); @@ -1597,7 +1609,7 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) // Reorganize is costly in terms of db load, as it works in a single db transaction. // Try to limit how much needs to be done inside - while (pindexIntermediate->pprev && pindexIntermediate->pprev->nChainTrust > pindexBest->nChainTrust) + while (pindexIntermediate->pprev && pindexIntermediate->pprev->bnChainTrust > pindexBest->bnChainTrust) { vpindexSecondary.push_back(pindexIntermediate); pindexIntermediate = pindexIntermediate->pprev; @@ -1645,10 +1657,10 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) hashBestChain = hash; pindexBest = pindexNew; nBestHeight = pindexBest->nHeight; - nBestChainTrust = pindexNew->nChainTrust; + bnBestChainTrust = pindexNew->bnChainTrust; nTimeBestReceived = GetTime(); nTransactionsUpdated++; - printf("SetBestChain: new best=%s height=%d trust=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, CBigNum(nBestChainTrust).ToString().c_str()); + printf("SetBestChain: new best=%s height=%d trust=%s moneysupply=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainTrust.ToString().c_str(), FormatMoney(pindexBest->nMoneySupply).c_str()); std::string strCmd = GetArg("-blocknotify", ""); @@ -1663,7 +1675,7 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) // ppcoin: coinstake must meet hash target according to the protocol: -// input 0 must meet the formula +// kernel (input 0) must meet the formula // hash(nBits + txPrev.block.nTime + txPrev.offset + txPrev.nTime + txPrev.vout.n + nTime) < bnTarget * nCoinDay // this ensures that the chance of getting a coinstake is proportional to the // amount of coin age one owns. @@ -1690,7 +1702,7 @@ bool CTransaction::CheckProofOfStake(unsigned int nBits) const if (!IsCoinStake()) return true; - // Input 0 must match the stake hash target per coin age (nBits) + // Kernel (input 0) must match the stake hash target per coin age (nBits) const CTxIn& txin = vin[0]; // First try finding the previous transaction in database @@ -1711,11 +1723,11 @@ bool CTransaction::CheckProofOfStake(unsigned int nBits) const CBlock block; if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) return false; // unable to read block of previous transaction - if (block.GetBlockTime() + STAKE_MIN_AGE > nTime) + if (block.GetBlockTime() + nStakeMinAge > nTime) return false; // only count coins meeting min age requirement int64 nValueIn = txPrev.vout[txin.prevout.n].nValue; - CBigNum bnCoinDay = CBigNum(nValueIn) * (nTime-txPrev.nTime) / COIN / (24 * 60 * 60); + CBigNum bnCoinDay = CBigNum(nValueIn) * min(nTime-txPrev.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) << txPrev.nTime << txin.prevout.n << nTime; @@ -1754,7 +1766,7 @@ bool CTransaction::GetCoinAge(CTxDB& txdb, uint64& nCoinAge) const CBlock block; if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) return false; // unable to read block of previous transaction - if (block.GetBlockTime() + STAKE_MIN_AGE > nTime) + if (block.GetBlockTime() + nStakeMinAge > nTime) continue; // only count coins meeting min age requirement int64 nValueIn = txPrev.vout[txin.prevout.n].nValue; @@ -1818,10 +1830,7 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos) } // ppcoin: compute chain trust score - uint64 nCoinAge; - if (!GetCoinAge(nCoinAge)) - return error("AddToBlockIndex() : invalid transaction in block"); - pindexNew->nChainTrust = (pindexNew->pprev ? pindexNew->pprev->nChainTrust : 0) + nCoinAge; + pindexNew->bnChainTrust = (pindexNew->pprev ? pindexNew->pprev->bnChainTrust : 0) + pindexNew->GetBlockTrust(); CTxDB txdb; if (!txdb.TxnBegin()) @@ -1831,10 +1840,17 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos) return false; // New best - if (pindexNew->nChainTrust > nBestChainTrust) + if (pindexNew->bnChainTrust > bnBestChainTrust) if (!SetBestChain(txdb, pindexNew)) return false; + // ppcoin: got mint/moneysupply info in block index, write to db + if (!txdb.TxnBegin()) + return false; + txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew)); + if (!txdb.TxnCommit()) + return false; + txdb.Close(); if (pindexNew == pindexBest) @@ -1882,7 +1898,7 @@ bool CBlock::CheckBlock() const return DoS(100, error("CheckBlock() : coinstake in wrong position")); // ppcoin: coinbase output should be empty if proof-of-stake block - if (IsProofOfStake() && !vtx[0].vout[0].IsEmpty()) + if (IsProofOfStake() && (vtx[0].vout.size() != 1 || !vtx[0].vout[0].IsEmpty())) return error("CheckBlock() : coinbase output not empty for proof-of-stake block"); // Check coinbase timestamp @@ -1893,6 +1909,12 @@ bool CBlock::CheckBlock() const if (IsProofOfStake() && GetBlockTime() > (int64)vtx[1].nTime + nMaxClockDrift) return DoS(50, error("CheckBlock() : coinstake timestamp is too early")); + // Check coinbase reward + if (vtx[0].GetValueOut() > (IsProofOfWork()? (GetProofOfWorkReward(nBits) - vtx[0].GetMinFee() + MIN_TX_FEE) : 0)) + return DoS(50, error("CheckBlock() : coinbase reward exceeded %s > %s", + FormatMoney(vtx[0].GetValueOut()).c_str(), + FormatMoney(IsProofOfWork()? GetProofOfWorkReward(nBits) : 0).c_str())); + // Check transactions BOOST_FOREACH(const CTransaction& tx, vtx) { @@ -2222,12 +2244,14 @@ bool LoadBlockIndex(bool fAllowNew) { hashGenesisBlock = hashGenesisBlockTestNet; bnProofOfWorkLimit = CBigNum(~uint256(0) >> 28); - pchMessageStart[0] = 0xfa; - pchMessageStart[1] = 0xbf; - pchMessageStart[2] = 0xb5; - pchMessageStart[3] = 0xda; + nStakeMinAge = 60 * 60 * 24; // test net min age is 1 day + nCoinbaseMaturity = 60; + bnInitialHashTarget = CBigNum(~uint256(0) >> 29); } + printf("%s Network: genesis=0x%s nBitsLimit=0x%08x nBitsInitial=0x%08x nStakeMinAge=%d nCoinbaseMaturity=%d\n", + fTestNet? "Test" : "PPCoin", hashGenesisBlock.ToString().substr(0, 20).c_str(), bnProofOfWorkLimit.GetCompact(), bnInitialHashTarget.GetCompact(), nStakeMinAge, nCoinbaseMaturity); + // // Load block index // @@ -2308,7 +2332,7 @@ bool LoadBlockIndex(bool fAllowNew) return error("LoadBlockIndex() : failed to write new checkpoint master key to db"); if (!txdb.TxnCommit()) return error("LoadBlockIndex() : failed to commit new checkpoint master key to db"); - if (!Checkpoints::ResetSyncCheckpoint()) + if ((!fTestNet) && !Checkpoints::ResetSyncCheckpoint()) return error("LoadBlockIndex() : failed to reset sync-checkpoint"); } txdb.Close(); @@ -2364,13 +2388,14 @@ void PrintBlockTree() // print item CBlock block; block.ReadFromDisk(pindex); - printf("%d (%u,%u) %s %08lx %s tx %d", + printf("%d (%u,%u) %s %08lx %s mint %7s tx %d", pindex->nHeight, pindex->nFile, pindex->nBlockPos, - block.GetHash().ToString().substr(0,20).c_str(), + block.GetHash().ToString().c_str(), block.nBits, DateTimeStrFormat(block.GetBlockTime()).c_str(), + FormatMoney(pindex->nMint).c_str(), block.vtx.size()); PrintWallets(block); @@ -2436,7 +2461,7 @@ string GetWarnings(string strFor) // ppcoin: should not enter safe mode for longer invalid chain // ppcoin: if sync-checkpoint too old enter safe mode - if (Checkpoints::IsMatureSyncCheckpoint()) + if (Checkpoints::IsMatureSyncCheckpoint() && !fTestNet) { nPriority = 2000; strStatusBar = strRPC = "WARNING: Checkpoint is too old. Wait for block chain to download, or notify developers of the issue."; @@ -2560,12 +2585,6 @@ bool static AlreadyHave(CTxDB& txdb, const CInv& inv) -// The message start string is designed to be unlikely to occur in normal data. -// The characters are rarely used upper ascii, not valid as UTF-8, and produce -// a large 4-byte int at any alignment. -unsigned char pchMessageStart[4] = { 0xf9, 0xbe, 0xb4, 0xd9 }; - - bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { static map > mapReuseKey; @@ -2912,7 +2931,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) printf(" getblocks stopping at %d %s (%u bytes)\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str(), nBytes); // ppcoin: tell downloading node about the latest block if it's // without risk being rejected due to stake connection check - if (hashStop != hashBestChain && pindex->GetBlockTime() + STAKE_MIN_AGE > pindexBest->GetBlockTime()) + if (hashStop != hashBestChain && pindex->GetBlockTime() + nStakeMinAge > pindexBest->GetBlockTime()) pfrom->PushInventory(CInv(MSG_BLOCK, hashBestChain)); break; } @@ -3196,6 +3215,17 @@ bool ProcessMessages(CNode* pfrom) // (x) data // + unsigned char pchMessageStart[4]; + GetMessageStart(pchMessageStart); + static int64 nTimeLastPrintMessageStart = 0; + if (fDebug && GetBoolArg("-printmessagestart") && nTimeLastPrintMessageStart + 30 < GetAdjustedTime()) + { + string strMessageStart((const char *)pchMessageStart, sizeof(pchMessageStart)); + vector vchMessageStart(strMessageStart.begin(), strMessageStart.end()); + printf("ProcessMessages : AdjustedTime=%"PRI64d" MessageStart=%s\n", GetAdjustedTime(), HexStr(vchMessageStart).c_str()); + nTimeLastPrintMessageStart = GetAdjustedTime(); + } + loop { // Scan for message start @@ -3568,6 +3598,7 @@ public: uint64 nLastBlockTx = 0; uint64 nLastBlockSize = 0; +int64 nLastCoinStakeSearchInterval = 0; // CreateNewBlock: // fProofOfStake: try (best effort) to make a proof-of-stake block @@ -3591,31 +3622,23 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) pblock->vtx.push_back(txNew); // ppcoin: if coinstake available add coinstake tx - static unsigned int nLastCoinStakeCheckTime = GetAdjustedTime() - nMaxClockDrift / 2; // only initialized at startup + static int64 nLastCoinStakeSearchTime = GetAdjustedTime(); // only initialized at startup CBlockIndex* pindexPrev = pindexBest; if (fProofOfStake) // attemp to find a coinstake { - while (nLastCoinStakeCheckTime < GetAdjustedTime()) + pblock->nBits = GetNextTargetRequired(pindexPrev, true); + CTransaction txCoinStake; + int64 nSearchTime = GetAdjustedTime(); + if (nSearchTime > nLastCoinStakeSearchTime) { - pindexPrev = pindexBest; // get best block again to avoid getting stale - pblock->nBits = GetNextTargetRequired(pindexPrev, true); - CTransaction txCoinStake; - { - static CCriticalSection cs; - LOCK(cs); - // mining may have been suspended for a while so - // need to take max to satisfy the timestamp protocol - nLastCoinStakeCheckTime++; - nLastCoinStakeCheckTime = max(nLastCoinStakeCheckTime, (unsigned int) (GetAdjustedTime() - nMaxClockDrift / 2)); - txCoinStake.nTime = nLastCoinStakeCheckTime; - } - if (pwallet->CreateCoinStake(pblock->nBits, txCoinStake)) + if (pwallet->CreateCoinStake(*pwallet, pblock->nBits, nSearchTime-nLastCoinStakeSearchTime, txCoinStake)) { pblock->vtx.push_back(txCoinStake); pblock->vtx[0].vout[0].SetEmpty(); - break; } + nLastCoinStakeSearchInterval = nSearchTime - nLastCoinStakeSearchTime; + nLastCoinStakeSearchTime = nSearchTime; } } @@ -3926,9 +3949,9 @@ void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce); - // ppcoin: if proof-of-stake block found then process block if (fProofOfStake) { + // ppcoin: if proof-of-stake block found then process block if (pblock->IsProofOfStake()) { if (!pblock->SignBlock(*pwalletMain))