main: mempool address index

This commit is contained in:
Braydon Fuller 2016-04-04 16:37:43 -04:00 committed by Braydon Fuller
parent 1bd65a5c4b
commit b66eff47cf
8 changed files with 254 additions and 1 deletions

View File

@ -7,6 +7,7 @@
# Test addressindex generation and fetching
#
import time
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
from test_framework.script import *
@ -25,7 +26,7 @@ class AddressIndexTest(BitcoinTestFramework):
self.nodes.append(start_node(0, self.options.tmpdir, ["-debug"]))
self.nodes.append(start_node(1, self.options.tmpdir, ["-debug", "-addressindex"]))
# Nodes 2/3 are used for testing
self.nodes.append(start_node(2, self.options.tmpdir, ["-debug"]))
self.nodes.append(start_node(2, self.options.tmpdir, ["-debug", "-addressindex"]))
self.nodes.append(start_node(3, self.options.tmpdir, ["-debug", "-addressindex"]))
connect_nodes(self.nodes[0], 1)
connect_nodes(self.nodes[0], 2)
@ -207,6 +208,43 @@ class AddressIndexTest(BitcoinTestFramework):
assert_equal(utxos3[1]["height"], 264)
assert_equal(utxos3[2]["height"], 265)
# Check mempool indexing
print "Testing mempool indexing..."
privKey3 = "cVfUn53hAbRrDEuMexyfgDpZPhF7KqXpS8UZevsyTDaugB7HZ3CD"
address3 = "mw4ynwhS7MmrQ27hr82kgqu7zryNDK26JB"
addressHash3 = "aa9872b5bbcdb511d89e0e11aa27da73fd2c3f50".decode("hex")
scriptPubKey3 = CScript([OP_DUP, OP_HASH160, addressHash3, OP_EQUALVERIFY, OP_CHECKSIG])
unspent = self.nodes[2].listunspent()
tx = CTransaction()
tx.vin = [CTxIn(COutPoint(int(unspent[0]["txid"], 16), unspent[0]["vout"]))]
amount = unspent[0]["amount"] * 100000000
tx.vout = [CTxOut(amount, scriptPubKey3)]
tx.rehash()
signed_tx = self.nodes[2].signrawtransaction(binascii.hexlify(tx.serialize()).decode("utf-8"))
memtxid1 = self.nodes[2].sendrawtransaction(signed_tx["hex"], True)
time.sleep(2)
tx2 = CTransaction()
tx2.vin = [CTxIn(COutPoint(int(unspent[1]["txid"], 16), unspent[1]["vout"]))]
amount = unspent[1]["amount"] * 100000000
tx2.vout = [CTxOut(amount, scriptPubKey3)]
tx2.rehash()
signed_tx2 = self.nodes[2].signrawtransaction(binascii.hexlify(tx2.serialize()).decode("utf-8"))
memtxid2 = self.nodes[2].sendrawtransaction(signed_tx2["hex"], True)
time.sleep(2)
mempool = self.nodes[2].getaddressmempool({"addresses": [address3]})
assert_equal(len(mempool), 2)
assert_equal(mempool[0]["txid"], memtxid1)
assert_equal(mempool[1]["txid"], memtxid2)
self.nodes[2].generate(1);
self.sync_all();
mempool2 = self.nodes[2].getaddressmempool({"addresses": [address3]})
assert_equal(len(mempool2), 0)
print "Passed\n"

77
src/addressindex.h Normal file
View File

@ -0,0 +1,77 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-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.
#ifndef BITCOIN_ADDRESSINDEX_H
#define BITCOIN_ADDRESSINDEX_H
#include "uint256.h"
#include "amount.h"
struct CMempoolAddressDelta
{
int64_t time;
CAmount amount;
uint256 prevhash;
unsigned int prevout;
CMempoolAddressDelta(int64_t t, CAmount a, uint256 hash, unsigned int out) {
time = t;
amount = a;
prevhash = hash;
prevout = out;
}
CMempoolAddressDelta(int64_t t, CAmount a) {
time = t;
amount = a;
prevhash.SetNull();
prevout = 0;
}
};
struct CMempoolAddressDeltaKey
{
int type;
uint160 addressBytes;
uint256 txhash;
unsigned int index;
bool spending;
CMempoolAddressDeltaKey(int addressType, uint160 addressHash, uint256 hash, unsigned int i, bool s) {
type = addressType;
addressBytes = addressHash;
txhash = hash;
index = i;
spending = s;
}
CMempoolAddressDeltaKey(int addressType, uint160 addressHash) {
type = addressType;
addressBytes = addressHash;
txhash.SetNull();
index = 0;
}
};
struct CMempoolAddressDeltaKeyCompare
{
bool operator()(const CMempoolAddressDeltaKey& a, const CMempoolAddressDeltaKey& b) {
if (a.type == b.type) {
if (a.addressBytes == b.addressBytes) {
if (a.txhash == b.txhash) {
return a.index < b.index;
} else {
return a.txhash < b.txhash;
}
} else {
return a.addressBytes < b.addressBytes;
}
} else {
return a.type < b.type;
}
}
};
#endif // BITCOIN_ADDRESSINDEX_H

View File

@ -1414,6 +1414,9 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C
// Store transaction in memory
pool.addUnchecked(hash, entry, setAncestors, !IsInitialBlockDownload());
if (fAddressIndex) {
pool.addAddressIndex(entry, view);
}
// trim mempool and check if tx was trimmed
if (!fOverrideMempoolLimit) {

View File

@ -11,6 +11,7 @@
#include "netbase.h"
#include "rpcserver.h"
#include "timedata.h"
#include "txmempool.h"
#include "util.h"
#include "utilstrencodings.h"
#ifdef ENABLE_WALLET
@ -438,6 +439,51 @@ bool heightSort(std::pair<CAddressUnspentKey, CAddressUnspentValue> a,
return a.second.blockHeight < b.second.blockHeight;
}
bool timestampSort(std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta> a,
std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta> b) {
return a.second.time < b.second.time;
}
UniValue getaddressmempool(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw runtime_error(
"getaddressmempool\n"
"\nReturns all mempool deltas for an address (requires addressindex to be enabled).\n"
);
std::vector<std::pair<uint160, int> > addresses;
if (!getAddressesFromParams(params, addresses)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
}
std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta> > indexes;
if (!mempool.getAddressIndex(addresses, indexes)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address");
}
std::sort(indexes.begin(), indexes.end(), timestampSort);
UniValue result(UniValue::VARR);
for (std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta> >::iterator it = indexes.begin();
it != indexes.end(); it++) {
UniValue delta(UniValue::VOBJ);
delta.push_back(Pair("addressType", (int)it->first.type));
delta.push_back(Pair("addressHash", it->first.addressBytes.GetHex()));
delta.push_back(Pair("txid", it->first.txhash.GetHex()));
delta.push_back(Pair("index", (int)it->first.index));
delta.push_back(Pair("satoshis", it->second.amount));
delta.push_back(Pair("timestamp", it->second.time));
result.push_back(delta);
}
return result;
}
UniValue getaddressutxos(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 1)

View File

@ -315,6 +315,7 @@ static const CRPCCommand vRPCCommands[] =
#endif
/* Address index */
{ "addressindex", "getaddressmempool", &getaddressmempool, false },
{ "addressindex", "getaddressutxos", &getaddressutxos, false },
{ "addressindex", "getaddressdeltas", &getaddressdeltas, false },
{ "addressindex", "getaddresstxids", &getaddresstxids, false },

View File

@ -166,6 +166,7 @@ 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 getaddressmempool(const UniValue& params, bool fHelp);
extern UniValue getaddressutxos(const UniValue& params, bool fHelp);
extern UniValue getaddressdeltas(const UniValue& params, bool fHelp);
extern UniValue getaddresstxids(const UniValue& params, bool fHelp);

View File

@ -422,6 +422,80 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
return true;
}
void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewCache &view)
{
LOCK(cs);
const CTransaction& tx = entry.GetTx();
std::vector<CMempoolAddressDeltaKey> inserted;
uint256 txhash = tx.GetHash();
for (unsigned int j = 0; j < tx.vin.size(); j++) {
const CTxIn input = tx.vin[j];
const CTxOut &prevout = view.GetOutputFor(input);
if (prevout.scriptPubKey.IsPayToScriptHash()) {
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22);
CMempoolAddressDeltaKey key(2, uint160(hashBytes), txhash, j, true);
CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n);
mapAddress.insert(make_pair(key, delta));
inserted.push_back(key);
} else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) {
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23);
CMempoolAddressDeltaKey key(1, uint160(hashBytes), txhash, j, true);
CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n);
mapAddress.insert(make_pair(key, delta));
inserted.push_back(key);
}
}
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);
CMempoolAddressDeltaKey key(2, uint160(hashBytes), txhash, k, false);
mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue)));
inserted.push_back(key);
} else if (out.scriptPubKey.IsPayToPublicKeyHash()) {
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23);
std::pair<addressDeltaMap::iterator,bool> ret;
CMempoolAddressDeltaKey key(1, uint160(hashBytes), txhash, k, false);
mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue)));
inserted.push_back(key);
}
}
mapAddressInserted.insert(make_pair(txhash, inserted));
}
bool CTxMemPool::getAddressIndex(std::vector<std::pair<uint160, int> > &addresses,
std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta> > &results)
{
LOCK(cs);
for (std::vector<std::pair<uint160, int> >::iterator it = addresses.begin(); it != addresses.end(); it++) {
addressDeltaMap::iterator ait = mapAddress.lower_bound(CMempoolAddressDeltaKey((*it).second, (*it).first));
while (ait != mapAddress.end() && (*ait).first.addressBytes == (*it).first) {
results.push_back(*ait);
ait++;
}
}
return true;
}
bool CTxMemPool::removeAddressIndex(const uint256 txhash)
{
LOCK(cs);
addressDeltaMapInserted::iterator it = mapAddressInserted.find(txhash);
if (it != mapAddressInserted.end()) {
std::vector<CMempoolAddressDeltaKey> keys = (*it).second;
for (std::vector<CMempoolAddressDeltaKey>::iterator mit = keys.begin(); mit != keys.end(); mit++) {
mapAddress.erase(*mit);
}
mapAddressInserted.erase(it);
}
return true;
}
void CTxMemPool::removeUnchecked(txiter it)
{
const uint256 hash = it->GetTx().GetHash();
@ -435,6 +509,7 @@ void CTxMemPool::removeUnchecked(txiter it)
mapTx.erase(it);
nTransactionsUpdated++;
minerPolicyEstimator->removeTx(hash);
removeAddressIndex(hash);
}
// Calculates descendants of entry that are not already in setDescendants, and adds to

View File

@ -9,6 +9,7 @@
#include <list>
#include <set>
#include "addressindex.h"
#include "amount.h"
#include "coins.h"
#include "primitives/transaction.h"
@ -419,6 +420,12 @@ private:
typedef std::map<txiter, TxLinks, CompareIteratorByHash> txlinksMap;
txlinksMap mapLinks;
typedef std::map<CMempoolAddressDeltaKey, CMempoolAddressDelta, CMempoolAddressDeltaKeyCompare> addressDeltaMap;
addressDeltaMap mapAddress;
typedef std::map<uint256, std::vector<CMempoolAddressDeltaKey> > addressDeltaMapInserted;
addressDeltaMapInserted mapAddressInserted;
void UpdateParent(txiter entry, txiter parent, bool add);
void UpdateChild(txiter entry, txiter child, bool add);
@ -450,6 +457,11 @@ public:
bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool fCurrentEstimate = true);
bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool fCurrentEstimate = true);
void addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewCache &view);
bool getAddressIndex(std::vector<std::pair<uint160, int> > &addresses,
std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta> > &results);
bool removeAddressIndex(const uint256 txhash);
void remove(const CTransaction &tx, std::list<CTransaction>& removed, bool fRecursive = false);
void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags);
void removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed);