logical timestamp indexing of block hashes
This commit is contained in:
parent
1a07bc991a
commit
1537c902dc
|
@ -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"
|
||||
|
||||
|
||||
|
|
25
src/main.cpp
25
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<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());
|
||||
|
||||
|
|
61
src/main.h
61
src/main.h
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -101,6 +101,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||
{ "setban", 3 },
|
||||
{ "getblockhashes", 0 },
|
||||
{ "getblockhashes", 1 },
|
||||
{ "getblockhashes", 2 },
|
||||
{ "getspentinfo", 0},
|
||||
{ "getaddresstxids", 0},
|
||||
{ "getaddressbalance", 0},
|
||||
|
|
40
src/txdb.cpp
40
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<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 <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<CDBIterator> pcursor(NewIterator());
|
||||
|
|
|
@ -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 ×tampIndex);
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue