Skip to content

Commit

Permalink
Merge pull request bitcoin#29 from danjurious/logicalts
Browse files Browse the repository at this point in the history
logical timestamp indexing of block hashes
  • Loading branch information
braydonf committed Aug 18, 2016
2 parents 796a274 + 36f5ee5 commit de6f29e
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 21 deletions.
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\n", __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 @@ -357,6 +357,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 @@ -699,7 +758,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
51 changes: 45 additions & 6 deletions src/rpcblockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -277,33 +277,72 @@ UniValue getrawmempool(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 (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, blockHashes)) {
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

0 comments on commit de6f29e

Please sign in to comment.