From ccc414643c011112647f59fb0abec0a5c3ba31d3 Mon Sep 17 00:00:00 2001
From: Chethan Krishna <chethan@bitpay.com>
Date: Thu, 21 Jul 2016 16:52:51 -0400
Subject: [PATCH] logical timestamp indexing of block hashes

---
 qa/rpc-tests/timestampindex.py | 22 ++++++++----
 src/main.cpp                   | 23 +++++++++++--
 src/main.h                     | 63 ++++++++++++++++++++++++++++++++++
 src/txdb.cpp                   | 42 ++++++++++++++++++++++-
 src/txdb.h                     |  7 ++++
 5 files changed, 147 insertions(+), 10 deletions(-)

diff --git a/qa/rpc-tests/timestampindex.py b/qa/rpc-tests/timestampindex.py
index 46ff710bdf37f..289c81b2a5212 100755
--- a/qa/rpc-tests/timestampindex.py
+++ b/qa/rpc-tests/timestampindex.py
@@ -35,15 +35,25 @@ def setup_network(self):
         self.sync_all()
 
     def run_test(self):
-        print "Mining 5 blocks..."
-        blockhashes = self.nodes[0].generate(5)
-        low = self.nodes[0].getblock(blockhashes[0])["time"]
-        high = self.nodes[0].getblock(blockhashes[4])["time"]
+        print "Mining 25 blocks..."
+        blockhashes = self.nodes[0].generate(25)
+        time.sleep(3)
+        print "Mining 25 blocks..."
+        blockhashes.extend(self.nodes[0].generate(25))
+        time.sleep(3)
+        print "Mining 25 blocks..."
+        blockhashes.extend(self.nodes[0].generate(25))
         self.sync_all()
+        low = self.nodes[1].getblock(blockhashes[0])["time"]
+        high = low + 76
+
         print "Checking timestamp index..."
         hashes = self.nodes[1].getblockhashes(high, low)
-        assert_equal(len(hashes), 5)
-        assert_equal(sorted(blockhashes), sorted(hashes))
+
+        assert_equal(len(hashes), len(blockhashes))
+
+        assert_equal(hashes, blockhashes)
+
         print "Passed\n"
 
 
diff --git a/src/main.cpp b/src/main.cpp
index 1092d35630549..d0a76bf16bde2 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -2648,9 +2648,26 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
         if (!pblocktree->UpdateSpentIndex(spentIndex))
             return AbortNode(state, "Failed to write transaction index");
 
-    if (fTimestampIndex)
-        if (!pblocktree->WriteTimestampIndex(CTimestampIndexKey(pindex->nTime, pindex->GetBlockHash())))
-            return AbortNode(state, "Failed to write timestamp index");
+	if (fTimestampIndex) {
+		unsigned int logicalTS = pindex->nTime;
+		unsigned int prevLogicalTS = 0;
+
+		// retrieve logical timestamp of the previous block
+		if (pindex->pprev)
+			if (!pblocktree->ReadBlockhashIndex(pindex->pprev->GetBlockHash(), prevLogicalTS))
+				LogPrintf("%s: Failed to read previous block's logical timestamp", __func__);
+
+		if (logicalTS <= prevLogicalTS) {
+			logicalTS = prevLogicalTS + 1;
+			LogPrintf("%s: Previous logical timestamp is newer Actual[%d] prevLogical[%d] Logical[%d]", __func__, pindex->nTime, prevLogicalTS, logicalTS);
+		}
+
+		if (!pblocktree->WriteTimestampIndex(CTimestampIndexKey(logicalTS, pindex->GetBlockHash())))
+			return AbortNode(state, "Failed to write timestamp index");
+
+		if (!pblocktree->WriteBlockhashIndex(CBlockhashIndexKey(pindex->GetBlockHash(), logicalTS)))
+			return AbortNode(state, "Failed to write blockhash index");
+	}
 
     // add this block to the view's block chain
     view.SetBestBlock(pindex->GetBlockHash());
diff --git a/src/main.h b/src/main.h
index 3f8a3fb32a219..a19270b6eb86d 100644
--- a/src/main.h
+++ b/src/main.h
@@ -355,6 +355,69 @@ struct CTimestampIndexKey {
     }
 };
 
+struct CBlockhashIndexKey {
+    uint256 blockHash;
+    unsigned int ltimestamp;
+
+    size_t GetSerializeSize(int nType, int nVersion) const {
+        return 36;
+    }
+
+    template<typename Stream>
+    void Serialize(Stream& s, int nType, int nVersion) const {
+        blockHash.Serialize(s, nType, nVersion);
+        ser_writedata32be(s, ltimestamp);
+    }
+
+    template<typename Stream>
+    void Unserialize(Stream& s, int nType, int nVersion) {
+        blockHash.Unserialize(s, nType, nVersion);
+        ltimestamp = ser_readdata32be(s);
+    }
+
+    CBlockhashIndexKey(uint256 hash, unsigned int time) {
+        blockHash = hash;
+        ltimestamp = time;
+    }
+
+    CBlockhashIndexKey() {
+        SetNull();
+    }
+
+    void SetNull() {
+        blockHash.SetNull();
+        ltimestamp = 0;
+    }
+};
+
+struct CBlockhashIndexIteratorKey {
+    uint256 blockHash;
+
+    size_t GetSerializeSize(int nType, int nVersion) const {
+        return 32;
+    }
+    template<typename Stream>
+    void Serialize(Stream& s, int nType, int nVersion) const {
+        blockHash.Serialize(s, nType, nVersion);
+    }
+    template<typename Stream>
+    void Unserialize(Stream& s, int nType, int nVersion) {
+        blockHash.Unserialize(s, nType, nVersion);
+    }
+
+    CBlockhashIndexIteratorKey(uint256 hash) {
+        blockHash = hash;
+    }
+
+    CBlockhashIndexIteratorKey() {
+        SetNull();
+    }
+
+    void SetNull() {
+        blockHash.SetNull();
+    }
+};
+
 struct CAddressUnspentKey {
     unsigned int type;
     uint160 hashBytes;
diff --git a/src/txdb.cpp b/src/txdb.cpp
index 48150198f3a34..1d9e6d71f08f9 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -1,3 +1,4 @@
+
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2009-2015 The Bitcoin Core developers
 // Distributed under the MIT software license, see the accompanying
@@ -24,6 +25,7 @@ static const char DB_TXINDEX = 't';
 static const char DB_ADDRESSINDEX = 'a';
 static const char DB_ADDRESSUNSPENTINDEX = 'u';
 static const char DB_TIMESTAMPINDEX = 's';
+static const char DB_BLOCKHASHINDEX = 'z';
 static const char DB_SPENTINDEX = 'p';
 static const char DB_BLOCK_INDEX = 'b';
 
@@ -285,7 +287,10 @@ bool CBlockTreeDB::ReadTimestampIndex(const unsigned int &high, const unsigned i
         boost::this_thread::interruption_point();
         std::pair<char, CTimestampIndexKey> key;
         if (pcursor->GetKey(key) && key.first == DB_TIMESTAMPINDEX && key.second.timestamp <= high) {
-            hashes.push_back(key.second.blockHash);
+	    if (blockOnchainActive(key.second.blockHash)) {
+		hashes.push_back(key.second.blockHash);
+	    }
+
             pcursor->Next();
         } else {
             break;
@@ -295,6 +300,31 @@ bool CBlockTreeDB::ReadTimestampIndex(const unsigned int &high, const unsigned i
     return true;
 }
 
+bool CBlockTreeDB::WriteBlockhashIndex(const CBlockhashIndexKey &blockhashIndex) {
+    CDBBatch batch(&GetObfuscateKey());
+    batch.Write(make_pair(DB_BLOCKHASHINDEX, blockhashIndex), 0);
+    return WriteBatch(batch);
+}
+
+bool CBlockTreeDB::ReadBlockhashIndex(const uint256 &hash, unsigned int &ltimestamp) {
+    boost::scoped_ptr<CDBIterator> pcursor(NewIterator());
+
+    pcursor->Seek(make_pair(DB_BLOCKHASHINDEX, CBlockhashIndexIteratorKey(hash)));
+
+    while (pcursor->Valid()) {
+        boost::this_thread::interruption_point();
+	std::pair<char, CBlockhashIndexKey> key;
+	if (pcursor->GetKey(key) && key.first == DB_BLOCKHASHINDEX && key.second.blockHash == hash) {
+	    ltimestamp = key.second.ltimestamp;
+	    pcursor->Next();
+	} else {
+	    break;
+	}
+    }
+
+    return true;
+}
+
 bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) {
     return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0');
 }
@@ -307,6 +337,16 @@ bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) {
     return true;
 }
 
+bool CBlockTreeDB::blockOnchainActive(const uint256 &hash) {
+    CBlockIndex* pblockindex = mapBlockIndex[hash];
+
+    if (!chainActive.Contains(pblockindex)) {
+	return false;
+    }
+
+    return true;
+}
+
 bool CBlockTreeDB::LoadBlockIndexGuts()
 {
     boost::scoped_ptr<CDBIterator> pcursor(NewIterator());
diff --git a/src/txdb.h b/src/txdb.h
index 14d501278f23e..c0697f662aa04 100644
--- a/src/txdb.h
+++ b/src/txdb.h
@@ -16,6 +16,7 @@
 
 class CBlockFileInfo;
 class CBlockIndex;
+class CChain;
 struct CDiskTxPos;
 struct CAddressUnspentKey;
 struct CAddressUnspentValue;
@@ -24,9 +25,12 @@ struct CAddressIndexIteratorKey;
 struct CAddressIndexIteratorHeightKey;
 struct CTimestampIndexKey;
 struct CTimestampIndexIteratorKey;
+struct CBlockhashIndexKey;
+struct CBlockhashIndexIteratorKey;
 struct CSpentIndexKey;
 struct CSpentIndexValue;
 class uint256;
+extern CChain activeChain;
 
 //! -dbcache default (MiB)
 static const int64_t nDefaultDbCache = 100;
@@ -78,9 +82,12 @@ class CBlockTreeDB : public CDBWrapper
                           int start = 0, int end = 0);
     bool WriteTimestampIndex(const CTimestampIndexKey &timestampIndex);
     bool ReadTimestampIndex(const unsigned int &high, const unsigned int &low, std::vector<uint256> &vect);
+    bool WriteBlockhashIndex(const CBlockhashIndexKey &blockhashIndex);
+    bool ReadBlockhashIndex(const uint256 &hash, unsigned int &logicalTS);
     bool WriteFlag(const std::string &name, bool fValue);
     bool ReadFlag(const std::string &name, bool &fValue);
     bool LoadBlockIndexGuts();
+    bool blockOnchainActive(const uint256 &hash);
 };
 
 #endif // BITCOIN_TXDB_H