Skip to content

Commit

Permalink
Port of addrindex patch jmcorgan@4790f3c
Browse files Browse the repository at this point in the history
  • Loading branch information
reorder authored and btcdrak committed May 19, 2015
1 parent 16f4560 commit d48d9c0
Show file tree
Hide file tree
Showing 9 changed files with 265 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 @@ -991,7 +991,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", false))
if (nBlockTreeDBCache > (1 << 21) && !GetBoolArg("-txindex", false) && !GetBoolArg("-addrindex", false))
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 @@ -1044,6 +1044,12 @@ bool AppInit2(boost::thread_group& threadGroup)
break;
}

// Check for changed -addrindex state
if (fAddrIndex != GetBoolArg("-addrindex", false)) {
strLoadError = _("You need to rebuild the database using -reindex to change -addrindex");
break;
}

uiInterface.InitMessage(_("Verifying blocks..."));
if (!CVerifyDB().VerifyDB(pcoinsdbview, GetArg("-checklevel", 3),
GetArg("-checkblocks", 288))) {
Expand Down
104 changes: 98 additions & 6 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "addrman.h"
#include "alert.h"
#include "base58.h"
#include "chainparams.h"
#include "checkpoints.h"
#include "checkqueue.h"
Expand Down Expand Up @@ -50,6 +51,7 @@ int nScriptCheckThreads = 0;
bool fImporting = false;
bool fReindex = false;
bool fTxIndex = false;
bool fAddrIndex = false;
bool fIsBareMultisigStd = true;
bool fCheckBlockIndex = false;
unsigned int nCoinCacheSize = 5000;
Expand Down Expand Up @@ -1106,6 +1108,43 @@ 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 = 0;
const CKeyID *pkeyid = boost::get<CKeyID>(&dest);
if (pkeyid)
addrid = static_cast<uint160>(*pkeyid);
if (!addrid) {
const CScriptID *pscriptid = boost::get<CScriptID>(&dest);
if (pscriptid)
addrid = static_cast<uint160>(*pscriptid);
}
if (!addrid)
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;
}

/** Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock */
bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow)
{
Expand All @@ -1122,6 +1161,8 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock
if (fTxIndex) {
CDiskTxPos postx;
if (pblocktree->ReadTxIndex(hash, postx)) {
if (!ReadTransaction(txOut, postx, hashBlock))
return false;
CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
if (file.IsNull())
return error("%s: OpenBlockFile failed", __func__);
Expand Down Expand Up @@ -1648,6 +1689,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 = 0;
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)
{
AssertLockHeld(cs_main);
Expand Down Expand Up @@ -1711,9 +1779,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(4*block.vtx.size());
blockundo.vtxundo.reserve(block.vtx.size() - 1);
for (unsigned int i = 0; i < block.vtx.size(); i++)
{
Expand Down Expand Up @@ -1750,13 +1822,26 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
control.Add(vChecks);
}

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);
BuildAddrIndex(coins.vout[txin.prevout.n].scriptPubKey, pos, vPosAddrid);
}
}
BOOST_FOREACH(const CTxOut &txout, tx.vout)
BuildAddrIndex(txout.scriptPubKey, pos, vPosAddrid);
}

CTxUndo undoDummy;
if (i > 0) {
blockundo.vtxundo.push_back(CTxUndo());
}
UpdateCoins(tx, state, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight);

vPos.push_back(std::make_pair(tx.GetHash(), pos));
pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
}
int64_t nTime1 = GetTimeMicros(); nTimeConnect += nTime1 - nTimeStart;
Expand Down Expand Up @@ -1796,9 +1881,11 @@ 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 @@ -2956,6 +3043,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");

// Load pointer to end of best chain
BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock());
if (it == mapBlockIndex.end())
Expand Down Expand Up @@ -3086,6 +3176,8 @@ bool InitBlockIndex() {
// Use the provided setting for -txindex in the new database
fTxIndex = GetBoolArg("-txindex", false);
pblocktree->WriteFlag("txindex", fTxIndex);
fAddrIndex = GetBoolArg("-addrindex", false);
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
48 changes: 47 additions & 1 deletion src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,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 @@ -246,8 +247,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);

Expand Down Expand Up @@ -365,7 +410,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
1 change: 1 addition & 0 deletions src/rpcclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "createrawtransaction", 1 },
{ "signrawtransaction", 1 },
{ "signrawtransaction", 2 },
{ "searchrawtransaction", 2 },

This comment has been minimized.

Copy link
@carnesen

carnesen Jul 7, 2015

Everywhere else I see "searchrawtransactions" with an s at the end. Is that what it's supposed to be here?

This comment has been minimized.

Copy link
@btcdrak

btcdrak Jul 8, 2015

Owner

Yes, it's fixed in the upcoming 0.11 branch 4a87fd1

{ "sendrawtransaction", 1 },
{ "gettxout", 1 },
{ "gettxout", 2 },
Expand Down
59 changes: 59 additions & 0 deletions src/rpcrawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,65 @@ 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 Bitcoin 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 @@ -291,6 +291,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 @@ -201,6 +201,7 @@ extern json_spirit::Value getnetworkinfo(const json_spirit::Array& params, bool
extern json_spirit::Value setmocktime(const json_spirit::Array& params, bool fHelp);

extern json_spirit::Value getrawtransaction(const json_spirit::Array& params, bool fHelp); // in rcprawtransaction.cpp
extern json_spirit::Value searchrawtransactions(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value listunspent(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value lockunspent(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value listlockunspent(const json_spirit::Array& params, bool fHelp);
Expand Down
Loading

0 comments on commit d48d9c0

Please sign in to comment.