X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fmain.cpp;h=0390b9c64f95023fcd71298321b8f1be72c8b322;hb=85016efc6ded9a0b4467625a4fab047a5a4f299b;hp=c90565dab2fa772556cf0fc8fcee76052d357846;hpb=06281cf1f6e0aee12300a9aebe9d1363b2e22c90;p=novacoin.git diff --git a/src/main.cpp b/src/main.cpp index c90565d..0390b9c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,10 +32,11 @@ map mapBlockIndex; set > setStakeSeen; uint256 hashGenesisBlock = hashGenesisBlockOfficial; static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32); +static CBigNum bnInitialHashTarget(~uint256(0) >> 40); 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; @@ -455,8 +456,9 @@ bool CTransaction::CheckTransaction() const const CTxOut& txout = vout[i]; if (txout.IsEmpty() && (!IsCoinBase()) && (!IsCoinStake())) return DoS(100, error("CTransaction::CheckTransaction() : txout empty for user transaction")); - if (txout.nValue < 0) - return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue negative")); + // ppcoin: enforce minimum output amount + if ((!txout.IsEmpty()) && txout.nValue < MIN_TXOUT_AMOUNT) + return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue below minimum")); if (txout.nValue > MAX_MONEY) return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high")); nValueOut += txout.nValue; @@ -820,7 +822,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; @@ -849,7 +851,7 @@ int64 static GetProofOfWorkReward(unsigned int nBits) if (fDebug && GetBoolArg("-printcreation")) printf("GetProofOfWorkReward() : create=%s nBits=0x%08x nSubsidy=%"PRI64d"\n", FormatMoney(nSubsidy).c_str(), nBits, nSubsidy); - return nSubsidy; + return min(nSubsidy, MAX_MINT_PROOF_OF_WORK); } // ppcoin: miner's coin stake is rewarded based on coin age spent (coin-days) @@ -897,16 +899,16 @@ const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfSta unsigned int static GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake) { - // Genesis block and first block - if (pindexLast == NULL || pindexLast->pprev == NULL) - return bnProofOfWorkLimit.GetCompact(); + if (pindexLast == NULL) + return bnProofOfWorkLimit.GetCompact(); // genesis block const CBlockIndex* pindexPrev = GetLastBlockIndex(pindexLast, fProofOfStake); - if (pindexPrev == NULL) - return bnProofOfWorkLimit.GetCompact(); + if (pindexPrev->pprev == NULL) + return bnInitialHashTarget.GetCompact(); // first block const CBlockIndex* pindexPrevPrev = GetLastBlockIndex(pindexPrev->pprev, fProofOfStake); - if (pindexPrevPrev == NULL) - return bnProofOfWorkLimit.GetCompact(); + if (pindexPrevPrev->pprev == NULL) + return bnInitialHashTarget.GetCompact(); // second block + int64 nActualSpacing = pindexPrev->GetBlockTime() - pindexPrevPrev->GetBlockTime(); // ppcoin: target change every block @@ -963,14 +965,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 } @@ -1230,7 +1232,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 @@ -1367,6 +1369,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) { @@ -1378,7 +1382,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)) @@ -1394,8 +1400,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; @@ -1404,6 +1414,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) { @@ -1413,8 +1427,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); @@ -1595,7 +1607,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; @@ -1643,10 +1655,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", ""); @@ -1661,7 +1673,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. @@ -1688,7 +1700,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 @@ -1816,10 +1828,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()) @@ -1829,10 +1838,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) @@ -1880,7 +1896,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 @@ -1891,6 +1907,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) { @@ -2063,7 +2085,8 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(pblock2)); // ppcoin: getblocks may not obtain the ancestor block rejected // earlier by duplicate-stake check so we ask for it again directly - pfrom->AskFor(CInv(MSG_BLOCK, WantedByOrphan(pblock2))); + if (!IsInitialBlockDownload()) + pfrom->AskFor(CInv(MSG_BLOCK, WantedByOrphan(pblock2))); } return true; } @@ -2249,34 +2272,33 @@ bool LoadBlockIndex(bool fAllowNew) // vMerkleTree: 4a5e1e // Genesis block - const char* pszTimestamp = "MarketWatch 07/Nov/2011 Gold tops $1,790 to end at over six-week high"; + const char* pszTimestamp = "Matonis 07-AUG-2012 Parallel Currencies And The Roadmap To Monetary Freedom"; CTransaction txNew; - txNew.nTime = 1339538219; + txNew.nTime = 1345083810; txNew.vin.resize(1); txNew.vout.resize(1); - txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(4) << vector((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); + txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(9999) << vector((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); txNew.vout[0].SetEmpty(); CBlock block; block.vtx.push_back(txNew); block.hashPrevBlock = 0; block.hashMerkleRoot = block.BuildMerkleTree(); block.nVersion = 1; - block.nTime = 1339540307; + block.nTime = 1345084287; block.nBits = bnProofOfWorkLimit.GetCompact(); - block.nNonce = 1281822831; + block.nNonce = 2179302059; if (fTestNet) { - block.nTime = 1296688602; - block.nBits = 0x1d07fff8; - block.nNonce = 384568319; + block.nTime = 1345090000; + block.nNonce = 122894938; } //// debug print printf("%s\n", block.GetHash().ToString().c_str()); printf("%s\n", hashGenesisBlock.ToString().c_str()); printf("%s\n", block.hashMerkleRoot.ToString().c_str()); - assert(block.hashMerkleRoot == uint256("0x1557f46a17fcf8843dbe4c0c0edfd1d17eeff2c3c48d73a59d11f5d176e4b54d")); + assert(block.hashMerkleRoot == uint256("0x3c2d8f85fab4d17aac558cc648a1a58acff0de6deb890c29985690052c5993c2")); block.print(); assert(block.GetHash() == hashGenesisBlock); assert(block.CheckBlock()); @@ -2362,13 +2384,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 %s tx %d", pindex->nHeight, pindex->nFile, pindex->nBlockPos, block.GetHash().ToString().substr(0,20).c_str(), block.nBits, DateTimeStrFormat(block.GetBlockTime()).c_str(), + FormatMoney(pindex->nMint).c_str(), block.vtx.size()); PrintWallets(block); @@ -2407,6 +2430,9 @@ void PrintBlockTree() map mapAlerts; CCriticalSection cs_mapAlerts; +static string strMintMessage = _("Info: Minting suspended due to locked wallet."); +static string strMintWarning; + string GetWarnings(string strFor) { int nPriority = 0; @@ -2415,6 +2441,13 @@ string GetWarnings(string strFor) if (GetBoolArg("-testsafemode")) strRPC = "test"; + // ppcoin: wallet lock warning for minting + if (strMintWarning != "") + { + nPriority = 0; + strStatusBar = strMintWarning; + } + // Misc warnings like out of disk space and clock is wrong if (strMiscWarning != "") { @@ -2898,6 +2931,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (pindex->GetBlockHash() == hashStop) { 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()) + pfrom->PushInventory(CInv(MSG_BLOCK, hashBestChain)); break; } pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash())); @@ -3553,6 +3590,8 @@ public: uint64 nLastBlockTx = 0; uint64 nLastBlockSize = 0; +// CreateNewBlock: +// fProofOfStake: try (best effort) to make a proof-of-stake block CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) { CReserveKey reservekey(pwallet); @@ -3576,7 +3615,7 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) static unsigned int nLastCoinStakeCheckTime = GetAdjustedTime() - nMaxClockDrift / 2; // only initialized at startup CBlockIndex* pindexPrev = pindexBest; - if (fProofOfStake) + if (fProofOfStake) // attemp to find a coinstake { while (nLastCoinStakeCheckTime < GetAdjustedTime()) { @@ -3592,7 +3631,7 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) nLastCoinStakeCheckTime = max(nLastCoinStakeCheckTime, (unsigned int) (GetAdjustedTime() - nMaxClockDrift / 2)); txCoinStake.nTime = nLastCoinStakeCheckTime; } - if (pwallet->CreateCoinStake(pblock->nBits, txCoinStake)) + if (pwallet->CreateCoinStake(*pwallet, pblock->nBits, txCoinStake)) { pblock->vtx.push_back(txCoinStake); pblock->vtx[0].vout[0].SetEmpty(); @@ -3741,7 +3780,8 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) nLastBlockTx = nBlockTx; nLastBlockSize = nBlockSize; - printf("CreateNewBlock(): total size %lu\n", nBlockSize); + if (fDebug && GetBoolArg("-printpriority")) + printf("CreateNewBlock(): total size %lu\n", nBlockSize); } if (pblock->IsProofOfWork()) @@ -3750,8 +3790,7 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) // Fill in header pblock->hashPrevBlock = pindexPrev->GetBlockHash(); pblock->hashMerkleRoot = pblock->BuildMerkleTree(); - pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); - pblock->nTime = max(pblock->GetBlockTime(), pblock->GetMaxTransactionTime()); + pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, pblock->GetMaxTransactionTime()); pblock->nTime = max(pblock->GetBlockTime(), pindexPrev->GetBlockTime() - nMaxClockDrift); pblock->UpdateTime(pindexPrev); pblock->nNonce = 0; @@ -3876,7 +3915,7 @@ void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) CReserveKey reservekey(pwallet); unsigned int nExtraNonce = 0; - while (fGenerateBitcoins) + while (fGenerateBitcoins || fProofOfStake) { if (fShutdown) return; @@ -3885,10 +3924,16 @@ void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) Sleep(1000); if (fShutdown) return; - if (!fGenerateBitcoins) + if ((!fGenerateBitcoins) && !fProofOfStake) return; } + while (pwallet->IsLocked()) + { + strMintWarning = strMintMessage; + Sleep(1000); + } + strMintWarning = ""; // // Create new block @@ -3903,17 +3948,21 @@ void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce); // ppcoin: if proof-of-stake block found then process block - if (pblock->IsProofOfStake()) + if (fProofOfStake) { - if (!pblock->SignBlock(*pwalletMain)) + if (pblock->IsProofOfStake()) { - error("CPUMiner: Unable to sign new proof-of-stake block"); - return; + if (!pblock->SignBlock(*pwalletMain)) + { + strMintWarning = strMintMessage; + continue; + } + strMintWarning = ""; + printf("CPUMiner : proof-of-stake block found %s\n", pblock->GetHash().ToString().c_str()); + SetThreadPriority(THREAD_PRIORITY_NORMAL); + CheckWork(pblock.get(), *pwalletMain, reservekey); + SetThreadPriority(THREAD_PRIORITY_LOWEST); } - printf("CPUMiner : proof-of-stake block found %s\n", pblock->GetHash().ToString().c_str()); - SetThreadPriority(THREAD_PRIORITY_NORMAL); - CheckWork(pblock.get(), *pwalletMain, reservekey); - SetThreadPriority(THREAD_PRIORITY_LOWEST); Sleep(500); continue; } @@ -3963,10 +4012,10 @@ void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) assert(hash == pblock->GetHash()); if (!pblock->SignBlock(*pwalletMain)) { - error("BitcoinMiner: Unable to sign new proof-of-work block"); - return; + strMintWarning = strMintMessage; + break; } - + strMintWarning = ""; SetThreadPriority(THREAD_PRIORITY_NORMAL); CheckWork(pblock.get(), *pwalletMain, reservekey); SetThreadPriority(THREAD_PRIORITY_LOWEST); @@ -4021,8 +4070,7 @@ void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) break; // Update nTime every few seconds - pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); - pblock->nTime = max(pblock->GetBlockTime(), pblock->GetMaxTransactionTime()); + pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, pblock->GetMaxTransactionTime()); pblock->nTime = max(pblock->GetBlockTime(), pindexPrev->GetBlockTime() - nMaxClockDrift); pblock->UpdateTime(pindexPrev); nBlockTime = ByteReverse(pblock->nTime);