main: add block timestamp index
This commit is contained in:
parent
24deb4efc1
commit
935ca8f832
@ -98,6 +98,7 @@ testScripts = [
|
|||||||
'nodehandling.py',
|
'nodehandling.py',
|
||||||
'reindex.py',
|
'reindex.py',
|
||||||
'addressindex.py',
|
'addressindex.py',
|
||||||
|
'timestampindex.py',
|
||||||
'decodescript.py',
|
'decodescript.py',
|
||||||
'p2p-fullblocktest.py',
|
'p2p-fullblocktest.py',
|
||||||
'blockchain.py',
|
'blockchain.py',
|
||||||
|
51
qa/rpc-tests/timestampindex.py
Executable file
51
qa/rpc-tests/timestampindex.py
Executable 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()
|
28
src/main.cpp
28
src/main.cpp
@ -67,6 +67,7 @@ bool fImporting = false;
|
|||||||
bool fReindex = false;
|
bool fReindex = false;
|
||||||
bool fTxIndex = false;
|
bool fTxIndex = false;
|
||||||
bool fAddressIndex = false;
|
bool fAddressIndex = false;
|
||||||
|
bool fTimestampIndex = false;
|
||||||
bool fHavePruned = false;
|
bool fHavePruned = false;
|
||||||
bool fPruneMode = false;
|
bool fPruneMode = false;
|
||||||
bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
|
bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
|
||||||
@ -1439,6 +1440,17 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
bool GetAddressIndex(uint160 addressHash, int type, std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex)
|
||||||
{
|
{
|
||||||
if (!fAddressIndex)
|
if (!fAddressIndex)
|
||||||
@ -2471,8 +2483,12 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||||||
return AbortNode(state, "Failed to write transaction index");
|
return AbortNode(state, "Failed to write transaction index");
|
||||||
|
|
||||||
if (fAddressIndex)
|
if (fAddressIndex)
|
||||||
if (!pblocktree->WriteAddressIndex(addressIndex))
|
if (!pblocktree->WriteAddressIndex(addressIndex))
|
||||||
return AbortNode(state, "Failed to write address index");
|
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
|
// add this block to the view's block chain
|
||||||
view.SetBestBlock(pindex->GetBlockHash());
|
view.SetBestBlock(pindex->GetBlockHash());
|
||||||
@ -3869,6 +3885,10 @@ bool static LoadBlockIndexDB()
|
|||||||
pblocktree->ReadFlag("addressindex", fAddressIndex);
|
pblocktree->ReadFlag("addressindex", fAddressIndex);
|
||||||
LogPrintf("%s: address index %s\n", __func__, fAddressIndex ? "enabled" : "disabled");
|
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");
|
||||||
|
|
||||||
// Load pointer to end of best chain
|
// Load pointer to end of best chain
|
||||||
BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock());
|
BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock());
|
||||||
if (it == mapBlockIndex.end())
|
if (it == mapBlockIndex.end())
|
||||||
@ -4034,6 +4054,10 @@ bool InitBlockIndex(const CChainParams& chainparams)
|
|||||||
fAddressIndex = GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX);
|
fAddressIndex = GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX);
|
||||||
pblocktree->WriteFlag("addressindex", fAddressIndex);
|
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");
|
LogPrintf("Initializing databases...\n");
|
||||||
|
|
||||||
// Only add the genesis block if not reindexing (in which case we reuse the one already on disk)
|
// Only add the genesis block if not reindexing (in which case we reuse the one already on disk)
|
||||||
|
63
src/main.h
63
src/main.h
@ -112,6 +112,7 @@ static const unsigned int DEFAULT_BYTES_PER_SIGOP = 20;
|
|||||||
static const bool DEFAULT_CHECKPOINTS_ENABLED = true;
|
static const bool DEFAULT_CHECKPOINTS_ENABLED = true;
|
||||||
static const bool DEFAULT_TXINDEX = false;
|
static const bool DEFAULT_TXINDEX = false;
|
||||||
static const bool DEFAULT_ADDRESSINDEX = false;
|
static const bool DEFAULT_ADDRESSINDEX = false;
|
||||||
|
static const bool DEFAULT_TIMESTAMPINDEX = false;
|
||||||
static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100;
|
static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100;
|
||||||
|
|
||||||
static const bool DEFAULT_TESTSAFEMODE = false;
|
static const bool DEFAULT_TESTSAFEMODE = false;
|
||||||
@ -291,6 +292,67 @@ struct CNodeStateStats {
|
|||||||
std::vector<int> vHeightInFlight;
|
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 {
|
struct CAddressIndexKey {
|
||||||
unsigned int type;
|
unsigned int type;
|
||||||
uint160 hashBytes;
|
uint160 hashBytes;
|
||||||
@ -510,6 +572,7 @@ public:
|
|||||||
ScriptError GetScriptError() const { return error; }
|
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);
|
bool GetAddressIndex(uint160 addressHash, int type, std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex);
|
||||||
|
|
||||||
/** Functions for disk access for blocks */
|
/** Functions for disk access for blocks */
|
||||||
|
@ -275,6 +275,38 @@ UniValue getrawmempool(const UniValue& params, bool fHelp)
|
|||||||
return mempoolToJSON(fVerbose);
|
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)
|
UniValue getblockhash(const UniValue& params, bool fHelp)
|
||||||
{
|
{
|
||||||
if (fHelp || params.size() != 1)
|
if (fHelp || params.size() != 1)
|
||||||
|
@ -278,6 +278,7 @@ static const CRPCCommand vRPCCommands[] =
|
|||||||
{ "blockchain", "getbestblockhash", &getbestblockhash, true },
|
{ "blockchain", "getbestblockhash", &getbestblockhash, true },
|
||||||
{ "blockchain", "getblockcount", &getblockcount, true },
|
{ "blockchain", "getblockcount", &getblockcount, true },
|
||||||
{ "blockchain", "getblock", &getblock, true },
|
{ "blockchain", "getblock", &getblock, true },
|
||||||
|
{ "blockchain", "getblockhashes", &getblockhashes, true },
|
||||||
{ "blockchain", "getblockhash", &getblockhash, true },
|
{ "blockchain", "getblockhash", &getblockhash, true },
|
||||||
{ "blockchain", "getblockheader", &getblockheader, true },
|
{ "blockchain", "getblockheader", &getblockheader, true },
|
||||||
{ "blockchain", "getchaintips", &getchaintips, true },
|
{ "blockchain", "getchaintips", &getchaintips, true },
|
||||||
|
@ -258,6 +258,7 @@ extern UniValue getdifficulty(const UniValue& params, bool fHelp);
|
|||||||
extern UniValue settxfee(const UniValue& params, bool fHelp);
|
extern UniValue settxfee(const UniValue& params, bool fHelp);
|
||||||
extern UniValue getmempoolinfo(const UniValue& params, bool fHelp);
|
extern UniValue getmempoolinfo(const UniValue& params, bool fHelp);
|
||||||
extern UniValue getrawmempool(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 getblockhash(const UniValue& params, bool fHelp);
|
||||||
extern UniValue getblockheader(const UniValue& params, bool fHelp);
|
extern UniValue getblockheader(const UniValue& params, bool fHelp);
|
||||||
extern UniValue getblock(const UniValue& params, bool fHelp);
|
extern UniValue getblock(const UniValue& params, bool fHelp);
|
||||||
|
27
src/txdb.cpp
27
src/txdb.cpp
@ -22,6 +22,7 @@ static const char DB_COINS = 'c';
|
|||||||
static const char DB_BLOCK_FILES = 'f';
|
static const char DB_BLOCK_FILES = 'f';
|
||||||
static const char DB_TXINDEX = 't';
|
static const char DB_TXINDEX = 't';
|
||||||
static const char DB_ADDRESSINDEX = 'a';
|
static const char DB_ADDRESSINDEX = 'a';
|
||||||
|
static const char DB_TIMESTAMPINDEX = 's';
|
||||||
static const char DB_BLOCK_INDEX = 'b';
|
static const char DB_BLOCK_INDEX = 'b';
|
||||||
|
|
||||||
static const char DB_BEST_BLOCK = 'B';
|
static const char DB_BEST_BLOCK = 'B';
|
||||||
@ -196,6 +197,32 @@ bool CBlockTreeDB::ReadAddressIndex(uint160 addressHash, int type, std::vector<s
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CBlockTreeDB::WriteTimestampIndex(const CTimestampIndexKey ×tampIndex) {
|
||||||
|
CDBBatch batch(&GetObfuscateKey());
|
||||||
|
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) {
|
bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) {
|
||||||
return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0');
|
return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0');
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@ class CBlockIndex;
|
|||||||
struct CDiskTxPos;
|
struct CDiskTxPos;
|
||||||
struct CAddressIndexKey;
|
struct CAddressIndexKey;
|
||||||
struct CAddressIndexIteratorKey;
|
struct CAddressIndexIteratorKey;
|
||||||
|
struct CTimestampIndexKey;
|
||||||
|
struct CTimestampIndexIteratorKey;
|
||||||
class uint256;
|
class uint256;
|
||||||
|
|
||||||
//! -dbcache default (MiB)
|
//! -dbcache default (MiB)
|
||||||
@ -61,6 +63,8 @@ public:
|
|||||||
bool WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> > &list);
|
bool WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> > &list);
|
||||||
bool WriteAddressIndex(const std::vector<std::pair<CAddressIndexKey, CAmount> > &vect);
|
bool WriteAddressIndex(const std::vector<std::pair<CAddressIndexKey, CAmount> > &vect);
|
||||||
bool ReadAddressIndex(uint160 addressHash, int type, std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex);
|
bool ReadAddressIndex(uint160 addressHash, int type, std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex);
|
||||||
|
bool WriteTimestampIndex(const CTimestampIndexKey ×tampIndex);
|
||||||
|
bool ReadTimestampIndex(const unsigned int &high, const unsigned int &low, std::vector<uint256> &vect);
|
||||||
bool WriteFlag(const std::string &name, bool fValue);
|
bool WriteFlag(const std::string &name, bool fValue);
|
||||||
bool ReadFlag(const std::string &name, bool &fValue);
|
bool ReadFlag(const std::string &name, bool &fValue);
|
||||||
bool LoadBlockIndexGuts();
|
bool LoadBlockIndexGuts();
|
||||||
|
Loading…
Reference in New Issue
Block a user