changeset 29649:139d221a297c

main: mempool address index
author Braydon Fuller <courier@braydon.com>
date Mon, 04 Apr 2016 16:37:43 -0400
parents 05a47a3a8daa
children aa4a517420b8
files qa/rpc-tests/addressindex.py src/addressindex.h src/main.cpp src/rpcmisc.cpp src/rpcserver.cpp src/rpcserver.h src/txmempool.cpp src/txmempool.h
diffstat 8 files changed, 254 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/qa/rpc-tests/addressindex.py	Wed Mar 30 16:42:37 2016 -0400
+++ b/qa/rpc-tests/addressindex.py	Mon Apr 04 16:37:43 2016 -0400
@@ -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 @@
         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 @@
         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"
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/addressindex.h	Mon Apr 04 16:37:43 2016 -0400
@@ -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
--- a/src/main.cpp	Wed Mar 30 16:42:37 2016 -0400
+++ b/src/main.cpp	Mon Apr 04 16:37:43 2016 -0400
@@ -1414,6 +1414,9 @@
 
         // 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) {
--- a/src/rpcmisc.cpp	Wed Mar 30 16:42:37 2016 -0400
+++ b/src/rpcmisc.cpp	Mon Apr 04 16:37:43 2016 -0400
@@ -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 @@
     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)
--- a/src/rpcserver.cpp	Wed Mar 30 16:42:37 2016 -0400
+++ b/src/rpcserver.cpp	Mon Apr 04 16:37:43 2016 -0400
@@ -315,6 +315,7 @@
 #endif
 
     /* Address index */
+    { "addressindex",       "getaddressmempool",      &getaddressmempool,      false },
     { "addressindex",       "getaddressutxos",        &getaddressutxos,        false },
     { "addressindex",       "getaddressdeltas",       &getaddressdeltas,       false },
     { "addressindex",       "getaddresstxids",        &getaddresstxids,        false },
--- a/src/rpcserver.h	Wed Mar 30 16:42:37 2016 -0400
+++ b/src/rpcserver.h	Mon Apr 04 16:37:43 2016 -0400
@@ -166,6 +166,7 @@
 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);
--- a/src/txmempool.cpp	Wed Mar 30 16:42:37 2016 -0400
+++ b/src/txmempool.cpp	Mon Apr 04 16:37:43 2016 -0400
@@ -422,6 +422,80 @@
     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 @@
     mapTx.erase(it);
     nTransactionsUpdated++;
     minerPolicyEstimator->removeTx(hash);
+    removeAddressIndex(hash);
 }
 
 // Calculates descendants of entry that are not already in setDescendants, and adds to
--- a/src/txmempool.h	Wed Mar 30 16:42:37 2016 -0400
+++ b/src/txmempool.h	Mon Apr 04 16:37:43 2016 -0400
@@ -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 @@
     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 @@
     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);