Skip to content

Commit 9babc7f

Browse files
braydonfBraydon Fuller
authored andcommitted
main: start of address index
Adds a configuration option for addressindex to search for txids by address. Includes an additional rpc method for getting the txids for an address.
1 parent 075b416 commit 9babc7f

File tree

10 files changed

+192
-0
lines changed

10 files changed

+192
-0
lines changed

src/init.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,8 @@ std::string HelpMessage(HelpMessageMode mode)
350350
#endif
351351
strUsage += HelpMessageOpt("-txindex", strprintf(_("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)"), DEFAULT_TXINDEX));
352352

353+
strUsage += HelpMessageOpt("-addressindex", strprintf(_("Maintain a full address index, used to query for the balance, txids and unspent outputs for addresses (default: %u)"), DEFAULT_ADDRESSINDEX));
354+
353355
strUsage += HelpMessageGroup(_("Connection options:"));
354356
strUsage += HelpMessageOpt("-addnode=<ip>", _("Add a node to connect to and attempt to keep the connection open"));
355357
strUsage += HelpMessageOpt("-banscore=<n>", strprintf(_("Threshold for disconnecting misbehaving peers (default: %u)"), DEFAULT_BANSCORE_THRESHOLD));

src/main.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ int nScriptCheckThreads = 0;
6666
bool fImporting = false;
6767
bool fReindex = false;
6868
bool fTxIndex = false;
69+
bool fAddressIndex = false;
6970
bool fHavePruned = false;
7071
bool fPruneMode = false;
7172
bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
@@ -1438,6 +1439,17 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
14381439
return res;
14391440
}
14401441

1442+
bool GetAddressIndex(uint160 addressHash, int type, std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex)
1443+
{
1444+
if (!fAddressIndex)
1445+
return error("%s: address index not enabled");
1446+
1447+
if (!pblocktree->ReadAddressIndex(addressHash, type, addressIndex))
1448+
return error("%s: unable to get txids for address");
1449+
1450+
return true;
1451+
}
1452+
14411453
/** Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock */
14421454
bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::Params& consensusParams, uint256 &hashBlock, bool fAllowSlow)
14431455
{
@@ -2322,9 +2334,12 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
23222334
std::vector<std::pair<uint256, CDiskTxPos> > vPos;
23232335
vPos.reserve(block.vtx.size());
23242336
blockundo.vtxundo.reserve(block.vtx.size() - 1);
2337+
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
2338+
23252339
for (unsigned int i = 0; i < block.vtx.size(); i++)
23262340
{
23272341
const CTransaction &tx = block.vtx[i];
2342+
const uint256 txhash = tx.GetHash();
23282343

23292344
nInputs += tx.vin.size();
23302345
nSigOps += GetLegacySigOpCount(tx);
@@ -2351,6 +2366,22 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
23512366
REJECT_INVALID, "bad-txns-nonfinal");
23522367
}
23532368

2369+
if (fAddressIndex)
2370+
{
2371+
for (size_t j = 0; j < tx.vin.size(); j++) {
2372+
const CTxOut &prevout = view.GetOutputFor(tx.vin[j]);
2373+
if (prevout.scriptPubKey.IsPayToScriptHash()) {
2374+
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22);
2375+
addressIndex.push_back(make_pair(CAddressIndexKey(uint160(hashBytes), 2, txhash, j), prevout.nValue * -1));
2376+
} else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) {
2377+
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23);
2378+
addressIndex.push_back(make_pair(CAddressIndexKey(uint160(hashBytes), 1, txhash, j), prevout.nValue * -1));
2379+
} else {
2380+
continue;
2381+
}
2382+
}
2383+
}
2384+
23542385
if (fStrictPayToScriptHash)
23552386
{
23562387
// Add in sigops done by pay-to-script-hash inputs;
@@ -2372,6 +2403,24 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
23722403
control.Add(vChecks);
23732404
}
23742405

2406+
if (fAddressIndex) {
2407+
for (unsigned int k = 0; k < tx.vout.size(); k++) {
2408+
const CTxOut &out = tx.vout[k];
2409+
2410+
if (out.scriptPubKey.IsPayToScriptHash()) {
2411+
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+2, out.scriptPubKey.begin()+22);
2412+
addressIndex.push_back(make_pair(CAddressIndexKey(uint160(hashBytes), 2, txhash, k), out.nValue));
2413+
} else if (out.scriptPubKey.IsPayToPublicKeyHash()) {
2414+
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23);
2415+
addressIndex.push_back(make_pair(CAddressIndexKey(uint160(hashBytes), 1, txhash, k), out.nValue));
2416+
} else {
2417+
continue;
2418+
}
2419+
2420+
}
2421+
}
2422+
2423+
23752424
CTxUndo undoDummy;
23762425
if (i > 0) {
23772426
blockundo.vtxundo.push_back(CTxUndo());
@@ -2422,6 +2471,10 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
24222471
if (!pblocktree->WriteTxIndex(vPos))
24232472
return AbortNode(state, "Failed to write transaction index");
24242473

2474+
if (fAddressIndex)
2475+
if (!pblocktree->WriteAddressIndex(addressIndex))
2476+
return AbortNode(state, "Failed to write address index");
2477+
24252478
// add this block to the view's block chain
24262479
view.SetBestBlock(pindex->GetBlockHash());
24272480

@@ -3813,6 +3866,10 @@ bool static LoadBlockIndexDB()
38133866
pblocktree->ReadFlag("txindex", fTxIndex);
38143867
LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled");
38153868

3869+
// Check whether we have an address index
3870+
pblocktree->ReadFlag("addressindex", fAddressIndex);
3871+
LogPrintf("%s: address index %s\n", __func__, fAddressIndex ? "enabled" : "disabled");
3872+
38163873
// Load pointer to end of best chain
38173874
BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock());
38183875
if (it == mapBlockIndex.end())
@@ -3973,6 +4030,11 @@ bool InitBlockIndex(const CChainParams& chainparams)
39734030
// Use the provided setting for -txindex in the new database
39744031
fTxIndex = GetBoolArg("-txindex", DEFAULT_TXINDEX);
39754032
pblocktree->WriteFlag("txindex", fTxIndex);
4033+
4034+
// Use the provided setting for -addressindex in the new database
4035+
fAddressIndex = GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX);
4036+
pblocktree->WriteFlag("addressindex", fAddressIndex);
4037+
39764038
LogPrintf("Initializing databases...\n");
39774039

39784040
// Only add the genesis block if not reindexing (in which case we reuse the one already on disk)

src/main.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ static const bool DEFAULT_PERMIT_BAREMULTISIG = true;
111111
static const unsigned int DEFAULT_BYTES_PER_SIGOP = 20;
112112
static const bool DEFAULT_CHECKPOINTS_ENABLED = true;
113113
static const bool DEFAULT_TXINDEX = false;
114+
static const bool DEFAULT_ADDRESSINDEX = false;
114115
static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100;
115116

116117
static const bool DEFAULT_TESTSAFEMODE = false;
@@ -290,6 +291,42 @@ struct CNodeStateStats {
290291
std::vector<int> vHeightInFlight;
291292
};
292293

294+
struct CAddressIndexKey {
295+
uint160 hashBytes;
296+
unsigned int type;
297+
uint256 txhash;
298+
size_t index;
299+
300+
ADD_SERIALIZE_METHODS;
301+
302+
template <typename Stream, typename Operation>
303+
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
304+
READWRITE(hashBytes);
305+
READWRITE(type);
306+
READWRITE(txhash);
307+
READWRITE(index);
308+
}
309+
310+
CAddressIndexKey(uint160 addressHash, unsigned int addressType, uint256 txid, size_t txindex) {
311+
hashBytes = addressHash;
312+
type = addressType;
313+
txhash = txid;
314+
index = txindex;
315+
}
316+
317+
CAddressIndexKey() {
318+
SetNull();
319+
}
320+
321+
void SetNull() {
322+
hashBytes.SetNull();
323+
type = 0;
324+
txhash.SetNull();
325+
index = 0;
326+
}
327+
328+
};
329+
293330
struct CDiskTxPos : public CDiskBlockPos
294331
{
295332
unsigned int nTxOffset; // after header
@@ -420,6 +457,7 @@ class CScriptCheck
420457
ScriptError GetScriptError() const { return error; }
421458
};
422459

460+
bool GetAddressIndex(uint160 addressHash, int type, std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex);
423461

424462
/** Functions for disk access for blocks */
425463
bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart);

src/rpcmisc.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,3 +396,39 @@ UniValue setmocktime(const UniValue& params, bool fHelp)
396396

397397
return NullUniValue;
398398
}
399+
400+
UniValue getaddresstxids(const UniValue& params, bool fHelp)
401+
{
402+
if (fHelp || params.size() != 1)
403+
throw runtime_error(
404+
"getaddresstxids\n"
405+
"\nReturns the txids for an address (requires addressindex to be enabled).\n"
406+
"\nResult\n"
407+
"[\n"
408+
" \"transactionid\" (string) The transaction id\n"
409+
" ,...\n"
410+
"]\n"
411+
);
412+
413+
CBitcoinAddress address(params[0].get_str());
414+
if (!address.IsValid())
415+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
416+
417+
CKeyID keyID;
418+
address.GetKeyID(keyID);
419+
420+
int type = 1; // TODO
421+
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
422+
423+
LOCK(cs_main);
424+
425+
if (!GetAddressIndex(keyID, type, addressIndex))
426+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address");
427+
428+
UniValue result(UniValue::VARR);
429+
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++)
430+
result.push_back(it->first.txhash.GetHex());
431+
432+
return result;
433+
434+
}

src/rpcserver.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,9 @@ static const CRPCCommand vRPCCommands[] =
313313
{ "rawtransactions", "fundrawtransaction", &fundrawtransaction, false },
314314
#endif
315315

316+
/* Address index */
317+
{ "addressindex", "getaddresstxids", &getaddresstxids, false },
318+
316319
/* Utility functions */
317320
{ "util", "createmultisig", &createmultisig, true },
318321
{ "util", "validateaddress", &validateaddress, true }, /* uses wallet if enabled */

src/rpcserver.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ extern std::string HelpExampleRpc(const std::string& methodname, const std::stri
166166
extern void EnsureWalletIsUnlocked();
167167

168168
extern UniValue getconnectioncount(const UniValue& params, bool fHelp); // in rpcnet.cpp
169+
extern UniValue getaddresstxids(const UniValue& params, bool fHelp);
170+
169171
extern UniValue getpeerinfo(const UniValue& params, bool fHelp);
170172
extern UniValue ping(const UniValue& params, bool fHelp);
171173
extern UniValue addnode(const UniValue& params, bool fHelp);

src/script/script.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,17 @@ unsigned int CScript::GetSigOpCount(const CScript& scriptSig) const
201201
return subscript.GetSigOpCount(true);
202202
}
203203

204+
bool CScript::IsPayToPublicKeyHash() const
205+
{
206+
// Extra-fast test for pay-to-pubkey-hash CScripts:
207+
return (this->size() == 25 &&
208+
(*this)[0] == OP_DUP &&
209+
(*this)[1] == OP_HASH160 &&
210+
(*this)[2] == 0x14 &&
211+
(*this)[23] == OP_EQUALVERIFY &&
212+
(*this)[24] == OP_CHECKSIG);
213+
}
214+
204215
bool CScript::IsPayToScriptHash() const
205216
{
206217
// Extra-fast test for pay-to-script-hash CScripts:

src/script/script.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,8 @@ class CScript : public CScriptBase
608608
*/
609609
unsigned int GetSigOpCount(const CScript& scriptSig) const;
610610

611+
bool IsPayToPublicKeyHash() const;
612+
611613
bool IsPayToScriptHash() const;
612614

613615
/** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */

src/txdb.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ using namespace std;
2121
static const char DB_COINS = 'c';
2222
static const char DB_BLOCK_FILES = 'f';
2323
static const char DB_TXINDEX = 't';
24+
static const char DB_ADDRESSINDEX = 'a';
2425
static const char DB_BLOCK_INDEX = 'b';
2526

2627
static const char DB_BEST_BLOCK = 'B';
@@ -163,6 +164,38 @@ bool CBlockTreeDB::WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos>
163164
return WriteBatch(batch);
164165
}
165166

167+
bool CBlockTreeDB::WriteAddressIndex(const std::vector<std::pair<CAddressIndexKey, CAmount > >&vect) {
168+
CDBBatch batch(&GetObfuscateKey());
169+
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=vect.begin(); it!=vect.end(); it++)
170+
batch.Write(make_pair(DB_ADDRESSINDEX, it->first), it->second);
171+
return WriteBatch(batch);
172+
}
173+
174+
bool CBlockTreeDB::ReadAddressIndex(uint160 addressHash, int type, std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex) {
175+
176+
boost::scoped_ptr<CDBIterator> pcursor(NewIterator());
177+
178+
pcursor->Seek(make_pair(DB_ADDRESSINDEX, addressHash)); //TODO include type
179+
180+
while (pcursor->Valid()) {
181+
boost::this_thread::interruption_point();
182+
std::pair<char,CAddressIndexKey> key;
183+
if (pcursor->GetKey(key) && key.first == DB_ADDRESSINDEX && key.second.hashBytes == addressHash) {
184+
CAmount nValue;
185+
if (pcursor->GetValue(nValue)) {
186+
addressIndex.push_back(make_pair(key.second, nValue));
187+
pcursor->Next();
188+
} else {
189+
return error("failed to get address index value");
190+
}
191+
} else {
192+
break;
193+
}
194+
}
195+
196+
return true;
197+
}
198+
166199
bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) {
167200
return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0');
168201
}

src/txdb.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
class CBlockFileInfo;
1818
class CBlockIndex;
1919
struct CDiskTxPos;
20+
struct CAddressIndexKey;
2021
class uint256;
2122

2223
//! -dbcache default (MiB)
@@ -57,6 +58,8 @@ class CBlockTreeDB : public CDBWrapper
5758
bool ReadReindexing(bool &fReindex);
5859
bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos);
5960
bool WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> > &list);
61+
bool WriteAddressIndex(const std::vector<std::pair<CAddressIndexKey, CAmount> > &vect);
62+
bool ReadAddressIndex(uint160 addressHash, int type, std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex);
6063
bool WriteFlag(const std::string &name, bool fValue);
6164
bool ReadFlag(const std::string &name, bool &fValue);
6265
bool LoadBlockIndexGuts();

0 commit comments

Comments
 (0)