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()
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"

View File

@ -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<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)
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());

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 {
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<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 GetAddressIndex(uint160 addressHash, int type,
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)
{
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<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");
}
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;

View File

@ -101,6 +101,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "setban", 3 },
{ "getblockhashes", 0 },
{ "getblockhashes", 1 },
{ "getblockhashes", 2 },
{ "getspentinfo", 0},
{ "getaddresstxids", 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_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 &timestampIndex)
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());
@ -413,8 +414,15 @@ bool CBlockTreeDB::ReadTimestampIndex(const unsigned int &high, const unsigned i
while (pcursor->Valid()) {
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 (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 &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) {
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<CDBIterator> pcursor(NewIterator());

View File

@ -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<std::pair<CAddressIndexKey, CAmount> > &addressIndex,
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 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 ReadFlag(const std::string &name, bool &fValue);
bool LoadBlockIndexGuts();
bool blockOnchainActive(const uint256 &hash);
};
#endif // BITCOIN_TXDB_H