main: add block timestamp index

This commit is contained in:
Braydon Fuller 2016-03-22 18:11:04 -04:00 committed by Simon
parent 59568ff5e8
commit 4fcfdc9b21
8 changed files with 205 additions and 2 deletions

View File

@ -48,6 +48,7 @@ testScripts=(
'nodehandling.py'
'reindex.py'
'addressindex.py'
'timestampindex.py'
'decodescript.py'
'blockchain.py'
'disablewallet.py'

51
qa/rpc-tests/timestampindex.py Executable file
View File

@ -0,0 +1,51 @@
#!/usr/bin/env python2
# Copyright (c) 2014-2015 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
#
# Test timestampindex generation and fetching
#
import time
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
class TimestampIndexTest(BitcoinTestFramework):
def setup_chain(self):
print("Initializing test directory "+self.options.tmpdir)
initialize_chain_clean(self.options.tmpdir, 4)
def setup_network(self):
self.nodes = []
# Nodes 0/1 are "wallet" nodes
self.nodes.append(start_node(0, self.options.tmpdir, ["-debug"]))
self.nodes.append(start_node(1, self.options.tmpdir, ["-debug", "-timestampindex"]))
# Nodes 2/3 are used for testing
self.nodes.append(start_node(2, self.options.tmpdir, ["-debug"]))
self.nodes.append(start_node(3, self.options.tmpdir, ["-debug", "-timestampindex"]))
connect_nodes(self.nodes[0], 1)
connect_nodes(self.nodes[0], 2)
connect_nodes(self.nodes[0], 3)
self.is_network_split = False
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"]
self.sync_all()
print "Checking timestamp index..."
hashes = self.nodes[1].getblockhashes(high, low)
assert_equal(len(hashes), 5)
assert_equal(sorted(blockhashes), sorted(hashes))
print "Passed\n"
if __name__ == '__main__':
TimestampIndexTest().main()

View File

@ -68,6 +68,7 @@ bool fImporting = false;
bool fReindex = false;
bool fTxIndex = false;
bool fAddressIndex = false;
bool fTimestampIndex = false;
bool fHavePruned = false;
bool fPruneMode = false;
bool fIsBareMultisigStd = true;
@ -1583,6 +1584,17 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
return true;
}
bool GetTimestampIndex(const unsigned int &high, const unsigned int &low, std::vector<uint256> &hashes)
{
if (!fTimestampIndex)
return error("Timestamp index not enabled");
if (!pblocktree->ReadTimestampIndex(high, low, hashes))
return error("Unable to get hashes for timestamps");
return true;
}
bool GetAddressIndex(uint160 addressHash, int type, std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex)
{
if (!fAddressIndex)
@ -2655,8 +2667,12 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
return AbortNode(state, "Failed to write transaction index");
if (fAddressIndex)
if (!pblocktree->WriteAddressIndex(addressIndex))
return AbortNode(state, "Failed to write address index");
if (!pblocktree->WriteAddressIndex(addressIndex))
return AbortNode(state, "Failed to write address index");
if (fTimestampIndex)
if (!pblocktree->WriteTimestampIndex(CTimestampIndexKey(pindex->nTime, pindex->GetBlockHash())))
return AbortNode(state, "Failed to write timestamp index");
// add this block to the view's block chain
view.SetBestBlock(pindex->GetBlockHash());
@ -4148,6 +4164,10 @@ bool static LoadBlockIndexDB()
pblocktree->ReadFlag("addressindex", fAddressIndex);
LogPrintf("%s: address index %s\n", __func__, fAddressIndex ? "enabled" : "disabled");
// Check whether we have a timestamp index
pblocktree->ReadFlag("timestampindex", fTimestampIndex);
LogPrintf("%s: timestamp index %s\n", __func__, fTimestampIndex ? "enabled" : "disabled");
// Fill in-memory data
BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex)
{
@ -4486,6 +4506,10 @@ bool InitBlockIndex() {
fAddressIndex = GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX);
pblocktree->WriteFlag("addressindex", fAddressIndex);
// Use the provided setting for -timestampindex in the new database
fTimestampIndex = GetBoolArg("-timestampindex", DEFAULT_TIMESTAMPINDEX);
pblocktree->WriteFlag("timestampindex", fTimestampIndex);
LogPrintf("Initializing databases...\n");
// Only add the genesis block if not reindexing (in which case we reuse the one already on disk)

View File

@ -100,6 +100,7 @@ static const unsigned int DATABASE_FLUSH_INTERVAL = 24 * 60 * 60;
static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111;
static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60;
static const bool DEFAULT_ADDRESSINDEX = false;
static const bool DEFAULT_TIMESTAMPINDEX = false;
// Sanity check the magic numbers when we change them
BOOST_STATIC_ASSERT(DEFAULT_BLOCK_MAX_SIZE <= MAX_BLOCK_SIZE);
@ -269,6 +270,67 @@ struct CNodeStateStats {
std::vector<int> vHeightInFlight;
};
struct CTimestampIndexIteratorKey {
unsigned int timestamp;
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, timestamp);
}
template<typename Stream>
void Unserialize(Stream& s, int nType, int nVersion) {
timestamp = ser_readdata32be(s);
}
CTimestampIndexIteratorKey(unsigned int time) {
timestamp = time;
}
CTimestampIndexIteratorKey() {
SetNull();
}
void SetNull() {
timestamp = 0;
}
};
struct CTimestampIndexKey {
unsigned int timestamp;
uint256 blockHash;
size_t GetSerializeSize(int nType, int nVersion) const {
return 36;
}
template<typename Stream>
void Serialize(Stream& s, int nType, int nVersion) const {
ser_writedata32be(s, timestamp);
blockHash.Serialize(s, nType, nVersion);
}
template<typename Stream>
void Unserialize(Stream& s, int nType, int nVersion) {
timestamp = ser_readdata32be(s);
blockHash.Unserialize(s, nType, nVersion);
}
CTimestampIndexKey(unsigned int time, uint256 hash) {
timestamp = time;
blockHash = hash;
}
CTimestampIndexKey() {
SetNull();
}
void SetNull() {
timestamp = 0;
blockHash.SetNull();
}
};
struct CAddressIndexKey {
unsigned int type;
uint160 hashBytes;
@ -521,6 +583,7 @@ public:
ScriptError GetScriptError() const { return error; }
};
bool GetTimestampIndex(const unsigned int &high, const unsigned int &low, std::vector<uint256> &hashes);
bool GetAddressIndex(uint160 addressHash, int type, std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex);
/** Functions for disk access for blocks */

View File

@ -313,6 +313,38 @@ UniValue getrawmempool(const UniValue& params, bool fHelp)
return mempoolToJSON(fVerbose);
}
UniValue getblockhashes(const UniValue& params, bool fHelp)
{
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"
"\nResult:\n"
"["
" \"hash\" (string) The block hash\n"
"]"
"\nExamples:\n"
);
unsigned int high = params[0].get_int();
unsigned int low = params[1].get_int();
std::vector<uint256> blockHashes;
if (!GetTimestampIndex(high, low, 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());
}
return result;
}
UniValue getblockhash(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 1)

View File

@ -269,6 +269,7 @@ extern UniValue getdifficulty(const UniValue& params, bool fHelp);
extern UniValue settxfee(const UniValue& params, bool fHelp);
extern UniValue getmempoolinfo(const UniValue& params, bool fHelp);
extern UniValue getrawmempool(const UniValue& params, bool fHelp);
extern UniValue getblockhashes(const UniValue& params, bool fHelp);
extern UniValue getblockhash(const UniValue& params, bool fHelp);
extern UniValue getblockheader(const UniValue& params, bool fHelp);
extern UniValue getblock(const UniValue& params, bool fHelp);

View File

@ -27,6 +27,7 @@ static const char DB_COINS = 'c';
static const char DB_BLOCK_FILES = 'f';
static const char DB_TXINDEX = 't';
static const char DB_ADDRESSINDEX = 'd';
static const char DB_TIMESTAMPINDEX = 'S';
static const char DB_BLOCK_INDEX = 'b';
static const char DB_BEST_BLOCK = 'B';
@ -326,6 +327,32 @@ bool CBlockTreeDB::ReadAddressIndex(uint160 addressHash, int type, std::vector<s
return true;
}
bool CBlockTreeDB::WriteTimestampIndex(const CTimestampIndexKey &timestampIndex) {
CDBBatch batch(*this);
batch.Write(make_pair(DB_TIMESTAMPINDEX, timestampIndex), NULL);
return WriteBatch(batch);
}
bool CBlockTreeDB::ReadTimestampIndex(const unsigned int &high, const unsigned int &low, std::vector<uint256> &hashes) {
boost::scoped_ptr<CDBIterator> pcursor(NewIterator());
pcursor->Seek(make_pair(DB_TIMESTAMPINDEX, CTimestampIndexIteratorKey(low)));
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);
pcursor->Next();
} else {
break;
}
}
return true;
}
bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) {
return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0');
}

View File

@ -19,6 +19,8 @@ class CBlockIndex;
struct CDiskTxPos;
struct CAddressIndexKey;
struct CAddressIndexIteratorKey;
struct CTimestampIndexKey;
struct CTimestampIndexIteratorKey;
class uint256;
//! -dbcache default (MiB)
@ -74,6 +76,8 @@ public:
bool WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> > &list);
bool WriteAddressIndex(const std::vector<std::pair<CAddressIndexKey, CAmount> > &vect);
bool ReadAddressIndex(uint160 addressHash, int type, std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex);
bool WriteTimestampIndex(const CTimestampIndexKey &timestampIndex);
bool ReadTimestampIndex(const unsigned int &high, const unsigned int &low, std::vector<uint256> &vect);
bool WriteFlag(const std::string &name, bool fValue);
bool ReadFlag(const std::string &name, bool &fValue);
bool LoadBlockIndexGuts();