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.
This commit is contained in:
Braydon Fuller
2016-03-05 16:31:10 -05:00
committed by Braydon Fuller
parent 075b416f56
commit 9babc7ff9f
10 changed files with 192 additions and 0 deletions

View File

@@ -350,6 +350,8 @@ std::string HelpMessage(HelpMessageMode mode)
#endif
strUsage += HelpMessageOpt("-txindex", strprintf(_("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)"), DEFAULT_TXINDEX));
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));
strUsage += HelpMessageGroup(_("Connection options:"));
strUsage += HelpMessageOpt("-addnode=<ip>", _("Add a node to connect to and attempt to keep the connection open"));
strUsage += HelpMessageOpt("-banscore=<n>", strprintf(_("Threshold for disconnecting misbehaving peers (default: %u)"), DEFAULT_BANSCORE_THRESHOLD));

View File

@@ -66,6 +66,7 @@ int nScriptCheckThreads = 0;
bool fImporting = false;
bool fReindex = false;
bool fTxIndex = false;
bool fAddressIndex = false;
bool fHavePruned = false;
bool fPruneMode = false;
bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
@@ -1438,6 +1439,17 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
return res;
}
bool GetAddressIndex(uint160 addressHash, int type, std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex)
{
if (!fAddressIndex)
return error("%s: address index not enabled");
if (!pblocktree->ReadAddressIndex(addressHash, type, addressIndex))
return error("%s: unable to get txids for address");
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, const Consensus::Params& consensusParams, uint256 &hashBlock, bool fAllowSlow)
{
@@ -2322,9 +2334,12 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
std::vector<std::pair<uint256, CDiskTxPos> > vPos;
vPos.reserve(block.vtx.size());
blockundo.vtxundo.reserve(block.vtx.size() - 1);
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
for (unsigned int i = 0; i < block.vtx.size(); i++)
{
const CTransaction &tx = block.vtx[i];
const uint256 txhash = tx.GetHash();
nInputs += tx.vin.size();
nSigOps += GetLegacySigOpCount(tx);
@@ -2351,6 +2366,22 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
REJECT_INVALID, "bad-txns-nonfinal");
}
if (fAddressIndex)
{
for (size_t j = 0; j < tx.vin.size(); j++) {
const CTxOut &prevout = view.GetOutputFor(tx.vin[j]);
if (prevout.scriptPubKey.IsPayToScriptHash()) {
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22);
addressIndex.push_back(make_pair(CAddressIndexKey(uint160(hashBytes), 2, txhash, j), prevout.nValue * -1));
} else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) {
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23);
addressIndex.push_back(make_pair(CAddressIndexKey(uint160(hashBytes), 1, txhash, j), prevout.nValue * -1));
} else {
continue;
}
}
}
if (fStrictPayToScriptHash)
{
// Add in sigops done by pay-to-script-hash inputs;
@@ -2372,6 +2403,24 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
control.Add(vChecks);
}
if (fAddressIndex) {
for (unsigned int k = 0; k < tx.vout.size(); k++) {
const CTxOut &out = tx.vout[k];
if (out.scriptPubKey.IsPayToScriptHash()) {
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+2, out.scriptPubKey.begin()+22);
addressIndex.push_back(make_pair(CAddressIndexKey(uint160(hashBytes), 2, txhash, k), out.nValue));
} else if (out.scriptPubKey.IsPayToPublicKeyHash()) {
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23);
addressIndex.push_back(make_pair(CAddressIndexKey(uint160(hashBytes), 1, txhash, k), out.nValue));
} else {
continue;
}
}
}
CTxUndo undoDummy;
if (i > 0) {
blockundo.vtxundo.push_back(CTxUndo());
@@ -2422,6 +2471,10 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
if (!pblocktree->WriteTxIndex(vPos))
return AbortNode(state, "Failed to write transaction index");
if (fAddressIndex)
if (!pblocktree->WriteAddressIndex(addressIndex))
return AbortNode(state, "Failed to write address index");
// add this block to the view's block chain
view.SetBestBlock(pindex->GetBlockHash());
@@ -3813,6 +3866,10 @@ bool static LoadBlockIndexDB()
pblocktree->ReadFlag("txindex", fTxIndex);
LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled");
// Check whether we have an address index
pblocktree->ReadFlag("addressindex", fAddressIndex);
LogPrintf("%s: address index %s\n", __func__, fAddressIndex ? "enabled" : "disabled");
// Load pointer to end of best chain
BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock());
if (it == mapBlockIndex.end())
@@ -3973,6 +4030,11 @@ bool InitBlockIndex(const CChainParams& chainparams)
// Use the provided setting for -txindex in the new database
fTxIndex = GetBoolArg("-txindex", DEFAULT_TXINDEX);
pblocktree->WriteFlag("txindex", fTxIndex);
// Use the provided setting for -addressindex in the new database
fAddressIndex = GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX);
pblocktree->WriteFlag("addressindex", fAddressIndex);
LogPrintf("Initializing databases...\n");
// Only add the genesis block if not reindexing (in which case we reuse the one already on disk)

View File

@@ -111,6 +111,7 @@ static const bool DEFAULT_PERMIT_BAREMULTISIG = true;
static const unsigned int DEFAULT_BYTES_PER_SIGOP = 20;
static const bool DEFAULT_CHECKPOINTS_ENABLED = true;
static const bool DEFAULT_TXINDEX = false;
static const bool DEFAULT_ADDRESSINDEX = false;
static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100;
static const bool DEFAULT_TESTSAFEMODE = false;
@@ -290,6 +291,42 @@ struct CNodeStateStats {
std::vector<int> vHeightInFlight;
};
struct CAddressIndexKey {
uint160 hashBytes;
unsigned int type;
uint256 txhash;
size_t index;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(hashBytes);
READWRITE(type);
READWRITE(txhash);
READWRITE(index);
}
CAddressIndexKey(uint160 addressHash, unsigned int addressType, uint256 txid, size_t txindex) {
hashBytes = addressHash;
type = addressType;
txhash = txid;
index = txindex;
}
CAddressIndexKey() {
SetNull();
}
void SetNull() {
hashBytes.SetNull();
type = 0;
txhash.SetNull();
index = 0;
}
};
struct CDiskTxPos : public CDiskBlockPos
{
unsigned int nTxOffset; // after header
@@ -420,6 +457,7 @@ public:
ScriptError GetScriptError() const { return error; }
};
bool GetAddressIndex(uint160 addressHash, int type, std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex);
/** Functions for disk access for blocks */
bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart);

View File

@@ -396,3 +396,39 @@ UniValue setmocktime(const UniValue& params, bool fHelp)
return NullUniValue;
}
UniValue getaddresstxids(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw runtime_error(
"getaddresstxids\n"
"\nReturns the txids for an address (requires addressindex to be enabled).\n"
"\nResult\n"
"[\n"
" \"transactionid\" (string) The transaction id\n"
" ,...\n"
"]\n"
);
CBitcoinAddress address(params[0].get_str());
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
CKeyID keyID;
address.GetKeyID(keyID);
int type = 1; // TODO
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
LOCK(cs_main);
if (!GetAddressIndex(keyID, type, addressIndex))
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address");
UniValue result(UniValue::VARR);
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++)
result.push_back(it->first.txhash.GetHex());
return result;
}

View File

@@ -313,6 +313,9 @@ static const CRPCCommand vRPCCommands[] =
{ "rawtransactions", "fundrawtransaction", &fundrawtransaction, false },
#endif
/* Address index */
{ "addressindex", "getaddresstxids", &getaddresstxids, false },
/* Utility functions */
{ "util", "createmultisig", &createmultisig, true },
{ "util", "validateaddress", &validateaddress, true }, /* uses wallet if enabled */

View File

@@ -166,6 +166,8 @@ extern std::string HelpExampleRpc(const std::string& methodname, const std::stri
extern void EnsureWalletIsUnlocked();
extern UniValue getconnectioncount(const UniValue& params, bool fHelp); // in rpcnet.cpp
extern UniValue getaddresstxids(const UniValue& params, bool fHelp);
extern UniValue getpeerinfo(const UniValue& params, bool fHelp);
extern UniValue ping(const UniValue& params, bool fHelp);
extern UniValue addnode(const UniValue& params, bool fHelp);

View File

@@ -201,6 +201,17 @@ unsigned int CScript::GetSigOpCount(const CScript& scriptSig) const
return subscript.GetSigOpCount(true);
}
bool CScript::IsPayToPublicKeyHash() const
{
// Extra-fast test for pay-to-pubkey-hash CScripts:
return (this->size() == 25 &&
(*this)[0] == OP_DUP &&
(*this)[1] == OP_HASH160 &&
(*this)[2] == 0x14 &&
(*this)[23] == OP_EQUALVERIFY &&
(*this)[24] == OP_CHECKSIG);
}
bool CScript::IsPayToScriptHash() const
{
// Extra-fast test for pay-to-script-hash CScripts:

View File

@@ -608,6 +608,8 @@ public:
*/
unsigned int GetSigOpCount(const CScript& scriptSig) const;
bool IsPayToPublicKeyHash() const;
bool IsPayToScriptHash() const;
/** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */

View File

@@ -21,6 +21,7 @@ using namespace std;
static const char DB_COINS = 'c';
static const char DB_BLOCK_FILES = 'f';
static const char DB_TXINDEX = 't';
static const char DB_ADDRESSINDEX = 'a';
static const char DB_BLOCK_INDEX = 'b';
static const char DB_BEST_BLOCK = 'B';
@@ -163,6 +164,38 @@ bool CBlockTreeDB::WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos>
return WriteBatch(batch);
}
bool CBlockTreeDB::WriteAddressIndex(const std::vector<std::pair<CAddressIndexKey, CAmount > >&vect) {
CDBBatch batch(&GetObfuscateKey());
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=vect.begin(); it!=vect.end(); it++)
batch.Write(make_pair(DB_ADDRESSINDEX, it->first), it->second);
return WriteBatch(batch);
}
bool CBlockTreeDB::ReadAddressIndex(uint160 addressHash, int type, std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex) {
boost::scoped_ptr<CDBIterator> pcursor(NewIterator());
pcursor->Seek(make_pair(DB_ADDRESSINDEX, addressHash)); //TODO include type
while (pcursor->Valid()) {
boost::this_thread::interruption_point();
std::pair<char,CAddressIndexKey> key;
if (pcursor->GetKey(key) && key.first == DB_ADDRESSINDEX && key.second.hashBytes == addressHash) {
CAmount nValue;
if (pcursor->GetValue(nValue)) {
addressIndex.push_back(make_pair(key.second, nValue));
pcursor->Next();
} else {
return error("failed to get address index value");
}
} 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

@@ -17,6 +17,7 @@
class CBlockFileInfo;
class CBlockIndex;
struct CDiskTxPos;
struct CAddressIndexKey;
class uint256;
//! -dbcache default (MiB)
@@ -57,6 +58,8 @@ public:
bool ReadReindexing(bool &fReindex);
bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos);
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 WriteFlag(const std::string &name, bool fValue);
bool ReadFlag(const std::string &name, bool &fValue);
bool LoadBlockIndexGuts();