X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fmain.cpp;h=08ceb208ed2eb2246ae4f6eba8cd5aa035f8b1c3;hb=c646d69d20d2cace5afaa8cd9790edb94f555bd1;hp=9d9020c721458d49b901fa25f59fdb73f5132fbb;hpb=5949ad350531b1e57df9d1fdc5966132e6916080;p=novacoin.git diff --git a/src/main.cpp b/src/main.cpp index 9d9020c..08ceb20 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -116,8 +116,18 @@ void static EraseFromWallets(uint256 hash) } // make sure all wallets know about the given transaction, in the given block -void static SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false) +void static SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false, bool fConnect = true) { + if (!fConnect) + { + // ppcoin: wallets need to refund inputs when disconnecting coinstake + if (tx.IsCoinStake()) + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + if (pwallet->IsFromMe(tx)) + pwallet->DisableTransaction(tx); + return; + } + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate); } @@ -721,13 +731,27 @@ unsigned int ComputeMinWork(unsigned int nBase, int64 nTime) return bnResult.GetCompact(); } -unsigned int static GetNextWorkRequired(const CBlockIndex* pindexLast) +// ppcoin: find last block index up to pindex +const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake) +{ + while (pindex && (pindex->IsProofOfStake() != fProofOfStake)) + pindex = pindex->pprev; + return pindex; +} + +unsigned int static GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake) { // Genesis block and first block if (pindexLast == NULL || pindexLast->pprev == NULL) return bnProofOfWorkLimit.GetCompact(); - int64 nActualSpacing = pindexLast->GetBlockTime() - pindexLast->pprev->GetBlockTime(); + const CBlockIndex* pindexPrev = GetLastBlockIndex(pindexLast, fProofOfStake); + if (pindexPrev == NULL) + return bnProofOfWorkLimit.GetCompact(); + const CBlockIndex* pindexPrevPrev = GetLastBlockIndex(pindexPrev->pprev, fProofOfStake); + if (pindexPrevPrev == NULL) + return bnProofOfWorkLimit.GetCompact(); + int64 nActualSpacing = pindexPrev->GetBlockTime() - pindexPrevPrev->GetBlockTime(); // ppcoin: target change every block // ppcoin: retarget with exponential moving toward target spacing @@ -1038,6 +1062,10 @@ bool CBlock::DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex) return error("DisconnectBlock() : WriteBlockIndex failed"); } + // ppcoin: clean up wallet after disconnecting coinstake + BOOST_FOREACH(CTransaction& tx, vtx) + SyncWithWallets(tx, this, false, false); + return true; } @@ -1047,8 +1075,12 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) if (!CheckBlock()) return false; + // ppcoin: coin stake tx must meet target protocol + if (IsProofOfStake() && !vtx[1].CheckProofOfStake(txdb, nBits)) + return error("ConnectBlock() : Block %s unable to meet hash target for coinstake", GetHash().ToString().c_str()); + //// issue here: it doesn't know the version - unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK) - 1 + GetSizeOfCompactSize(vtx.size()); + unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK) - (2 * GetSizeOfCompactSize(0)) + GetSizeOfCompactSize(vtx.size()); map mapQueuedChanges; int64 nFees = 0; @@ -1250,6 +1282,57 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) } +// ppcoin: coinstake must meet hash target according to the protocol: +// at least one input must meet the formula +// hash(nBits + txPrev.block.nTime + txPrev.nTime + nTime) < bnTarget * nCoinDay +// this ensures that the chance of getting a coinstake is proportional to the +// amount of coin age one owns. +// The reason this hash is chosen is the following: +// nBits: encodes all past block timestamps, prevents computing hash in advance +// txPrev.block.nTime: prevent nodes from guessing a good timestamp to generate +// transaction for future advantage +// txPrev.nTime: prevent nodes from meeting target simultaneously +// block/tx hash should not be used here as they can be generated in vast quatities +// so as to generate blocks faster, degrading the system back into a proof-of-work +// situation. +// +bool CTransaction::CheckProofOfStake(CTxDB& txdb, unsigned int nBits) const +{ + CBigNum bnTargetPerCoinDay; + bnTargetPerCoinDay.SetCompact(nBits); + + if (!IsCoinStake()) + return true; + + BOOST_FOREACH(const CTxIn& txin, vin) + { + // First try finding the previous transaction in database + CTransaction txPrev; + CTxIndex txindex; + if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex)) + continue; // previous transaction not in main chain + if (nTime < txPrev.nTime) + return false; // Transaction timestamp violation + + // Read block header + CBlock block; + if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) + return false; // unable to read block of previous transaction + if (block.GetBlockTime() + AUTO_CHECKPOINT_TRUST_SPAN > nTime) + continue; // only count coins from at least one week ago + + int64 nValueIn = txPrev.vout[txin.prevout.n].nValue; + CBigNum bnCoinDay = CBigNum(nValueIn) * (nTime-txPrev.nTime) / COIN / (24 * 60 * 60); + // Calculate hash + CDataStream ss(SER_GETHASH, VERSION); + ss << nBits << block.nTime << txPrev.nTime << nTime; + if (CBigNum(Hash(ss.begin(), ss.end())) <= bnCoinDay * bnTargetPerCoinDay) + return true; + } + + return false; +} + // ppcoin: total coin age spent in transaction, in the unit of coin-days. // Only those coins last spent at least a week ago count. As those // transactions not in main chain are not currently indexed so we @@ -1388,7 +1471,7 @@ bool CBlock::CheckBlock() const return DoS(100, error("CheckBlock() : size limits failed")); // Check proof of work matches claimed amount - if (!CheckProofOfWork(GetHash(), nBits)) + if (IsProofOfWork() && !CheckProofOfWork(GetHash(), nBits)) return DoS(50, error("CheckBlock() : proof of work failed")); // Check timestamp @@ -1429,6 +1512,17 @@ bool CBlock::CheckBlock() const if (hashMerkleRoot != BuildMerkleTree()) return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch")); + // Coin base vout[0] scriptPubKey must be the same as coin stake vout[1] + // scriptPubKey + if (vtx.size() > 1 && vtx[1].IsCoinStake() && + vtx[0].vout[0].scriptPubKey != vtx[1].vout[1].scriptPubKey) + return DoS(100, error("CheckBlock() : block key mismatch")); + + + // Check block signature + if (!CheckBlockSignature()) + return DoS(100, error("CheckBlock() : bad block signature")); + return true; } @@ -1446,9 +1540,18 @@ bool CBlock::AcceptBlock() CBlockIndex* pindexPrev = (*mi).second; int nHeight = pindexPrev->nHeight+1; - // Check proof of work - if (nBits != GetNextWorkRequired(pindexPrev)) - return DoS(100, error("AcceptBlock() : incorrect proof of work")); + // ppcoin: check for coinstake duplicate + if (IsProofOfStake()) + { // check if coinstake is already connected; that would imply the owner + // of the coinstake sent multiple blocks with the same coinstake + CTxIndex txindex; + if (CTxDB("r").ReadTxIndex(vtx[1].GetHash(), txindex)) + return error("AcceptBlock() : block %s has duplicate coinstake %s", hash.ToString().c_str(), vtx[1].GetHash().ToString().c_str()); + } + + // Check proof-of-work or proof-of-stake + if (nBits != GetNextTargetRequired(pindexPrev, IsProofOfStake())) + return DoS(100, error("AcceptBlock() : incorrect proof-of-work/proof-of-stake")); // Check timestamp against prev if (GetBlockTime() <= pindexPrev->GetMedianTimePast() || GetBlockTime() + nMaxClockDrift < pindexPrev->GetBlockTime()) @@ -1514,7 +1617,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) bnNewBlock.SetCompact(pblock->nBits); CBigNum bnRequired; bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime)); - if (bnNewBlock > bnRequired) + if (pblock->IsProofOfWork() && bnNewBlock > bnRequired) { pfrom->Misbehaving(100); return error("ProcessBlock() : block with too little proof-of-work"); @@ -1696,6 +1799,7 @@ bool LoadBlockIndex(bool fAllowNew) assert(block.hashMerkleRoot == uint256("0xc7311b56de266580cca65be108ae53d7100b5c3b17da8b1106044103abd7a521")); block.print(); assert(block.GetHash() == hashGenesisBlock); + assert(block.CheckBlock()); // Start new block file unsigned int nFile; @@ -1756,11 +1860,12 @@ void PrintBlockTree() // print item CBlock block; block.ReadFromDisk(pindex); - printf("%d (%u,%u) %s %s tx %d", + printf("%d (%u,%u) %s %08lx %s tx %d", pindex->nHeight, pindex->nFile, pindex->nBlockPos, block.GetHash().ToString().substr(0,20).c_str(), + block.nBits, DateTimeStrFormat("%x %H:%M:%S", block.GetBlockTime()).c_str(), block.vtx.size()); @@ -1976,6 +2081,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) return true; } + // ppcoin: record my external IP reported by peer + if (addrFrom.IsRoutable() && addrMe.IsRoutable()) + addrSeenByPeer = addrMe; + // Be shy and don't send version until we hear if (pfrom->fInbound) pfrom->PushVersion(); @@ -2865,7 +2974,6 @@ public: CBlock* CreateNewBlock(CWallet* pwallet) { - CBlockIndex* pindexPrev = pindexBest; CReserveKey reservekey(pwallet); // Create new block @@ -2884,9 +2992,27 @@ CBlock* CreateNewBlock(CWallet* pwallet) pblock->vtx.push_back(txNew); // ppcoin: if coinstake available add coinstake tx - CTransaction txCoinStake; - if (pwallet->CreateCoinStake(txNew.vout[0].scriptPubKey, txCoinStake)) - pblock->vtx.push_back(txCoinStake); + static unsigned int nLastCoinStakeCheckTime = GetAdjustedTime() - nMaxClockDrift; // only initialized at startup + CBlockIndex* pindexPrev = pindexBest; + while (nLastCoinStakeCheckTime < GetAdjustedTime()) + { + pindexPrev = pindexBest; // get best block again to avoid getting stale + pblock->nBits = GetNextTargetRequired(pindexPrev, true); + static CCriticalSection cs; + CTransaction txCoinStake; + CRITICAL_BLOCK(cs) + { + nLastCoinStakeCheckTime++; + txCoinStake.nTime = nLastCoinStakeCheckTime; + } + if (pwallet->CreateCoinStake(txNew.vout[0].scriptPubKey, pblock->nBits, txCoinStake)) + { + pblock->vtx.push_back(txCoinStake); + break; + } + } + + pblock->nBits = GetNextTargetRequired(pindexPrev, pblock->IsProofOfStake()); // Collect memory pool transactions into the block int64 nFees = 0; @@ -3007,7 +3133,6 @@ CBlock* CreateNewBlock(CWallet* pwallet) } } } - pblock->nBits = GetNextWorkRequired(pindexPrev); pblock->vtx[0].vout[0].nValue = GetProofOfWorkReward(pblock->nBits); // Fill in header @@ -3088,12 +3213,12 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) uint256 hash = pblock->GetHash(); uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); - if (hash > hashTarget) - return false; + if (hash > hashTarget && pblock->IsProofOfWork()) + return error("BitcoinMiner : proof-of-work not meeting target"); //// debug print printf("BitcoinMiner:\n"); - printf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str()); + printf("new block found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str()); pblock->print(); printf("%s ", DateTimeStrFormat("%x %H:%M", GetTime()).c_str()); printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str()); @@ -3155,8 +3280,21 @@ void static BitcoinMiner(CWallet *pwallet) auto_ptr pblock(CreateNewBlock(pwallet)); if (!pblock.get()) return; + IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce); + // ppcoin: if proof-of-stake block found then process block + if (pblock->IsProofOfStake()) + { + // should be able to sign block - assert here for now + assert(pblock->SignBlock(*pwalletMain)); + printf("BitcoinMiner : 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); + continue; + } + printf("Running BitcoinMiner with %d transactions in block\n", pblock->vtx.size()); @@ -3200,6 +3338,8 @@ void static BitcoinMiner(CWallet *pwallet) // Found a solution pblock->nNonce = ByteReverse(nNonceFound); assert(hash == pblock->GetHash()); + // should be able to sign block - assert here for now + assert(pblock->SignBlock(*pwalletMain)); SetThreadPriority(THREAD_PRIORITY_NORMAL); CheckWork(pblock.get(), *pwalletMain, reservekey);