diff --git a/qa/rpc-tests/timestampindex.py b/qa/rpc-tests/timestampindex.py index 46ff710bd..289c81b2a 100755 --- a/qa/rpc-tests/timestampindex.py +++ b/qa/rpc-tests/timestampindex.py @@ -35,15 +35,25 @@ class TimestampIndexTest(BitcoinTestFramework): 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 e175afeb6..d5fa7c045 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1595,12 +1595,12 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return true; } -bool GetTimestampIndex(const unsigned int &high, const unsigned int &low, std::vector &hashes) +bool GetTimestampIndex(const unsigned int &high, const unsigned int &low, const bool fActiveOnly, std::vector > &hashes) { if (!fTimestampIndex) return error("Timestamp index not enabled"); - if (!pblocktree->ReadTimestampIndex(high, low, hashes)) + if (!pblocktree->ReadTimestampIndex(high, low, fActiveOnly, hashes)) return error("Unable to get hashes for timestamps"); return true; @@ -2842,10 +2842,27 @@ 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()))) + if (fTimestampIndex) { + unsigned int logicalTS = pindex->nTime; + unsigned int prevLogicalTS = 0; + + // retrieve logical timestamp of the previous block + if (pindex->pprev) + if (!pblocktree->ReadTimestampBlockIndex(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]\n", __func__, pindex->nTime, prevLogicalTS, logicalTS); + } + + if (!pblocktree->WriteTimestampIndex(CTimestampIndexKey(logicalTS, pindex->GetBlockHash()))) return AbortNode(state, "Failed to write timestamp index"); + if (!pblocktree->WriteTimestampBlockIndex(CTimestampBlockIndexKey(pindex->GetBlockHash()), CTimestampBlockIndexValue(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 931c6a356..b611ccef7 100644 --- a/src/main.h +++ b/src/main.h @@ -335,6 +335,65 @@ struct CTimestampIndexKey { } }; +struct CTimestampBlockIndexKey { + uint256 blockHash; + + size_t GetSerializeSize(int nType, int nVersion) const { + return 32; + } + + template + void Serialize(Stream& s, int nType, int nVersion) const { + blockHash.Serialize(s, nType, nVersion); + } + + template + void Unserialize(Stream& s, int nType, int nVersion) { + blockHash.Unserialize(s, nType, nVersion); + } + + CTimestampBlockIndexKey(uint256 hash) { + blockHash = hash; + } + + CTimestampBlockIndexKey() { + SetNull(); + } + + void SetNull() { + blockHash.SetNull(); + } +}; + +struct CTimestampBlockIndexValue { + unsigned int ltimestamp; + size_t GetSerializeSize(int nType, int nVersion) const { + return 4; + } + + template + void Serialize(Stream& s, int nType, int nVersion) const { + ser_writedata32be(s, ltimestamp); + } + + template + void Unserialize(Stream& s, int nType, int nVersion) { + ltimestamp = ser_readdata32be(s); + } + + CTimestampBlockIndexValue (unsigned int time) { + ltimestamp = time; + } + + CTimestampBlockIndexValue() { + SetNull(); + } + + void SetNull() { + ltimestamp = 0; + } +}; + struct CAddressUnspentKey { unsigned int type; uint160 hashBytes; @@ -710,7 +769,7 @@ public: ScriptError GetScriptError() const { return error; } }; -bool GetTimestampIndex(const unsigned int &high, const unsigned int &low, std::vector &hashes); +bool GetTimestampIndex(const unsigned int &high, const unsigned int &low, const bool fActiveOnly, std::vector > &hashes); bool GetSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value); bool GetAddressIndex(uint160 addressHash, int type, std::vector > &addressIndex, diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index da541b0c5..9902b44ba 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -445,33 +445,72 @@ UniValue getblockdeltas(const UniValue& params, bool fHelp) UniValue getblockhashes(const UniValue& params, bool fHelp) { - if (fHelp || params.size() != 2) + if (fHelp || params.size() < 2) throw runtime_error( "getblockhashes timestamp\n" "\nReturns array of hashes of blocks within the timestamp range provided.\n" "\nArguments:\n" "1. high (numeric, required) The newer block timestamp\n" "2. low (numeric, required) The older block timestamp\n" + "3. options (string, required) A json object\n" + " {\n" + " \"noOrphans\":true (boolean) will only include blocks on the main chain\n" + " \"logicalTimes\":true (boolean) will include logical timestamps with hashes\n" + " }\n" "\nResult:\n" "[\n" " \"hash\" (string) The block hash\n" "]\n" + "[\n" + " {\n" + " \"blockhash\": (string) The block hash\n" + " \"logicalts\": (numeric) The logical timestamp\n" + " }\n" + "]\n" "\nExamples:\n" + HelpExampleCli("getblockhashes", "1231614698 1231024505") + HelpExampleRpc("getblockhashes", "1231614698, 1231024505") - ); + + HelpExampleCli("getblockhashes", "1231614698 1231024505 '{\"noOrphans\":false, \"logicalTimes\":true}'") + ); unsigned int high = params[0].get_int(); unsigned int low = params[1].get_int(); - std::vector blockHashes; + bool fActiveOnly = false; + bool fLogicalTS = false; - if (!GetTimestampIndex(high, low, blockHashes)) { + if (params.size() > 2) { + if (params[2].isObject()) { + UniValue noOrphans = find_value(params[2].get_obj(), "noOrphans"); + UniValue returnLogical = find_value(params[2].get_obj(), "logicalTimes"); + + if (noOrphans.isBool()) + fActiveOnly = noOrphans.get_bool(); + + if (returnLogical.isBool()) + fLogicalTS = returnLogical.get_bool(); + } + } + + std::vector > blockHashes; + + if (fActiveOnly) + LOCK(cs_main); + + if (!GetTimestampIndex(high, low, fActiveOnly, blockHashes)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for block hashes"); } UniValue result(UniValue::VARR); - for (std::vector::const_iterator it=blockHashes.begin(); it!=blockHashes.end(); it++) { - result.push_back(it->GetHex()); + + for (std::vector >::const_iterator it=blockHashes.begin(); it!=blockHashes.end(); it++) { + if (fLogicalTS) { + UniValue item(UniValue::VOBJ); + item.push_back(Pair("blockhash", it->first.GetHex())); + item.push_back(Pair("logicalts", (int)it->second)); + result.push_back(item); + } else { + result.push_back(it->first.GetHex()); + } } return result; diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 95d6cb303..23bd7bcc4 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -101,6 +101,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "setban", 3 }, { "getblockhashes", 0 }, { "getblockhashes", 1 }, + { "getblockhashes", 2 }, { "getspentinfo", 0}, { "getaddresstxids", 0}, { "getaddressbalance", 0}, diff --git a/src/txdb.cpp b/src/txdb.cpp index 97ebd523b..f0678809b 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -29,6 +29,7 @@ static const char DB_TXINDEX = 't'; static const char DB_ADDRESSINDEX = 'd'; 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'; @@ -404,7 +405,7 @@ bool CBlockTreeDB::WriteTimestampIndex(const CTimestampIndexKey ×tampIndex) return WriteBatch(batch); } -bool CBlockTreeDB::ReadTimestampIndex(const unsigned int &high, const unsigned int &low, std::vector &hashes) { +bool CBlockTreeDB::ReadTimestampIndex(const unsigned int &high, const unsigned int &low, const bool fActiveOnly, std::vector > &hashes) { boost::scoped_ptr pcursor(NewIterator()); @@ -413,8 +414,15 @@ bool CBlockTreeDB::ReadTimestampIndex(const unsigned int &high, const unsigned i while (pcursor->Valid()) { boost::this_thread::interruption_point(); std::pair key; - if (pcursor->GetKey(key) && key.first == DB_TIMESTAMPINDEX && key.second.timestamp <= high) { - hashes.push_back(key.second.blockHash); + if (pcursor->GetKey(key) && key.first == DB_TIMESTAMPINDEX && key.second.timestamp < high) { + if (fActiveOnly) { + if (blockOnchainActive(key.second.blockHash)) { + hashes.push_back(std::make_pair(key.second.blockHash, key.second.timestamp)); + } + } else { + hashes.push_back(std::make_pair(key.second.blockHash, key.second.timestamp)); + } + pcursor->Next(); } else { break; @@ -424,6 +432,22 @@ bool CBlockTreeDB::ReadTimestampIndex(const unsigned int &high, const unsigned i return true; } +bool CBlockTreeDB::WriteTimestampBlockIndex(const CTimestampBlockIndexKey &blockhashIndex, const CTimestampBlockIndexValue &logicalts) { + CDBBatch batch(*this); + batch.Write(make_pair(DB_BLOCKHASHINDEX, blockhashIndex), logicalts); + return WriteBatch(batch); +} + +bool CBlockTreeDB::ReadTimestampBlockIndex(const uint256 &hash, unsigned int <imestamp) { + + CTimestampBlockIndexValue(lts); + if (!Read(std::make_pair(DB_BLOCKHASHINDEX, hash), lts)) + return false; + + ltimestamp = lts.ltimestamp; + return true; +} + bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) { return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0'); } @@ -436,6 +460,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 pcursor(NewIterator()); diff --git a/src/txdb.h b/src/txdb.h index fa04a5c1f..5b2c8afd1 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -24,6 +24,8 @@ struct CAddressIndexIteratorKey; struct CAddressIndexIteratorHeightKey; struct CTimestampIndexKey; struct CTimestampIndexIteratorKey; +struct CTimestampBlockIndexKey; +struct CTimestampBlockIndexValue; struct CSpentIndexKey; struct CSpentIndexValue; class uint256; @@ -90,10 +92,13 @@ public: std::vector > &addressIndex, int start = 0, int end = 0); bool WriteTimestampIndex(const CTimestampIndexKey ×tampIndex); - bool ReadTimestampIndex(const unsigned int &high, const unsigned int &low, std::vector &vect); + bool ReadTimestampIndex(const unsigned int &high, const unsigned int &low, const bool fActiveOnly, std::vector > &vect); + bool WriteTimestampBlockIndex(const CTimestampBlockIndexKey &blockhashIndex, const CTimestampBlockIndexValue &logicalts); + bool ReadTimestampBlockIndex(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