Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/chain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ const CBlockIndex* CChain::FindFork(const CBlockIndex* pindex) const
return pindex;
}

CBlockIndex* CChain::FindEarliestAtLeast(int64_t nTime) const
{
std::vector<CBlockIndex*>::const_iterator lower = std::lower_bound(vChain.begin(), vChain.end(), nTime,
[](CBlockIndex* pBlock, const int64_t& time) -> bool { return pBlock->GetBlockTimeMax() < time; });
return (lower == vChain.end() ? nullptr : *lower);
}

/** Turn the lowest '1' bit in the binary representation of a number into a '0'. */
int static inline InvertLowestOne(int n) { return n & (n - 1); }

Expand Down
8 changes: 8 additions & 0 deletions src/chain.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,9 @@ class CBlockIndex
//! (memory only) Sequential id assigned to distinguish order in which blocks are received.
uint32_t nSequenceId{0};

//! (memory only) Maximum nTime in the chain upto and including this block.
unsigned int nTimeMax{0};

CBlockIndex() {}
CBlockIndex(const CBlock& block);

Expand All @@ -249,6 +252,8 @@ class CBlockIndex
CBlockHeader GetBlockHeader() const;
uint256 GetBlockHash() const { return *phashBlock; }
int64_t GetBlockTime() const { return (int64_t)nTime; }
int64_t GetBlockTimeMax() const { return (int64_t)nTimeMax; }

int64_t GetMedianTimePast() const;

int64_t MaxFutureBlockTime() const;
Expand Down Expand Up @@ -595,6 +600,9 @@ class CChain

/** Find the last common block between this chain and a block index entry. */
const CBlockIndex* FindFork(const CBlockIndex* pindex) const;

/** Find the earliest block with timestamp equal or greater than the given. */
CBlockIndex* FindEarliestAtLeast(int64_t nTime) const;
};

#endif // BITCOIN_CHAIN_H
2 changes: 2 additions & 0 deletions src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ static const CRPCConvertParam vRPCConvertParams[] = {
{ "importaddress", 2 },
{ "importaddress", 3 },
{ "importpubkey", 2 },
{ "importmulti", 0 },
{ "importmulti", 1 },
{ "exportsaplingkey", 1 },
{ "importsaplingkey", 2 },
{ "importsaplingviewingkey", 2 },
Expand Down
53 changes: 48 additions & 5 deletions src/test/skiplist_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ BOOST_AUTO_TEST_CASE(skiplist_test)

for (int i=0; i<SKIPLIST_LENGTH; i++) {
vIndex[i].nHeight = i;
vIndex[i].pprev = (i == 0) ? NULL : &vIndex[i - 1];
vIndex[i].pprev = (i == 0) ? nullptr : &vIndex[i - 1];
vIndex[i].BuildSkip();
}

Expand All @@ -31,7 +31,7 @@ BOOST_AUTO_TEST_CASE(skiplist_test)
BOOST_CHECK(vIndex[i].pskip == &vIndex[vIndex[i].pskip->nHeight]);
BOOST_CHECK(vIndex[i].pskip->nHeight < i);
} else {
BOOST_CHECK(vIndex[i].pskip == NULL);
BOOST_CHECK(vIndex[i].pskip == nullptr);
}
}

Expand All @@ -53,11 +53,11 @@ BOOST_AUTO_TEST_CASE(getlocator_test)
for (unsigned int i=0; i<vBlocksMain.size(); i++) {
vHashMain[i] = i; // Set the hash equal to the height, so we can quickly check the distances.
vBlocksMain[i].nHeight = i;
vBlocksMain[i].pprev = i ? &vBlocksMain[i - 1] : NULL;
vBlocksMain[i].pprev = i ? &vBlocksMain[i - 1] : nullptr;
vBlocksMain[i].phashBlock = &vHashMain[i];
vBlocksMain[i].BuildSkip();
BOOST_CHECK_EQUAL((int)vBlocksMain[i].GetBlockHash().GetLow64(), vBlocksMain[i].nHeight);
BOOST_CHECK(vBlocksMain[i].pprev == NULL || vBlocksMain[i].nHeight == vBlocksMain[i].pprev->nHeight + 1);
BOOST_CHECK(vBlocksMain[i].pprev == nullptr || vBlocksMain[i].nHeight == vBlocksMain[i].pprev->nHeight + 1);
}

// Build a branch that splits off at block 49999, 50000 blocks long.
Expand All @@ -70,7 +70,7 @@ BOOST_AUTO_TEST_CASE(getlocator_test)
vBlocksSide[i].phashBlock = &vHashSide[i];
vBlocksSide[i].BuildSkip();
BOOST_CHECK_EQUAL((int)vBlocksSide[i].GetBlockHash().GetLow64(), vBlocksSide[i].nHeight);
BOOST_CHECK(vBlocksSide[i].pprev == NULL || vBlocksSide[i].nHeight == vBlocksSide[i].pprev->nHeight + 1);
BOOST_CHECK(vBlocksSide[i].pprev == nullptr || vBlocksSide[i].nHeight == vBlocksSide[i].pprev->nHeight + 1);
}

// Build a CChain for the main branch.
Expand Down Expand Up @@ -101,4 +101,47 @@ BOOST_AUTO_TEST_CASE(getlocator_test)
}
}

BOOST_AUTO_TEST_CASE(findearliestatleast_test)
{
std::vector<uint256> vHashMain(100000);
std::vector<CBlockIndex> vBlocksMain(100000);
for (unsigned int i=0; i<vBlocksMain.size(); i++) {
vHashMain[i] = ArithToUint256(i); // Set the hash equal to the height
vBlocksMain[i].nHeight = i;
vBlocksMain[i].pprev = i ? &vBlocksMain[i - 1] : nullptr;
vBlocksMain[i].phashBlock = &vHashMain[i];
vBlocksMain[i].BuildSkip();
if (i < 10) {
vBlocksMain[i].nTime = i;
vBlocksMain[i].nTimeMax = i;
} else {
// randomly choose something in the range [MTP, MTP*2]
int64_t medianTimePast = vBlocksMain[i].GetMedianTimePast();
int r = InsecureRandRange(medianTimePast);
vBlocksMain[i].nTime = r + medianTimePast;
vBlocksMain[i].nTimeMax = std::max(vBlocksMain[i].nTime, vBlocksMain[i-1].nTimeMax);
}
}
// Check that we set nTimeMax up correctly.
unsigned int curTimeMax = 0;
for (unsigned int i=0; i<vBlocksMain.size(); ++i) {
curTimeMax = std::max(curTimeMax, vBlocksMain[i].nTime);
BOOST_CHECK(curTimeMax == vBlocksMain[i].nTimeMax);
}

// Build a CChain for the main branch.
CChain chain;
chain.SetTip(&vBlocksMain.back());

// Verify that FindEarliestAtLeast is correct.
for (unsigned int i=0; i<10000; ++i) {
// Pick a random element in vBlocksMain.
int r = InsecureRandRange(vBlocksMain.size());
int64_t test_time = vBlocksMain[r].nTime;
CBlockIndex *ret = chain.FindEarliestAtLeast(test_time);
BOOST_CHECK(ret->nTimeMax >= test_time);
BOOST_CHECK((ret->pprev==nullptr) || ret->pprev->nTimeMax < test_time);
BOOST_CHECK(vBlocksMain[r].GetAncestor(ret->nHeight) == ret);
}
}
BOOST_AUTO_TEST_SUITE_END()
2 changes: 2 additions & 0 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2426,6 +2426,7 @@ CBlockIndex* AddToBlockIndex(const CBlock& block)
pindexNew->SetNewStakeModifier(block.vtx[1]->vin[0].prevout.hash);
}
}
pindexNew->nTimeMax = (pindexNew->pprev ? std::max(pindexNew->pprev->nTimeMax, pindexNew->nTime) : pindexNew->nTime);
pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + GetBlockProof(*pindexNew);
pindexNew->RaiseValidity(BLOCK_VALID_TREE);
if (pindexBestHeader == NULL || pindexBestHeader->nChainWork < pindexNew->nChainWork)
Expand Down Expand Up @@ -3436,6 +3437,7 @@ bool static LoadBlockIndexDB(std::string& strError)

CBlockIndex* pindex = item.second;
pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex);
pindex->nTimeMax = (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) : pindex->nTime);
if (pindex->nStatus & BLOCK_HAVE_DATA) {
if (pindex->pprev) {
if (pindex->pprev->nChainTx) {
Expand Down
Loading