From 3dc4db1ccda03ef5635dfedffe3a27d567d3c062 Mon Sep 17 00:00:00 2001 From: CryptoManiac Date: Thu, 19 Jun 2014 02:50:31 +0400 Subject: [PATCH] Update transaction verification protocol * CheckBlock() refactoring: move headers checking code to new CheckBlockHeader() and coinbase value checking code to ConnectBlock(); * Fix minimum stake fee at CENT. --- src/main.cpp | 113 ++++++++++++++++++++++++++++++----------------------- src/main.h | 3 +- src/miner.cpp | 2 +- src/timestamps.h | 5 +- src/util.h | 1 - 5 files changed, 70 insertions(+), 54 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 5f6577a..26aabe8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1112,7 +1112,7 @@ CBigNum inline GetProofOfStakeLimit(int nHeight, unsigned int nTime) } // miner's coin base reward based on nBits -int64 GetProofOfWorkReward(unsigned int nBits) +int64 GetProofOfWorkReward(unsigned int nBits, int64 nFees) { CBigNum bnSubsidyLimit = MAX_MINT_PROOF_OF_WORK; @@ -1145,9 +1145,9 @@ int64 GetProofOfWorkReward(unsigned int nBits) nSubsidy = (nSubsidy / CENT) * CENT; if (fDebug && GetBoolArg("-printcreation")) - printf("GetProofOfWorkReward() : create=%s nBits=0x%08x nSubsidy=%"PRI64d"\n", FormatMoney(nSubsidy).c_str(), nBits, nSubsidy); + printf("GetProofOfWorkReward() : create=%s nBits=0x%08x nSubsidy=%"PRI64d" nFees=%"PRI64d"\n", FormatMoney(nSubsidy).c_str(), nBits, nSubsidy, nFees); - return min(nSubsidy, MAX_MINT_PROOF_OF_WORK); + return min(nSubsidy + nFees, MAX_MINT_PROOF_OF_WORK); } // miner's coin stake reward based on nBits and coin age spent (coin-days) @@ -1571,7 +1571,7 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmod if (!GetCoinAge(nCoinAge)) return error("CheckInputs() : %s unable to get coin age for coinstake", GetHash().ToString().substr(0,10).c_str()); - unsigned int nTxSize = (nTime > STAKEFEE_SWITCH_TIME || fTestNet) ? GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION) : 0; + unsigned int nTxSize = (nTime > VALIDATION_SWITCH_TIME || fTestNet) ? GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION) : 0; int64 nReward = GetValueOut() - nValueIn; int64 nCalculatedReward = GetProofOfStakeReward(nCoinAge, pblock->nBits, nTime) - GetMinFee(1, false, GMF_BLOCK, nTxSize, CENT) + CENT; @@ -1839,6 +1839,17 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust blockundo.vtxundo.push_back(txundo); } + if (IsProofOfWork()) + { + int64 nBlockReward = GetProofOfWorkReward(nBits, VALIDATION_SWITCH_TIME < nTime ? nFees : 0); + + // Check coinbase reward + if (vtx[0].GetValueOut() > nBlockReward) + return error("ConnectBlock() : coinbase reward exceeded (actual=%"PRI64d" vs calculated=%"PRI64d")", + vtx[0].GetValueOut(), + nBlockReward); + } + pindex->nMint = nValueOut - nValueIn + nFees; pindex->nMoneySupply = (pindex->pprev? pindex->pprev->nMoneySupply : 0) + nValueOut - nValueIn; @@ -2261,6 +2272,37 @@ bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize) return true; } +bool CBlock::CheckBlockHeader(bool fCheckPoW, bool fCheckSig) const +{ + // Check timestamp + if (GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) + return error("CheckBlockHeader() : block timestamp too far in the future"); + + if (IsProofOfWork()) + { + // Check proof of work matches claimed amount + if (fCheckPoW && !CheckProofOfWork(GetHash(), nBits)) + return DoS(50, error("CheckBlockHeader() : proof of work failed")); + + // Should we check proof-of-work block signature or not? + // + // * Always skip on TestNet + // * Perform checking for the first 9689 blocks + // * Perform checking since last checkpoint until 20 Sep 2013 (will be removed after) + + if(!fTestNet && fCheckSig) + { + bool checkEntropySig = (GetBlockTime() < ENTROPY_SWITCH_TIME); + + // check legacy proof-of-work block signature + if (checkEntropySig && !CheckLegacySignature()) + return DoS(100, error("CheckBlockHeader() : bad proof-of-work block signature")); + } + } + + return true; +} + bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckSig) const { // These are checks that are independent of context @@ -2270,20 +2312,12 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckSig) c if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) return DoS(100, error("CheckBlock() : size limits failed")); - // Check proof of work matches claimed amount - if (fCheckPOW && IsProofOfWork() && !CheckProofOfWork(GetHash(), nBits)) - return DoS(50, error("CheckBlock() : proof of work failed")); - - // Check timestamp - if (GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) - return error("CheckBlock() : block timestamp too far in the future"); + if (!CheckBlockHeader(fCheckPOW, fCheckSig)) + return false; - // First transaction must be coinbase, the rest must not be - if (vtx.empty() || !vtx[0].IsCoinBase()) + // First transaction must be coinbase + if (!vtx[0].IsCoinBase()) return DoS(100, error("CheckBlock() : first tx is not coinbase")); - for (unsigned int i = 1; i < vtx.size(); i++) - if (vtx[i].IsCoinBase()) - return DoS(100, error("CheckBlock() : more than one coinbase")); // Check coinbase timestamp if (GetBlockTime() > FutureDrift((int64)vtx[0].nTime)) @@ -2295,54 +2329,35 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckSig) c if (vtx[0].vout.size() != 1 || !vtx[0].vout[0].IsEmpty()) return DoS(100, error("CheckBlock() : coinbase output not empty for proof-of-stake block")); - // Second transaction must be coinstake, the rest must not be - if (vtx.empty() || !vtx[1].IsCoinStake()) + // Second transaction must be coinstake + if (!vtx[1].IsCoinStake()) return DoS(100, error("CheckBlock() : second tx is not coinstake")); - for (unsigned int i = 2; i < vtx.size(); i++) - if (vtx[i].IsCoinStake()) - return DoS(100, error("CheckBlock() : more than one coinstake")); // Check coinstake timestamp if (!CheckCoinStakeTimestamp(GetBlockTime(), (int64)vtx[1].nTime)) return DoS(50, error("CheckBlock() : coinstake timestamp violation nTimeBlock=%"PRI64d" nTimeTx=%u", GetBlockTime(), vtx[1].nTime)); } - else - { - int64 nReward = GetProofOfWorkReward(nBits); - // Check coinbase reward - if (vtx[0].GetValueOut() > nReward) - return DoS(50, error("CheckBlock() : coinbase reward exceeded (actual=%"PRI64d" vs calculated=%"PRI64d")", - vtx[0].GetValueOut(), - nReward)); - - // Should we check proof-of-work block signature or not? - // - // * Always skip on TestNet - // * Perform checking for the first 9689 blocks - // * Perform checking since last checkpoint until 20 Sep 2013 (will be removed after) - - if(!fTestNet && fCheckSig) - { - bool checkEntropySig = (GetBlockTime() < ENTROPY_SWITCH_TIME); - - // check legacy proof-of-work block signature - if (checkEntropySig && !CheckLegacySignature()) - return DoS(100, error("CheckBlock() : bad proof-of-work block signature")); - } - } - // Check transactions - BOOST_FOREACH(const CTransaction& tx, vtx) + for (unsigned int i = 0; i < vtx.size(); i++) { - if (!tx.CheckTransaction()) - return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed")); + const CTransaction& tx = vtx[i]; // check transaction timestamp if (GetBlockTime() < (int64)tx.nTime) return DoS(50, error("CheckBlock() : block timestamp earlier than transaction timestamp")); + + if (i != 0 && tx.IsCoinBase()) + return DoS(100, error("CheckBlock() : coinbase in wrong position")); + + if (i != 1 && tx.IsCoinStake()) + return DoS(100, error("CheckBlock() : coinstake in wrong position")); + + if (!tx.CheckTransaction()) + return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed")); } + // Check for duplicate txids. This is caught by ConnectInputs(), // but catching it earlier avoids a potential DoS attack: BuildMerkleTree(); diff --git a/src/main.h b/src/main.h index 9f43f51..2b9f15d 100644 --- a/src/main.h +++ b/src/main.h @@ -124,7 +124,7 @@ bool LoadExternalBlockFile(FILE* fileIn); bool CheckProofOfWork(uint256 hash, unsigned int nBits); unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake); -int64 GetProofOfWorkReward(unsigned int nBits); +int64 GetProofOfWorkReward(unsigned int nBits, int64 nFees=0); int64 GetProofOfStakeReward(int64 nCoinAge, unsigned int nBits, unsigned int nTime, bool bCoinYearOnly=false); unsigned int ComputeMinWork(unsigned int nBase, int64 nTime); unsigned int ComputeMinStake(unsigned int nBase, int64 nTime, unsigned int nBlockTime); @@ -1459,6 +1459,7 @@ public: bool AddToBlockIndex(const CDiskBlockPos &pos); // Context-independent validity checks + bool CheckBlockHeader(bool fCheckPoW=true, bool fCheckSig=false) const; bool CheckBlock(bool fCheckPOW=true, bool fCheckMerkleRoot=true, bool fCheckSig=false) const; // Store block on disk diff --git a/src/miner.cpp b/src/miner.cpp index 6a9adc6..7ee8525 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -356,7 +356,7 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) printf("CreateNewBlock(): total size %"PRI64u"\n", nBlockSize); if (!fProofOfStake) - pblock->vtx[0].vout[0].nValue = GetProofOfWorkReward(pblock->nBits); + pblock->vtx[0].vout[0].nValue = GetProofOfWorkReward(pblock->nBits, VALIDATION_SWITCH_TIME < pblock->nTime ? nFees : 0); // Fill in header pblock->hashPrevBlock = pindexPrev->GetBlockHash(); diff --git a/src/timestamps.h b/src/timestamps.h index 18dac1c..de8a6a6 100644 --- a/src/timestamps.h +++ b/src/timestamps.h @@ -6,7 +6,8 @@ static const unsigned int STAKE_SWITCH_TIME = 1371686400; // Thu, 20 Jun 2013 00 static const unsigned int TARGETS_SWITCH_TIME = 1374278400; // Sat, 20 Jul 2013 00:00:00 GMT static const unsigned int CHAINCHECKS_SWITCH_TIME = 1379635200; // Fri, 20 Sep 2013 00:00:00 GMT static const unsigned int STAKECURVE_SWITCH_TIME = 1382227200; // Sun, 20 Oct 2013 00:00:00 GMT -static const unsigned int FEE_SWITCH_TIME = 1404172800; // Tue, 01 Jul 2014 00:00:00 GMT -static const unsigned int STAKEFEE_SWITCH_TIME = 1406851200; // Fri, 01 Aug 2014 00:00:00 GMT +static const unsigned int FEE_SWITCH_TIME = 1405814400; // Sun, 20 Jul 2014 00:00:00 GMT + +static const unsigned int VALIDATION_SWITCH_TIME = 1408492800; // Wed, 20 Aug 2014 00:00:00 GMT #endif diff --git a/src/util.h b/src/util.h index 5117164..3e5beb9 100644 --- a/src/util.h +++ b/src/util.h @@ -63,7 +63,6 @@ static const int64 CENT = 10000; void LogStackTrace(); #endif - /* Format characters for (s)size_t and ptrdiff_t */ #if defined(_MSC_VER) || defined(__MSVCRT__) /* (s)size_t and ptrdiff_t have the same size specifier in MSVC: -- 1.7.1