Skip to content

logical timestamp indexing of block hashes #29

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 18, 2016
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
22 changes: 16 additions & 6 deletions qa/rpc-tests/timestampindex.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"


Expand Down
25 changes: 21 additions & 4 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1451,12 +1451,12 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
return res;
}

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;
Expand Down Expand Up @@ -2648,10 +2648,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());

Expand Down
61 changes: 60 additions & 1 deletion src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,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;
Expand Down Expand Up @@ -697,7 +756,7 @@ class CScriptCheck
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,
Expand Down
52 changes: 46 additions & 6 deletions src/rpcblockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,35 +275,75 @@ UniValue getrawmempool(const UniValue& params, bool fHelp)
return mempoolToJSON(fVerbose);
}


Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: extra line

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 (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 (!GetTimestampIndex(high, low, blockHashes)) {
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;
Expand Down
1 change: 1 addition & 0 deletions src/rpcclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "setban", 3 },
{ "getblockhashes", 0 },
{ "getblockhashes", 1 },
{ "getblockhashes", 2 },
{ "getspentinfo", 0},
{ "getaddresstxids", 0},
{ "getaddressbalance", 0},
Expand Down
40 changes: 37 additions & 3 deletions src/txdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,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';

Expand Down Expand Up @@ -275,7 +276,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());

Expand All @@ -284,8 +285,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;
Expand All @@ -295,6 +303,22 @@ bool CBlockTreeDB::ReadTimestampIndex(const unsigned int &high, const unsigned i
return true;
}

bool CBlockTreeDB::WriteTimestampBlockIndex(const CTimestampBlockIndexKey &blockhashIndex, const CTimestampBlockIndexValue &logicalts) {
CDBBatch batch(&GetObfuscateKey());
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');
}
Expand All @@ -307,6 +331,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());
Expand Down
7 changes: 6 additions & 1 deletion src/txdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ struct CAddressIndexIteratorKey;
struct CAddressIndexIteratorHeightKey;
struct CTimestampIndexKey;
struct CTimestampIndexIteratorKey;
struct CTimestampBlockIndexKey;
struct CTimestampBlockIndexValue;
struct CSpentIndexKey;
struct CSpentIndexValue;
class uint256;
Expand Down Expand Up @@ -77,10 +79,13 @@ class CBlockTreeDB : public CDBWrapper
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