From f7303f97933be33e34d482cf8348d180c8da2a26 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 17 Mar 2015 06:35:59 -0700 Subject: [PATCH] Use equivalent PoW for non-main-chain requests --- src/main.cpp | 9 +++++---- src/pow.cpp | 17 +++++++++++++++++ src/pow.h | 3 +++ src/test/pow_tests.cpp | 24 ++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 4352c719a12..c5168ec8c4a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3459,7 +3459,6 @@ bool static AlreadyHave(const CInv& inv) return true; } - void static ProcessGetData(CNode* pfrom) { std::deque::iterator it = pfrom->vRecvGetData.begin(); @@ -3487,11 +3486,13 @@ void static ProcessGetData(CNode* pfrom) if (chainActive.Contains(mi->second)) { send = true; } else { + static const int nOneMonth = 30 * 24 * 60 * 60; // To prevent fingerprinting attacks, only send blocks outside of the active - // chain if they are valid, and no more than a month older than the best header - // chain we know about. + // chain if they are valid, and no more than a month older (both in time, and in + // best equivalent proof of work) than the best header chain we know about. send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != NULL) && - (mi->second->GetBlockTime() > pindexBestHeader->GetBlockTime() - 30 * 24 * 60 * 60); + (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() < nOneMonth) && + (GetBlockProofEquivalentTime(*pindexBestHeader, *mi->second, *pindexBestHeader, Params().GetConsensus()) < nOneMonth); if (!send) { LogPrintf("%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId()); } diff --git a/src/pow.cpp b/src/pow.cpp index fc6ed4f3d19..bb53ad204bc 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -114,3 +114,20 @@ arith_uint256 GetBlockProof(const CBlockIndex& block) // or ~bnTarget / (nTarget+1) + 1. return (~bnTarget / (bnTarget + 1)) + 1; } + +int64_t GetBlockProofEquivalentTime(const CBlockIndex& to, const CBlockIndex& from, const CBlockIndex& tip, const Consensus::Params& params) +{ + arith_uint256 r; + int sign = 1; + if (to.nChainWork > from.nChainWork) { + r = to.nChainWork - from.nChainWork; + } else { + r = from.nChainWork - to.nChainWork; + sign = -1; + } + r = r * arith_uint256(params.nPowTargetSpacing) / GetBlockProof(tip); + if (r.bits() > 63) { + return sign * std::numeric_limits::max(); + } + return sign * r.GetLow64(); +} diff --git a/src/pow.h b/src/pow.h index a5d32db178e..e864a474ccd 100644 --- a/src/pow.h +++ b/src/pow.h @@ -22,4 +22,7 @@ unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nF bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params&); arith_uint256 GetBlockProof(const CBlockIndex& block); +/** Return the time it would take to redo the work difference between from and to, assuming the current hashrate corresponds to the difficulty at tip, in seconds. */ +int64_t GetBlockProofEquivalentTime(const CBlockIndex& to, const CBlockIndex& from, const CBlockIndex& tip, const Consensus::Params&); + #endif // BITCOIN_POW_H diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp index 4ce1591c353..a436749287d 100644 --- a/src/test/pow_tests.cpp +++ b/src/test/pow_tests.cpp @@ -69,4 +69,28 @@ BOOST_AUTO_TEST_CASE(get_next_work_upper_limit_actual) BOOST_CHECK_EQUAL(CalculateNextWorkRequired(&pindexLast, nLastRetargetTime, params), 0x1d00e1fd); } +BOOST_AUTO_TEST_CASE(GetBlockProofEquivalentTime_test) +{ + SelectParams(CBaseChainParams::MAIN); + const Consensus::Params& params = Params().GetConsensus(); + + std::vector blocks(10000); + for (int i = 0; i < 10000; i++) { + blocks[i].pprev = i ? &blocks[i - 1] : NULL; + blocks[i].nHeight = i; + blocks[i].nTime = 1269211443 + i * params.nPowTargetSpacing; + blocks[i].nBits = 0x207fffff; /* target 0x7fffff000... */ + blocks[i].nChainWork = i ? blocks[i - 1].nChainWork + GetBlockProof(blocks[i - 1]) : arith_uint256(0); + } + + for (int j = 0; j < 1000; j++) { + CBlockIndex *p1 = &blocks[GetRand(10000)]; + CBlockIndex *p2 = &blocks[GetRand(10000)]; + CBlockIndex *p3 = &blocks[GetRand(10000)]; + + int64_t tdiff = GetBlockProofEquivalentTime(*p1, *p2, *p3, params); + BOOST_CHECK_EQUAL(tdiff, p1->GetBlockTime() - p2->GetBlockTime()); + } +} + BOOST_AUTO_TEST_SUITE_END()