main: mempool address index
Zcash: Integrated into older mempool code
This commit is contained in:
parent
e85d165115
commit
2f8d20ba76
|
@ -7,6 +7,7 @@
|
||||||
# Test addressindex generation and fetching
|
# Test addressindex generation and fetching
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import time
|
||||||
from test_framework.test_framework import BitcoinTestFramework
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
from test_framework.util import *
|
from test_framework.util import *
|
||||||
from test_framework.script 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(0, self.options.tmpdir, ["-debug"]))
|
||||||
self.nodes.append(start_node(1, self.options.tmpdir, ["-debug", "-addressindex"]))
|
self.nodes.append(start_node(1, self.options.tmpdir, ["-debug", "-addressindex"]))
|
||||||
# Nodes 2/3 are used for testing
|
# 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"]))
|
self.nodes.append(start_node(3, self.options.tmpdir, ["-debug", "-addressindex"]))
|
||||||
connect_nodes(self.nodes[0], 1)
|
connect_nodes(self.nodes[0], 1)
|
||||||
connect_nodes(self.nodes[0], 2)
|
connect_nodes(self.nodes[0], 2)
|
||||||
|
@ -207,6 +208,43 @@ class AddressIndexTest(BitcoinTestFramework):
|
||||||
assert_equal(utxos3[1]["height"], 264)
|
assert_equal(utxos3[1]["height"], 264)
|
||||||
assert_equal(utxos3[2]["height"], 265)
|
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"
|
print "Passed\n"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -1577,6 +1577,9 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
||||||
|
|
||||||
// Store transaction in memory
|
// Store transaction in memory
|
||||||
pool.addUnchecked(hash, entry, !IsInitialBlockDownload());
|
pool.addUnchecked(hash, entry, !IsInitialBlockDownload());
|
||||||
|
if (fAddressIndex) {
|
||||||
|
pool.addAddressIndex(entry, view);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SyncWithWallets(tx, NULL);
|
SyncWithWallets(tx, NULL);
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "netbase.h"
|
#include "netbase.h"
|
||||||
#include "rpc/server.h"
|
#include "rpc/server.h"
|
||||||
#include "timedata.h"
|
#include "timedata.h"
|
||||||
|
#include "txmempool.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#ifdef ENABLE_WALLET
|
#ifdef ENABLE_WALLET
|
||||||
#include "wallet/wallet.h"
|
#include "wallet/wallet.h"
|
||||||
|
@ -497,6 +498,7 @@ static const CRPCCommand commands[] =
|
||||||
{ "addressindex", "getaddressbalance", &getaddressbalance, false }, /* insight explorer */
|
{ "addressindex", "getaddressbalance", &getaddressbalance, false }, /* insight explorer */
|
||||||
{ "addressindex", "getaddressdeltas", &getaddressdeltas, false }, /* insight explorer */
|
{ "addressindex", "getaddressdeltas", &getaddressdeltas, false }, /* insight explorer */
|
||||||
{ "addressindex", "getaddressutxos", &getaddressutxos, false }, /* insight explorer */
|
{ "addressindex", "getaddressutxos", &getaddressutxos, false }, /* insight explorer */
|
||||||
|
{ "addressindex", "getaddressmempool", &getaddressmempool, false }, /* insight explorer */
|
||||||
|
|
||||||
/* Not shown in help */
|
/* Not shown in help */
|
||||||
{ "hidden", "setmocktime", &setmocktime, true },
|
{ "hidden", "setmocktime", &setmocktime, true },
|
||||||
|
@ -549,6 +551,51 @@ bool heightSort(std::pair<CAddressUnspentKey, CAddressUnspentValue> a,
|
||||||
return a.second.blockHeight < b.second.blockHeight;
|
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)
|
UniValue getaddressutxos(const UniValue& params, bool fHelp)
|
||||||
{
|
{
|
||||||
if (fHelp || params.size() != 1)
|
if (fHelp || params.size() != 1)
|
||||||
|
|
|
@ -173,6 +173,7 @@ extern std::string HelpExampleRpc(const std::string& methodname, const std::stri
|
||||||
extern void EnsureWalletIsUnlocked();
|
extern void EnsureWalletIsUnlocked();
|
||||||
|
|
||||||
extern UniValue getconnectioncount(const UniValue& params, bool fHelp); // in rpcnet.cpp
|
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 getaddressutxos(const UniValue& params, bool fHelp);
|
||||||
extern UniValue getaddressdeltas(const UniValue& params, bool fHelp);
|
extern UniValue getaddressdeltas(const UniValue& params, bool fHelp);
|
||||||
extern UniValue getaddresstxids(const UniValue& params, bool fHelp);
|
extern UniValue getaddresstxids(const UniValue& params, bool fHelp);
|
||||||
|
|
|
@ -121,6 +121,79 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
|
||||||
return true;
|
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::remove(const CTransaction &origTx, std::list<CTransaction>& removed, bool fRecursive)
|
void CTxMemPool::remove(const CTransaction &origTx, std::list<CTransaction>& removed, bool fRecursive)
|
||||||
{
|
{
|
||||||
|
@ -172,6 +245,7 @@ void CTxMemPool::remove(const CTransaction &origTx, std::list<CTransaction>& rem
|
||||||
mapTx.erase(hash);
|
mapTx.erase(hash);
|
||||||
nTransactionsUpdated++;
|
nTransactionsUpdated++;
|
||||||
minerPolicyEstimator->removeTx(hash);
|
minerPolicyEstimator->removeTx(hash);
|
||||||
|
removeAddressIndex(hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
|
#include "addressindex.h"
|
||||||
#include "amount.h"
|
#include "amount.h"
|
||||||
#include "coins.h"
|
#include "coins.h"
|
||||||
#include "primitives/transaction.h"
|
#include "primitives/transaction.h"
|
||||||
|
@ -152,6 +153,15 @@ public:
|
||||||
|
|
||||||
mutable CCriticalSection cs;
|
mutable CCriticalSection cs;
|
||||||
indexed_transaction_set mapTx;
|
indexed_transaction_set mapTx;
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef std::map<CMempoolAddressDeltaKey, CMempoolAddressDelta, CMempoolAddressDeltaKeyCompare> addressDeltaMap;
|
||||||
|
addressDeltaMap mapAddress;
|
||||||
|
|
||||||
|
typedef std::map<uint256, std::vector<CMempoolAddressDeltaKey> > addressDeltaMapInserted;
|
||||||
|
addressDeltaMapInserted mapAddressInserted;
|
||||||
|
|
||||||
|
public:
|
||||||
std::map<COutPoint, CInPoint> mapNextTx;
|
std::map<COutPoint, CInPoint> mapNextTx;
|
||||||
std::map<uint256, std::pair<double, CAmount> > mapDeltas;
|
std::map<uint256, std::pair<double, CAmount> > mapDeltas;
|
||||||
|
|
||||||
|
@ -168,6 +178,12 @@ public:
|
||||||
void setSanityCheck(double dFrequency = 1.0) { nCheckFrequency = static_cast<uint32_t>(dFrequency * 4294967295.0); }
|
void setSanityCheck(double dFrequency = 1.0) { nCheckFrequency = static_cast<uint32_t>(dFrequency * 4294967295.0); }
|
||||||
|
|
||||||
bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool fCurrentEstimate = true);
|
bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, 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 remove(const CTransaction &tx, std::list<CTransaction>& removed, bool fRecursive = false);
|
||||||
void removeWithAnchor(const uint256 &invalidRoot, ShieldedType type);
|
void removeWithAnchor(const uint256 &invalidRoot, ShieldedType type);
|
||||||
void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags);
|
void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags);
|
||||||
|
|
Loading…
Reference in New Issue