Skip to content
This repository has been archived by the owner on Jan 23, 2018. It is now read-only.

Commit

Permalink
Address Index (#16)
Browse files Browse the repository at this point in the history
* Add address index

Fixed bugs with address index

* Patch addrindex for Zerocoin
  • Loading branch information
meyer9 authored Jan 10, 2018
1 parent b00a936 commit 59e531a
Show file tree
Hide file tree
Showing 10 changed files with 278 additions and 8 deletions.
8 changes: 7 additions & 1 deletion src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-sysperms", _("Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)"));
#endif
strUsage += HelpMessageOpt("-txindex", strprintf(_("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)"), 0));
strUsage += HelpMessageOpt("-addrindex", strprintf(_("Maintain a full address index, used by the searchrawtransactions rpc call (default: %u)"), 0));
strUsage += HelpMessageOpt("-forcestart", _("Attempt to force blockchain corruption recovery") + " " + _("on startup"));

strUsage += HelpMessageGroup(_("Connection options:"));
Expand Down Expand Up @@ -1298,7 +1299,7 @@ bool AppInit2(boost::thread_group& threadGroup)
else if (nTotalCache > (nMaxDbCache << 20))
nTotalCache = (nMaxDbCache << 20); // total cache cannot be greater than nMaxDbCache
size_t nBlockTreeDBCache = nTotalCache / 8;
if (nBlockTreeDBCache > (1 << 21) && !GetBoolArg("-txindex", true))
if (nBlockTreeDBCache > (1 << 21) && !GetBoolArg("-txindex", true) && !GetBoolArg("-addrindex", true))
nBlockTreeDBCache = (1 << 21); // block tree db cache shouldn't be larger than 2 MiB
nTotalCache -= nBlockTreeDBCache;
size_t nCoinDBCache = nTotalCache / 2; // use half of the remaining cache for coindb cache
Expand Down Expand Up @@ -1360,6 +1361,11 @@ bool AppInit2(boost::thread_group& threadGroup)
break;
}

if (fAddrIndex != GetBoolArg("-addrindex", true)) {
strLoadError = _("You need to rebuild the database using -reindex to change -addrindex");
break;
}

// Populate list of invalid/fraudulent outpoints that are banned from the chain
PopulateInvalidOutPointMap();

Expand Down
106 changes: 100 additions & 6 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "accumulators.h"
#include "addrman.h"
#include "alert.h"
#include "base58.h"
#include "chainparams.h"
#include "checkpoints.h"
#include "checkqueue.h"
Expand Down Expand Up @@ -74,6 +75,7 @@ int nScriptCheckThreads = 0;
bool fImporting = false;
bool fReindex = false;
bool fTxIndex = true;
bool fAddrIndex = true;
bool fIsBareMultisigStd = true;
bool fCheckBlockIndex = false;
bool fVerifyingBlocks = false;
Expand Down Expand Up @@ -1826,6 +1828,44 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState& state, const CTransa
return true;
}

bool ReadTransaction(CTransaction& tx, const CDiskTxPos &pos, uint256 &hashBlock) {
CAutoFile file(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION);
CBlockHeader header;
try {
file >> header;
fseek(file.Get(), pos.nTxOffset, SEEK_CUR);
file >> tx;
} catch (std::exception &e) {
return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__);
}
hashBlock = header.GetHash();
return true;
}

bool FindTransactionsByDestination(const CTxDestination &dest, std::set<CExtDiskTxPos> &setpos) {
uint160 addrid;
const CKeyID *pkeyid = boost::get<CKeyID>(&dest);
if (pkeyid) {
addrid = static_cast<uint160>(*pkeyid);
} else {
const CScriptID *pscriptid = boost::get<CScriptID>(&dest);
if (pscriptid) {
addrid = static_cast<uint160>(*pscriptid);
} else {
return false;
}
}

LOCK(cs_main);
if (!fAddrIndex)
return false;
std::vector<CExtDiskTxPos> vPos;
if (!pblocktree->ReadAddrIndex(addrid, vPos))
return false;
setpos.insert(vPos.begin(), vPos.end());
return true;
}

bool AcceptableInputs(CTxMemPool& pool, CValidationState& state, const CTransaction& tx, bool fLimitFree, bool* pfMissingInputs, bool fRejectInsaneFee, bool isDSTX)
{
AssertLockHeld(cs_main);
Expand Down Expand Up @@ -2932,6 +2972,33 @@ static int64_t nTimeIndex = 0;
static int64_t nTimeCallbacks = 0;
static int64_t nTimeTotal = 0;

// Index either: a) every data push >=8 bytes, b) if no such pushes, the entire script
void static BuildAddrIndex(const CScript &script, const CExtDiskTxPos &pos, std::vector<std::pair<uint160, CExtDiskTxPos> > &out)
{
CScript::const_iterator pc = script.begin();
CScript::const_iterator pend = script.end();
std::vector<unsigned char> data;
opcodetype opcode;
bool fHaveData = false;
while (pc < pend) {
script.GetOp(pc, opcode, data);
if (0 <= opcode && opcode <= OP_PUSHDATA4 && data.size() >= 8) { // data element
uint160 addrid;
if (data.size() <= 20) {
memcpy(&addrid, &data[0], data.size());
} else {
addrid = Hash160(data);
}
out.push_back(std::make_pair(addrid, pos));
fHaveData = true;
}
}
if (!fHaveData) {
uint160 addrid = Hash160(script);
out.push_back(std::make_pair(addrid, pos));
}
}

bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck, bool fAlreadyChecked)
{
AssertLockHeld(cs_main);
Expand Down Expand Up @@ -3005,9 +3072,13 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
CAmount nFees = 0;
int nInputs = 0;
unsigned int nSigOps = 0;
CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size()));
std::vector<std::pair<uint256, CDiskTxPos> > vPos;
vPos.reserve(block.vtx.size());
CExtDiskTxPos pos(CDiskTxPos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())), pindex->nHeight);
std::vector<std::pair<uint256, CDiskTxPos> > vPosTxid;
std::vector<std::pair<uint160, CExtDiskTxPos> > vPosAddrid;
if (fTxIndex)
vPosTxid.reserve(block.vtx.size());
if (fAddrIndex)
vPosAddrid.reserve(block.vtx.size() * 4);
blockundo.vtxundo.reserve(block.vtx.size() - 1);
CAmount nValueOut = 0;
CAmount nValueIn = 0;
Expand Down Expand Up @@ -3104,9 +3175,23 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
if (i > 0) {
blockundo.vtxundo.push_back(CTxUndo());
}
UpdateCoins(tx, state, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight);
if (fTxIndex)
vPosTxid.push_back(std::make_pair(tx.GetHash(), pos));
if (fAddrIndex) {
if (!tx.IsCoinBase()) {
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
CCoins coins;
view.GetCoins(txin.prevout.hash, coins);
if (coins.IsAvailable(txin.prevout.n)) {
BuildAddrIndex(coins.vout[txin.prevout.n].scriptPubKey, pos, vPosAddrid);
}
}
}
BOOST_FOREACH(const CTxOut &txout, tx.vout)
BuildAddrIndex(txout.scriptPubKey, pos, vPosAddrid);
}

vPos.push_back(std::make_pair(tx.GetHash(), pos));
UpdateCoins(tx, state, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight);
pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
}

Expand Down Expand Up @@ -3224,9 +3309,13 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
}

if (fTxIndex)
if (!pblocktree->WriteTxIndex(vPos))
if (!pblocktree->WriteTxIndex(vPosTxid))
return state.Abort("Failed to write transaction index");

if (fAddrIndex)
if (!pblocktree->AddAddrIndex(vPosAddrid))
return state.Abort("Failed to write address index");

// add this block to the view's block chain
view.SetBestBlock(pindex->GetBlockHash());

Expand Down Expand Up @@ -4868,6 +4957,9 @@ bool static LoadBlockIndexDB()
pblocktree->ReadFlag("txindex", fTxIndex);
LogPrintf("LoadBlockIndexDB(): transaction index %s\n", fTxIndex ? "enabled" : "disabled");

pblocktree->ReadFlag("addrindex", fAddrIndex);
LogPrintf("LoadBlockIndexDB(): address index %s\n", fAddrIndex ? "enabled" : "disabled");

// If this is written true before the next client init, then we know the shutdown process failed
pblocktree->WriteFlag("shutdown", false);

Expand Down Expand Up @@ -5001,6 +5093,8 @@ bool InitBlockIndex()
// Use the provided setting for -txindex in the new database
fTxIndex = GetBoolArg("-txindex", true);
pblocktree->WriteFlag("txindex", fTxIndex);
fAddrIndex = GetBoolArg("-addrindex", true);
pblocktree->WriteFlag("addrindex", fAddrIndex);
LogPrintf("Initializing databases...\n");

// Only add the genesis block if not reindexing (in which case we reuse the one already on disk)
Expand Down
47 changes: 47 additions & 0 deletions src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ extern bool fImporting;
extern bool fReindex;
extern int nScriptCheckThreads;
extern bool fTxIndex;
extern bool fAddrIndex;
extern bool fIsBareMultisigStd;
extern bool fCheckBlockIndex;
extern unsigned int nCoinCacheSize;
Expand Down Expand Up @@ -300,8 +301,52 @@ struct CDiskTxPos : public CDiskBlockPos {
CDiskBlockPos::SetNull();
nTxOffset = 0;
}

friend bool operator<(const CDiskTxPos &a, const CDiskTxPos &b) {
return (a.nFile < b.nFile || (
(a.nFile == b.nFile) && (a.nPos < b.nPos || (
(a.nPos == b.nPos) && (a.nTxOffset < b.nTxOffset)))));
}
};

struct CExtDiskTxPos : public CDiskTxPos
{
unsigned int nHeight;

ADD_SERIALIZE_METHODS;

template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(*(CDiskTxPos*)this);
READWRITE(VARINT(nHeight));
}

CExtDiskTxPos(const CDiskTxPos &pos, int nHeightIn) : CDiskTxPos(pos), nHeight(nHeightIn) {
}

CExtDiskTxPos() {
SetNull();
}

void SetNull() {
CDiskTxPos::SetNull();
nHeight = 0;
}

friend bool operator==(const CExtDiskTxPos &a, const CExtDiskTxPos &b) {
return (a.nHeight == b.nHeight && a.nFile == b.nFile && a.nPos == b.nPos && a.nTxOffset == b.nTxOffset);
}

friend bool operator!=(const CExtDiskTxPos &a, const CExtDiskTxPos &b) {
return !(a == b);
}

friend bool operator<(const CExtDiskTxPos &a, const CExtDiskTxPos &b) {
if (a.nHeight < b.nHeight) return true;
if (a.nHeight > b.nHeight) return false;
return ((const CDiskTxPos)a < (const CDiskTxPos)b);
}
};

CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree);
bool MoneyRange(CAmount nValueOut);
Expand Down Expand Up @@ -451,6 +496,8 @@ class CScriptCheck
bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos);
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos);
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex);
bool ReadTransaction(CTransaction& tx, const CDiskTxPos &pos, uint256 &hashBlock);
bool FindTransactionsByDestination(const CTxDestination &dest, std::set<CExtDiskTxPos> &setpos);


/** Functions for validating blocks and updating the block tree */
Expand Down
2 changes: 1 addition & 1 deletion src/masternodeconfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include "masternodeconfig.h"
#include "util.h"
#include "ui_interface.h"
#include <base58.h>
#include "base58.h"
// clang-format on

CMasternodeConfig masternodeConfig;
Expand Down
4 changes: 4 additions & 0 deletions src/rpcclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ static const CRPCConvertParam vRPCConvertParams[] =
{"signrawtransaction", 1},
{"signrawtransaction", 2},
{"sendrawtransaction", 1},
{ "searchrawtransactions", 1 },
{ "searchrawtransactions", 2 },
{ "searchrawtransactions", 3 },
{ "searchrawtransactions", 4 },
{"gettxout", 1},
{"gettxout", 2},
{"lockunspent", 0},
Expand Down
60 changes: 60 additions & 0 deletions src/rpcrawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,66 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
}
}

Value searchrawtransactions(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 4)
throw runtime_error("searchrawtransactions <address> [verbose=1] [skip=0] [count=100]\n");

if (!fAddrIndex)
throw JSONRPCError(RPC_MISC_ERROR, "Address index not enabled");

CBitcoinAddress address(params[0].get_str());
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");

CTxDestination dest = address.Get();

std::set<CExtDiskTxPos> setpos;
if (!FindTransactionsByDestination(dest, setpos))
throw JSONRPCError(RPC_DATABASE_ERROR, "Cannot search for address");

int nSkip = 0;
int nCount = 100;
bool fVerbose = true;
if (params.size() > 1)
fVerbose = (params[1].get_int() != 0);
if (params.size() > 2)
nSkip = params[2].get_int();
if (params.size() > 3)
nCount = params[3].get_int();

if (nSkip < 0)
nSkip += setpos.size();
if (nSkip < 0)
nSkip = 0;
if (nCount < 0)
nCount = 0;

std::set<CExtDiskTxPos>::const_iterator it = setpos.begin();
while (it != setpos.end() && nSkip--) it++;

Array result;
while (it != setpos.end() && nCount--) {
CTransaction tx;
uint256 hashBlock;
if (!ReadTransaction(tx, *it, hashBlock))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Cannot read transaction from disk");
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << tx;
string strHex = HexStr(ssTx.begin(), ssTx.end());
if (fVerbose) {
Object object;
TxToJSON(tx, hashBlock, object);
object.push_back(Pair("hex", strHex));
result.push_back(object);
} else {
result.push_back(strHex);
}
it++;
}
return result;
}

Value getrawtransaction(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 2)
Expand Down
1 change: 1 addition & 0 deletions src/rpcserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ static const CRPCCommand vRPCCommands[] =
{"rawtransactions", "decoderawtransaction", &decoderawtransaction, true, false, false},
{"rawtransactions", "decodescript", &decodescript, true, false, false},
{"rawtransactions", "getrawtransaction", &getrawtransaction, true, false, false},
{"rawtransactions", "searchrawtransactions", &searchrawtransactions, true, false, false},
{"rawtransactions", "sendrawtransaction", &sendrawtransaction, false, false, false},
{"rawtransactions", "signrawtransaction", &signrawtransaction, false, false, false}, /* uses wallet if enabled */

Expand Down
1 change: 1 addition & 0 deletions src/rpcserver.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ extern json_spirit::Value decoderawtransaction(const json_spirit::Array& params,
extern json_spirit::Value decodescript(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value signrawtransaction(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value sendrawtransaction(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value searchrawtransactions(const json_spirit::Array& params, bool fHelp);

extern json_spirit::Value getblockcount(const json_spirit::Array& params, bool fHelp); // in rpcblockchain.cpp
extern json_spirit::Value getbestblockhash(const json_spirit::Array& params, bool fHelp);
Expand Down
Loading

0 comments on commit 59e531a

Please sign in to comment.