logical timestamp indexing of block hashes

This commit is contained in:
Chethan Krishna 2016-07-21 16:52:51 -04:00 committed by Simon
parent 1a07bc991a
commit 1537c902dc
7 changed files with 186 additions and 21 deletions

View File

@ -35,15 +35,25 @@ class TimestampIndexTest(BitcoinTestFramework):
self.sync_all() self.sync_all()
def run_test(self): def run_test(self):
print "Mining 5 blocks..." print "Mining 25 blocks..."
blockhashes = self.nodes[0].generate(5) blockhashes = self.nodes[0].generate(25)
low = self.nodes[0].getblock(blockhashes[0])["time"] time.sleep(3)
high = self.nodes[0].getblock(blockhashes[4])["time"] 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() self.sync_all()
low = self.nodes[1].getblock(blockhashes[0])["time"]
high = low + 76
print "Checking timestamp index..." print "Checking timestamp index..."
hashes = self.nodes[1].getblockhashes(high, low) 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" print "Passed\n"

View File

@ -1595,12 +1595,12 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
return true; return true;
} }
bool GetTimestampIndex(const unsigned int &high, const unsigned int &low, std::vector<uint256> &hashes) bool GetTimestampIndex(const unsigned int &high, const unsigned int &low, const bool fActiveOnly, std::vector<std::pair<uint256, unsigned int> > &hashes)
{ {
if (!fTimestampIndex) if (!fTimestampIndex)
return error("Timestamp index not enabled"); 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 error("Unable to get hashes for timestamps");
return true; return true;
@ -2842,10 +2842,27 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
if (!pblocktree->UpdateSpentIndex(spentIndex)) if (!pblocktree->UpdateSpentIndex(spentIndex))
return AbortNode(state, "Failed to write transaction index"); return AbortNode(state, "Failed to write transaction index");
if (fTimestampIndex) if (fTimestampIndex) {
if (!pblocktree->WriteTimestampIndex(CTimestampIndexKey(pindex->nTime, pindex->GetBlockHash()))) 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"); 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 // add this block to the view's block chain
view.SetBestBlock(pindex->GetBlockHash()); view.SetBestBlock(pindex->GetBlockHash());

View File

@ -335,6 +335,65 @@ struct CTimestampIndexKey {
} }
}; };
struct CTimestampBlockIndexKey {
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);
}
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<typename Stream>
void Serialize(Stream& s, int nType, int nVersion) const {
ser_writedata32be(s, ltimestamp);
}
template<typename Stream>
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 { struct CAddressUnspentKey {
unsigned int type; unsigned int type;
uint160 hashBytes; uint160 hashBytes;
@ -710,7 +769,7 @@ public:
ScriptError GetScriptError() const { return error; } ScriptError GetScriptError() const { return error; }
}; };
bool GetTimestampIndex(const unsigned int &high, const unsigned int &low, std::vector<uint256> &hashes); bool GetTimestampIndex(const unsigned int &high, const unsigned int &low, const bool fActiveOnly, std::vector<std::pair<uint256, unsigned int> > &hashes);
bool GetSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value); bool GetSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value);
bool GetAddressIndex(uint160 addressHash, int type, bool GetAddressIndex(uint160 addressHash, int type,
std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex, std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex,

View File

@ -445,33 +445,72 @@ UniValue getblockdeltas(const UniValue& params, bool fHelp)
UniValue getblockhashes(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( throw runtime_error(
"getblockhashes timestamp\n" "getblockhashes timestamp\n"
"\nReturns array of hashes of blocks within the timestamp range provided.\n" "\nReturns array of hashes of blocks within the timestamp range provided.\n"
"\nArguments:\n" "\nArguments:\n"
"1. high (numeric, required) The newer block timestamp\n" "1. high (numeric, required) The newer block timestamp\n"
"2. low (numeric, required) The older 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" "\nResult:\n"
"[\n" "[\n"
" \"hash\" (string) The block hash\n" " \"hash\" (string) The block hash\n"
"]\n" "]\n"
"[\n"
" {\n"
" \"blockhash\": (string) The block hash\n"
" \"logicalts\": (numeric) The logical timestamp\n"
" }\n"
"]\n"
"\nExamples:\n" "\nExamples:\n"
+ HelpExampleCli("getblockhashes", "1231614698 1231024505") + HelpExampleCli("getblockhashes", "1231614698 1231024505")
+ HelpExampleRpc("getblockhashes", "1231614698, 1231024505") + HelpExampleRpc("getblockhashes", "1231614698, 1231024505")
+ HelpExampleCli("getblockhashes", "1231614698 1231024505 '{\"noOrphans\":false, \"logicalTimes\":true}'")
); );
unsigned int high = params[0].get_int(); unsigned int high = params[0].get_int();
unsigned int low = params[1].get_int(); unsigned int low = params[1].get_int();
std::vector<uint256> 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<std::pair<uint256, unsigned int> > 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"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for block hashes");
} }
UniValue result(UniValue::VARR); UniValue result(UniValue::VARR);
for (std::vector<uint256>::const_iterator it=blockHashes.begin(); it!=blockHashes.end(); it++) {
result.push_back(it->GetHex()); for (std::vector<std::pair<uint256, unsigned int> >::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; return result;

View File

@ -101,6 +101,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "setban", 3 }, { "setban", 3 },
{ "getblockhashes", 0 }, { "getblockhashes", 0 },
{ "getblockhashes", 1 }, { "getblockhashes", 1 },
{ "getblockhashes", 2 },
{ "getspentinfo", 0}, { "getspentinfo", 0},
{ "getaddresstxids", 0}, { "getaddresstxids", 0},
{ "getaddressbalance", 0}, { "getaddressbalance", 0},

View File

@ -29,6 +29,7 @@ static const char DB_TXINDEX = 't';
static const char DB_ADDRESSINDEX = 'd'; static const char DB_ADDRESSINDEX = 'd';
static const char DB_ADDRESSUNSPENTINDEX = 'u'; static const char DB_ADDRESSUNSPENTINDEX = 'u';
static const char DB_TIMESTAMPINDEX = 'S'; static const char DB_TIMESTAMPINDEX = 'S';
static const char DB_BLOCKHASHINDEX = 'z';
static const char DB_SPENTINDEX = 'p'; static const char DB_SPENTINDEX = 'p';
static const char DB_BLOCK_INDEX = 'b'; static const char DB_BLOCK_INDEX = 'b';
@ -404,7 +405,7 @@ bool CBlockTreeDB::WriteTimestampIndex(const CTimestampIndexKey &timestampIndex)
return WriteBatch(batch); return WriteBatch(batch);
} }
bool CBlockTreeDB::ReadTimestampIndex(const unsigned int &high, const unsigned int &low, std::vector<uint256> &hashes) { bool CBlockTreeDB::ReadTimestampIndex(const unsigned int &high, const unsigned int &low, const bool fActiveOnly, std::vector<std::pair<uint256, unsigned int> > &hashes) {
boost::scoped_ptr<CDBIterator> pcursor(NewIterator()); boost::scoped_ptr<CDBIterator> pcursor(NewIterator());
@ -413,8 +414,15 @@ bool CBlockTreeDB::ReadTimestampIndex(const unsigned int &high, const unsigned i
while (pcursor->Valid()) { while (pcursor->Valid()) {
boost::this_thread::interruption_point(); boost::this_thread::interruption_point();
std::pair<char, CTimestampIndexKey> key; std::pair<char, CTimestampIndexKey> key;
if (pcursor->GetKey(key) && key.first == DB_TIMESTAMPINDEX && key.second.timestamp <= high) { if (pcursor->GetKey(key) && key.first == DB_TIMESTAMPINDEX && key.second.timestamp < high) {
hashes.push_back(key.second.blockHash); 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(); pcursor->Next();
} else { } else {
break; break;
@ -424,6 +432,22 @@ bool CBlockTreeDB::ReadTimestampIndex(const unsigned int &high, const unsigned i
return true; 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 &ltimestamp) {
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) { bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) {
return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0'); 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; return true;
} }
bool CBlockTreeDB::blockOnchainActive(const uint256 &hash) {
CBlockIndex* pblockindex = mapBlockIndex[hash];
if (!chainActive.Contains(pblockindex)) {
return false;
}
return true;
}
bool CBlockTreeDB::LoadBlockIndexGuts() bool CBlockTreeDB::LoadBlockIndexGuts()
{ {
boost::scoped_ptr<CDBIterator> pcursor(NewIterator()); boost::scoped_ptr<CDBIterator> pcursor(NewIterator());

View File

@ -24,6 +24,8 @@ struct CAddressIndexIteratorKey;
struct CAddressIndexIteratorHeightKey; struct CAddressIndexIteratorHeightKey;
struct CTimestampIndexKey; struct CTimestampIndexKey;
struct CTimestampIndexIteratorKey; struct CTimestampIndexIteratorKey;
struct CTimestampBlockIndexKey;
struct CTimestampBlockIndexValue;
struct CSpentIndexKey; struct CSpentIndexKey;
struct CSpentIndexValue; struct CSpentIndexValue;
class uint256; class uint256;
@ -90,10 +92,13 @@ public:
std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex, std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex,
int start = 0, int end = 0); int start = 0, int end = 0);
bool WriteTimestampIndex(const CTimestampIndexKey &timestampIndex); bool WriteTimestampIndex(const CTimestampIndexKey &timestampIndex);
bool ReadTimestampIndex(const unsigned int &high, const unsigned int &low, std::vector<uint256> &vect); bool ReadTimestampIndex(const unsigned int &high, const unsigned int &low, const bool fActiveOnly, std::vector<std::pair<uint256, unsigned int> > &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 WriteFlag(const std::string &name, bool fValue);
bool ReadFlag(const std::string &name, bool &fValue); bool ReadFlag(const std::string &name, bool &fValue);
bool LoadBlockIndexGuts(); bool LoadBlockIndexGuts();
bool blockOnchainActive(const uint256 &hash);
}; };
#endif // BITCOIN_TXDB_H #endif // BITCOIN_TXDB_H