diff --git a/qa/rpc-tests/spentindex.py b/qa/rpc-tests/spentindex.py index 5d0ab58b6..c233ca019 100755 --- a/qa/rpc-tests/spentindex.py +++ b/qa/rpc-tests/spentindex.py @@ -95,13 +95,22 @@ class SpentIndexTest(BitcoinTestFramework): self.nodes[0].importprivkey(privkey) signed_tx2 = self.nodes[0].signrawtransaction(binascii.hexlify(tx2.serialize()).decode("utf-8")) txid2 = self.nodes[0].sendrawtransaction(signed_tx2["hex"], True) + + # Check the mempool index + self.sync_all() + txVerbose3 = self.nodes[1].getrawtransaction(txid2, 1) + assert_equal(txVerbose3["vin"][0]["address"], address2) + assert_equal(txVerbose3["vin"][0]["value"], Decimal(unspent[0]["amount"])) + assert_equal(txVerbose3["vin"][0]["valueSat"], amount) + + # Check the database index self.nodes[0].generate(1) self.sync_all() - txVerbose3 = self.nodes[3].getrawtransaction(txid2, 1) - assert_equal(txVerbose3["vin"][0]["address"], address2) - assert_equal(txVerbose2["vin"][0]["value"], Decimal(unspent[0]["amount"])) - assert_equal(txVerbose2["vin"][0]["valueSat"], amount) + txVerbose4 = self.nodes[3].getrawtransaction(txid2, 1) + assert_equal(txVerbose4["vin"][0]["address"], address2) + assert_equal(txVerbose4["vin"][0]["value"], Decimal(unspent[0]["amount"])) + assert_equal(txVerbose4["vin"][0]["valueSat"], amount) print "Passed\n" diff --git a/src/Makefile.am b/src/Makefile.am index 965dbbfee..dd0260763 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -114,6 +114,7 @@ LIBZCASH_H = \ # bitcoin core # BITCOIN_CORE_H = \ addressindex.h \ + spentindex.h \ addrman.h \ alert.h \ amount.h \ diff --git a/src/main.cpp b/src/main.cpp index 1b3e547a6..66b281c2c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1578,9 +1578,16 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // Store transaction in memory pool.addUnchecked(hash, entry, !IsInitialBlockDownload()); + + // Add memory address index if (fAddressIndex) { pool.addAddressIndex(entry, view); } + + // Add memory spent index + if (fSpentIndex) { + pool.addSpentIndex(entry, view); + } } SyncWithWallets(tx, NULL); @@ -1604,6 +1611,9 @@ bool GetSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value) if (!fSpentIndex) return false; + if (mempool.getSpentIndex(key, value)) + return true; + if (!pblocktree->ReadSpentIndex(key, value)) return error("unable to get spent info"); diff --git a/src/main.h b/src/main.h index 1a3546cfc..fbe6185c4 100644 --- a/src/main.h +++ b/src/main.h @@ -22,6 +22,7 @@ #include "script/script.h" #include "script/sigcache.h" #include "script/standard.h" +#include "spentindex.h" #include "sync.h" #include "tinyformat.h" #include "txmempool.h" @@ -271,81 +272,6 @@ struct CNodeStateStats { std::vector vHeightInFlight; }; -struct CSpentIndexKey { - uint256 txid; - unsigned int outputIndex; - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - READWRITE(txid); - READWRITE(outputIndex); - } - - CSpentIndexKey(uint256 t, unsigned int i) { - txid = t; - outputIndex = i; - } - - CSpentIndexKey() { - SetNull(); - } - - void SetNull() { - txid.SetNull(); - outputIndex = 0; - } - -}; - -struct CSpentIndexValue { - uint256 txid; - unsigned int inputIndex; - int blockHeight; - CAmount satoshis; - int addressType; - uint160 addressHash; - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - READWRITE(txid); - READWRITE(inputIndex); - READWRITE(blockHeight); - READWRITE(satoshis); - READWRITE(addressType); - READWRITE(addressHash); - } - - CSpentIndexValue(uint256 t, unsigned int i, int h, CAmount s, int type, uint160 a) { - txid = t; - inputIndex = i; - blockHeight = h; - satoshis = s; - addressType = type; - addressHash = a; - } - - CSpentIndexValue() { - SetNull(); - } - - void SetNull() { - txid.SetNull(); - inputIndex = 0; - blockHeight = 0; - satoshis = 0; - addressType = 0; - addressHash.SetNull(); - } - - bool IsNull() const { - return txid.IsNull(); - } -}; - struct CTimestampIndexIteratorKey { unsigned int timestamp; diff --git a/src/spentindex.h b/src/spentindex.h new file mode 100644 index 000000000..bd5da45d6 --- /dev/null +++ b/src/spentindex.h @@ -0,0 +1,98 @@ +// 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_SPENTINDEX_H +#define BITCOIN_SPENTINDEX_H + +#include "uint256.h" +#include "amount.h" + +struct CSpentIndexKey { + uint256 txid; + unsigned int outputIndex; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(txid); + READWRITE(outputIndex); + } + + CSpentIndexKey(uint256 t, unsigned int i) { + txid = t; + outputIndex = i; + } + + CSpentIndexKey() { + SetNull(); + } + + void SetNull() { + txid.SetNull(); + outputIndex = 0; + } + +}; + +struct CSpentIndexValue { + uint256 txid; + unsigned int inputIndex; + int blockHeight; + CAmount satoshis; + int addressType; + uint160 addressHash; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(txid); + READWRITE(inputIndex); + READWRITE(blockHeight); + READWRITE(satoshis); + READWRITE(addressType); + READWRITE(addressHash); + } + + CSpentIndexValue(uint256 t, unsigned int i, int h, CAmount s, int type, uint160 a) { + txid = t; + inputIndex = i; + blockHeight = h; + satoshis = s; + addressType = type; + addressHash = a; + } + + CSpentIndexValue() { + SetNull(); + } + + void SetNull() { + txid.SetNull(); + inputIndex = 0; + blockHeight = 0; + satoshis = 0; + addressType = 0; + addressHash.SetNull(); + } + + bool IsNull() const { + return txid.IsNull(); + } +}; + +struct CSpentIndexKeyCompare +{ + bool operator()(const CSpentIndexKey& a, const CSpentIndexKey& b) const { + if (a.txid == b.txid) { + return a.outputIndex < b.outputIndex; + } else { + return a.txid < b.txid; + } + } +}; + +#endif // BITCOIN_SPENTINDEX_H diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 6ff66f0dd..9265ddab5 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -195,6 +195,71 @@ bool CTxMemPool::removeAddressIndex(const uint256 txhash) return true; } +void CTxMemPool::addSpentIndex(const CTxMemPoolEntry &entry, const CCoinsViewCache &view) +{ + LOCK(cs); + + const CTransaction& tx = entry.GetTx(); + std::vector 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); + uint160 addressHash; + int addressType; + + if (prevout.scriptPubKey.IsPayToScriptHash()) { + addressHash = uint160(vector (prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22)); + addressType = 2; + } else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) { + addressHash = uint160(vector (prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23)); + addressType = 1; + } else { + addressHash.SetNull(); + addressType = 0; + } + + CSpentIndexKey key = CSpentIndexKey(input.prevout.hash, input.prevout.n); + CSpentIndexValue value = CSpentIndexValue(txhash, j, -1, prevout.nValue, addressType, addressHash); + + mapSpent.insert(make_pair(key, value)); + inserted.push_back(key); + + } + + mapSpentInserted.insert(make_pair(txhash, inserted)); +} + +bool CTxMemPool::getSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value) +{ + LOCK(cs); + mapSpentIndex::iterator it; + + it = mapSpent.find(key); + if (it != mapSpent.end()) { + value = it->second; + return true; + } + return false; +} + +bool CTxMemPool::removeSpentIndex(const uint256 txhash) +{ + LOCK(cs); + mapSpentIndexInserted::iterator it = mapSpentInserted.find(txhash); + + if (it != mapSpentInserted.end()) { + std::vector keys = (*it).second; + for (std::vector::iterator mit = keys.begin(); mit != keys.end(); mit++) { + mapSpent.erase(*mit); + } + mapSpentInserted.erase(it); + } + + return true; +} + void CTxMemPool::remove(const CTransaction &origTx, std::list& removed, bool fRecursive) { // Remove transaction from memory pool @@ -246,6 +311,7 @@ void CTxMemPool::remove(const CTransaction &origTx, std::list& rem nTransactionsUpdated++; minerPolicyEstimator->removeTx(hash); removeAddressIndex(hash); + removeSpentIndex(hash); } } } diff --git a/src/txmempool.h b/src/txmempool.h index 54b258dc2..e3465b04a 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -9,6 +9,7 @@ #include #include "addressindex.h" +#include "spentindex.h" #include "amount.h" #include "coins.h" #include "primitives/transaction.h" @@ -161,6 +162,12 @@ private: typedef std::map > addressDeltaMapInserted; addressDeltaMapInserted mapAddressInserted; + typedef std::map mapSpentIndex; + mapSpentIndex mapSpent; + + typedef std::map > mapSpentIndexInserted; + mapSpentIndexInserted mapSpentInserted; + public: std::map mapNextTx; std::map > mapDeltas; @@ -184,6 +191,10 @@ public: std::vector > &results); bool removeAddressIndex(const uint256 txhash); + void addSpentIndex(const CTxMemPoolEntry &entry, const CCoinsViewCache &view); + bool getSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value); + bool removeSpentIndex(const uint256 txhash); + void remove(const CTransaction &tx, std::list& removed, bool fRecursive = false); void removeWithAnchor(const uint256 &invalidRoot, ShieldedType type); void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags);