Auto merge of #3837 - LarryRuane:3708-addressindex, r=bitcartel
add AddressIndex changes needed for bitcore block explorer Addresses #3708, note there are no tests yet (will come later with RPC interfaces).
This commit is contained in:
commit
e73326eed0
|
@ -86,6 +86,7 @@ BITCOIN_TESTS =\
|
||||||
test/sanity_tests.cpp \
|
test/sanity_tests.cpp \
|
||||||
test/scheduler_tests.cpp \
|
test/scheduler_tests.cpp \
|
||||||
test/script_P2SH_tests.cpp \
|
test/script_P2SH_tests.cpp \
|
||||||
|
test/script_P2PKH_tests.cpp \
|
||||||
test/script_tests.cpp \
|
test/script_tests.cpp \
|
||||||
test/scriptnum_tests.cpp \
|
test/scriptnum_tests.cpp \
|
||||||
test/serialize_tests.cpp \
|
test/serialize_tests.cpp \
|
||||||
|
|
|
@ -0,0 +1,225 @@
|
||||||
|
// 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"
|
||||||
|
#include "script/script.h"
|
||||||
|
|
||||||
|
struct CAddressUnspentKey {
|
||||||
|
unsigned int type;
|
||||||
|
uint160 hashBytes;
|
||||||
|
uint256 txhash;
|
||||||
|
size_t index;
|
||||||
|
|
||||||
|
size_t GetSerializeSize(int nType, int nVersion) const {
|
||||||
|
return 57;
|
||||||
|
}
|
||||||
|
template<typename Stream>
|
||||||
|
void Serialize(Stream& s) const {
|
||||||
|
ser_writedata8(s, type);
|
||||||
|
hashBytes.Serialize(s);
|
||||||
|
txhash.Serialize(s);
|
||||||
|
ser_writedata32(s, index);
|
||||||
|
}
|
||||||
|
template<typename Stream>
|
||||||
|
void Unserialize(Stream& s) {
|
||||||
|
type = ser_readdata8(s);
|
||||||
|
hashBytes.Unserialize(s);
|
||||||
|
txhash.Unserialize(s);
|
||||||
|
index = ser_readdata32(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
CAddressUnspentKey(unsigned int addressType, uint160 addressHash, uint256 txid, size_t indexValue) {
|
||||||
|
type = addressType;
|
||||||
|
hashBytes = addressHash;
|
||||||
|
txhash = txid;
|
||||||
|
index = indexValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CAddressUnspentKey() {
|
||||||
|
SetNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetNull() {
|
||||||
|
type = 0;
|
||||||
|
hashBytes.SetNull();
|
||||||
|
txhash.SetNull();
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CAddressUnspentValue {
|
||||||
|
CAmount satoshis;
|
||||||
|
CScript script;
|
||||||
|
int blockHeight;
|
||||||
|
|
||||||
|
ADD_SERIALIZE_METHODS;
|
||||||
|
|
||||||
|
template <typename Stream, typename Operation>
|
||||||
|
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||||
|
READWRITE(satoshis);
|
||||||
|
READWRITE(*(CScriptBase*)(&script));
|
||||||
|
READWRITE(blockHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
CAddressUnspentValue(CAmount sats, CScript scriptPubKey, int height) {
|
||||||
|
satoshis = sats;
|
||||||
|
script = scriptPubKey;
|
||||||
|
blockHeight = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
CAddressUnspentValue() {
|
||||||
|
SetNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetNull() {
|
||||||
|
satoshis = -1;
|
||||||
|
script.clear();
|
||||||
|
blockHeight = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsNull() const {
|
||||||
|
return (satoshis == -1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CAddressIndexKey {
|
||||||
|
unsigned int type;
|
||||||
|
uint160 hashBytes;
|
||||||
|
int blockHeight;
|
||||||
|
unsigned int txindex;
|
||||||
|
uint256 txhash;
|
||||||
|
size_t index;
|
||||||
|
bool spending;
|
||||||
|
|
||||||
|
size_t GetSerializeSize(int nType, int nVersion) const {
|
||||||
|
return 66;
|
||||||
|
}
|
||||||
|
template<typename Stream>
|
||||||
|
void Serialize(Stream& s) const {
|
||||||
|
ser_writedata8(s, type);
|
||||||
|
hashBytes.Serialize(s);
|
||||||
|
// Heights are stored big-endian for key sorting in LevelDB
|
||||||
|
ser_writedata32be(s, blockHeight);
|
||||||
|
ser_writedata32be(s, txindex);
|
||||||
|
txhash.Serialize(s);
|
||||||
|
ser_writedata32(s, index);
|
||||||
|
char f = spending;
|
||||||
|
ser_writedata8(s, f);
|
||||||
|
}
|
||||||
|
template<typename Stream>
|
||||||
|
void Unserialize(Stream& s) {
|
||||||
|
type = ser_readdata8(s);
|
||||||
|
hashBytes.Unserialize(s);
|
||||||
|
blockHeight = ser_readdata32be(s);
|
||||||
|
txindex = ser_readdata32be(s);
|
||||||
|
txhash.Unserialize(s);
|
||||||
|
index = ser_readdata32(s);
|
||||||
|
char f = ser_readdata8(s);
|
||||||
|
spending = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
CAddressIndexKey(unsigned int addressType, uint160 addressHash, int height, int blockindex,
|
||||||
|
uint256 txid, size_t indexValue, bool isSpending) {
|
||||||
|
type = addressType;
|
||||||
|
hashBytes = addressHash;
|
||||||
|
blockHeight = height;
|
||||||
|
txindex = blockindex;
|
||||||
|
txhash = txid;
|
||||||
|
index = indexValue;
|
||||||
|
spending = isSpending;
|
||||||
|
}
|
||||||
|
|
||||||
|
CAddressIndexKey() {
|
||||||
|
SetNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetNull() {
|
||||||
|
type = 0;
|
||||||
|
hashBytes.SetNull();
|
||||||
|
blockHeight = 0;
|
||||||
|
txindex = 0;
|
||||||
|
txhash.SetNull();
|
||||||
|
index = 0;
|
||||||
|
spending = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CAddressIndexIteratorKey {
|
||||||
|
unsigned int type;
|
||||||
|
uint160 hashBytes;
|
||||||
|
|
||||||
|
size_t GetSerializeSize(int nType, int nVersion) const {
|
||||||
|
return 21;
|
||||||
|
}
|
||||||
|
template<typename Stream>
|
||||||
|
void Serialize(Stream& s) const {
|
||||||
|
ser_writedata8(s, type);
|
||||||
|
hashBytes.Serialize(s);
|
||||||
|
}
|
||||||
|
template<typename Stream>
|
||||||
|
void Unserialize(Stream& s) {
|
||||||
|
type = ser_readdata8(s);
|
||||||
|
hashBytes.Unserialize(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
CAddressIndexIteratorKey(unsigned int addressType, uint160 addressHash) {
|
||||||
|
type = addressType;
|
||||||
|
hashBytes = addressHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
CAddressIndexIteratorKey() {
|
||||||
|
SetNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetNull() {
|
||||||
|
type = 0;
|
||||||
|
hashBytes.SetNull();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CAddressIndexIteratorHeightKey {
|
||||||
|
unsigned int type;
|
||||||
|
uint160 hashBytes;
|
||||||
|
int blockHeight;
|
||||||
|
|
||||||
|
size_t GetSerializeSize(int nType, int nVersion) const {
|
||||||
|
return 25;
|
||||||
|
}
|
||||||
|
template<typename Stream>
|
||||||
|
void Serialize(Stream& s) const {
|
||||||
|
ser_writedata8(s, type);
|
||||||
|
hashBytes.Serialize(s);
|
||||||
|
ser_writedata32be(s, blockHeight);
|
||||||
|
}
|
||||||
|
template<typename Stream>
|
||||||
|
void Unserialize(Stream& s) {
|
||||||
|
type = ser_readdata8(s);
|
||||||
|
hashBytes.Unserialize(s);
|
||||||
|
blockHeight = ser_readdata32be(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
CAddressIndexIteratorHeightKey(unsigned int addressType, uint160 addressHash, int height) {
|
||||||
|
type = addressType;
|
||||||
|
hashBytes = addressHash;
|
||||||
|
blockHeight = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
CAddressIndexIteratorHeightKey() {
|
||||||
|
SetNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetNull() {
|
||||||
|
type = 0;
|
||||||
|
hashBytes.SetNull();
|
||||||
|
blockHeight = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BITCOIN_ADDRESSINDEX_H
|
17
src/init.cpp
17
src/init.cpp
|
@ -841,6 +841,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||||
return InitError(_("RPC method z_mergetoaddress requires -experimentalfeatures."));
|
return InitError(_("RPC method z_mergetoaddress requires -experimentalfeatures."));
|
||||||
} else if (mapArgs.count("-savesproutr1cs")) {
|
} else if (mapArgs.count("-savesproutr1cs")) {
|
||||||
return InitError(_("Saving the Sprout R1CS requires -experimentalfeatures."));
|
return InitError(_("Saving the Sprout R1CS requires -experimentalfeatures."));
|
||||||
|
} else if (mapArgs.count("-insightexplorer")) {
|
||||||
|
return InitError(_("Insight explorer requires -experimentalfeatures."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1442,6 +1444,15 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||||
int64_t nBlockTreeDBCache = nTotalCache / 8;
|
int64_t nBlockTreeDBCache = nTotalCache / 8;
|
||||||
if (nBlockTreeDBCache > (1 << 21) && !GetBoolArg("-txindex", false))
|
if (nBlockTreeDBCache > (1 << 21) && !GetBoolArg("-txindex", false))
|
||||||
nBlockTreeDBCache = (1 << 21); // block tree db cache shouldn't be larger than 2 MiB
|
nBlockTreeDBCache = (1 << 21); // block tree db cache shouldn't be larger than 2 MiB
|
||||||
|
|
||||||
|
// https://github.com/bitpay/bitcoin/commit/c91d78b578a8700a45be936cb5bb0931df8f4b87#diff-c865a8939105e6350a50af02766291b7R1233
|
||||||
|
if (GetBoolArg("-insightexplorer", false)) {
|
||||||
|
if (!GetBoolArg("-txindex", false)) {
|
||||||
|
return InitError(_("-insightexplorer requires -txindex."));
|
||||||
|
}
|
||||||
|
// increase cache if additional indices are needed
|
||||||
|
nBlockTreeDBCache = nTotalCache * 3 / 4;
|
||||||
|
}
|
||||||
nTotalCache -= nBlockTreeDBCache;
|
nTotalCache -= nBlockTreeDBCache;
|
||||||
int64_t nCoinDBCache = std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23)); // use 25%-50% of the remainder for disk cache
|
int64_t nCoinDBCache = std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23)); // use 25%-50% of the remainder for disk cache
|
||||||
nTotalCache -= nCoinDBCache;
|
nTotalCache -= nCoinDBCache;
|
||||||
|
@ -1503,6 +1514,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for changed -insightexplorer state
|
||||||
|
if (fInsightExplorer != GetBoolArg("-insightexplorer", false)) {
|
||||||
|
strLoadError = _("You need to rebuild the database using -reindex to change -insightexplorer");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for changed -prune state. What we are concerned about is a user who has pruned blocks
|
// Check for changed -prune state. What we are concerned about is a user who has pruned blocks
|
||||||
// in the past, but is now trying to run unpruned.
|
// in the past, but is now trying to run unpruned.
|
||||||
if (fHavePruned && !fPruneMode) {
|
if (fHavePruned && !fPruneMode) {
|
||||||
|
|
144
src/main.cpp
144
src/main.cpp
|
@ -67,6 +67,8 @@ bool fExperimentalMode = false;
|
||||||
bool fImporting = false;
|
bool fImporting = false;
|
||||||
bool fReindex = false;
|
bool fReindex = false;
|
||||||
bool fTxIndex = false;
|
bool fTxIndex = false;
|
||||||
|
bool fInsightExplorer = false; // insightexplorer
|
||||||
|
bool fAddressIndex = false; // insightexplorer
|
||||||
bool fHavePruned = false;
|
bool fHavePruned = false;
|
||||||
bool fPruneMode = false;
|
bool fPruneMode = false;
|
||||||
bool fIsBareMultisigStd = true;
|
bool fIsBareMultisigStd = true;
|
||||||
|
@ -2226,8 +2228,11 @@ enum DisconnectResult
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Undo the effects of this block (with given index) on the UTXO set represented by coins.
|
/** Undo the effects of this block (with given index) on the UTXO set represented by coins.
|
||||||
* When UNCLEAN or FAILED is returned, view is left in an indeterminate state. */
|
* When UNCLEAN or FAILED is returned, view is left in an indeterminate state.
|
||||||
DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view)
|
* The addressIndex will be updated if requested.
|
||||||
|
*/
|
||||||
|
static DisconnectResult DisconnectBlock(const CBlock& block, CValidationState& state,
|
||||||
|
const CBlockIndex* pindex, CCoinsViewCache& view, const bool fUpdateAddressIndex)
|
||||||
{
|
{
|
||||||
assert(pindex->GetBlockHash() == view.GetBestBlock());
|
assert(pindex->GetBlockHash() == view.GetBestBlock());
|
||||||
|
|
||||||
|
@ -2248,11 +2253,35 @@ DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex,
|
||||||
error("DisconnectBlock(): block and undo data inconsistent");
|
error("DisconnectBlock(): block and undo data inconsistent");
|
||||||
return DISCONNECT_FAILED;
|
return DISCONNECT_FAILED;
|
||||||
}
|
}
|
||||||
|
std::vector<CAddressIndexDbEntry> addressIndex;
|
||||||
|
std::vector<CAddressUnspentDbEntry> addressUnspentIndex;
|
||||||
|
|
||||||
// undo transactions in reverse order
|
// undo transactions in reverse order
|
||||||
for (int i = block.vtx.size() - 1; i >= 0; i--) {
|
for (int i = block.vtx.size() - 1; i >= 0; i--) {
|
||||||
const CTransaction &tx = block.vtx[i];
|
const CTransaction &tx = block.vtx[i];
|
||||||
uint256 hash = tx.GetHash();
|
uint256 const hash = tx.GetHash();
|
||||||
|
|
||||||
|
// insightexplorer
|
||||||
|
// https://github.com/bitpay/bitcoin/commit/017f548ea6d89423ef568117447e61dd5707ec42#diff-7ec3c68a81efff79b6ca22ac1f1eabbaR2236
|
||||||
|
if (fAddressIndex && fUpdateAddressIndex) {
|
||||||
|
for (unsigned int k = tx.vout.size(); k-- > 0;) {
|
||||||
|
const CTxOut &out = tx.vout[k];
|
||||||
|
int const scriptType = out.scriptPubKey.Type();
|
||||||
|
if (scriptType > 0) {
|
||||||
|
uint160 const addrHash = out.scriptPubKey.AddressHash();
|
||||||
|
|
||||||
|
// undo receiving activity
|
||||||
|
addressIndex.push_back(make_pair(
|
||||||
|
CAddressIndexKey(scriptType, addrHash, pindex->nHeight, i, hash, k, false),
|
||||||
|
out.nValue));
|
||||||
|
|
||||||
|
// undo unspent index
|
||||||
|
addressUnspentIndex.push_back(make_pair(
|
||||||
|
CAddressUnspentKey(scriptType, addrHash, hash, k),
|
||||||
|
CAddressUnspentValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check that all outputs are available and match the outputs in the block itself
|
// Check that all outputs are available and match the outputs in the block itself
|
||||||
// exactly.
|
// exactly.
|
||||||
|
@ -2288,6 +2317,27 @@ DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex,
|
||||||
const CTxInUndo &undo = txundo.vprevout[j];
|
const CTxInUndo &undo = txundo.vprevout[j];
|
||||||
if (!ApplyTxInUndo(undo, view, out))
|
if (!ApplyTxInUndo(undo, view, out))
|
||||||
fClean = false;
|
fClean = false;
|
||||||
|
|
||||||
|
// insightexplorer
|
||||||
|
// https://github.com/bitpay/bitcoin/commit/017f548ea6d89423ef568117447e61dd5707ec42#diff-7ec3c68a81efff79b6ca22ac1f1eabbaR2304
|
||||||
|
if (fAddressIndex && fUpdateAddressIndex) {
|
||||||
|
const CTxIn input = tx.vin[j];
|
||||||
|
const CTxOut &prevout = view.GetOutputFor(input);
|
||||||
|
int const scriptType = prevout.scriptPubKey.Type();
|
||||||
|
if (scriptType > 0) {
|
||||||
|
uint160 const addrHash = prevout.scriptPubKey.AddressHash();
|
||||||
|
|
||||||
|
// undo spending activity
|
||||||
|
addressIndex.push_back(make_pair(
|
||||||
|
CAddressIndexKey(scriptType, addrHash, pindex->nHeight, i, hash, j, true),
|
||||||
|
prevout.nValue * -1));
|
||||||
|
|
||||||
|
// restore unspent index
|
||||||
|
addressUnspentIndex.push_back(make_pair(
|
||||||
|
CAddressUnspentKey(scriptType, addrHash, input.prevout.hash, input.prevout.n),
|
||||||
|
CAddressUnspentValue(prevout.nValue, prevout.scriptPubKey, undo.nHeight)));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2309,6 +2359,17 @@ DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex,
|
||||||
// move best block pointer to prevout block
|
// move best block pointer to prevout block
|
||||||
view.SetBestBlock(pindex->pprev->GetBlockHash());
|
view.SetBestBlock(pindex->pprev->GetBlockHash());
|
||||||
|
|
||||||
|
// insightexplorer
|
||||||
|
if (fAddressIndex && fUpdateAddressIndex) {
|
||||||
|
if (!pblocktree->EraseAddressIndex(addressIndex)) {
|
||||||
|
AbortNode(state, "Failed to delete address index");
|
||||||
|
return DISCONNECT_FAILED;
|
||||||
|
}
|
||||||
|
if (!pblocktree->UpdateAddressUnspentIndex(addressUnspentIndex)) {
|
||||||
|
AbortNode(state, "Failed to write address unspent index");
|
||||||
|
return DISCONNECT_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN;
|
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2505,6 +2566,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||||
std::vector<std::pair<uint256, CDiskTxPos> > vPos;
|
std::vector<std::pair<uint256, CDiskTxPos> > vPos;
|
||||||
vPos.reserve(block.vtx.size());
|
vPos.reserve(block.vtx.size());
|
||||||
blockundo.vtxundo.reserve(block.vtx.size() - 1);
|
blockundo.vtxundo.reserve(block.vtx.size() - 1);
|
||||||
|
std::vector<CAddressIndexDbEntry> addressIndex;
|
||||||
|
std::vector<CAddressUnspentDbEntry> addressUnspentIndex;
|
||||||
|
|
||||||
// Construct the incremental merkle tree at the current
|
// Construct the incremental merkle tree at the current
|
||||||
// block position,
|
// block position,
|
||||||
|
@ -2535,6 +2598,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||||
for (unsigned int i = 0; i < block.vtx.size(); i++)
|
for (unsigned int i = 0; i < block.vtx.size(); i++)
|
||||||
{
|
{
|
||||||
const CTransaction &tx = block.vtx[i];
|
const CTransaction &tx = block.vtx[i];
|
||||||
|
const uint256 hash = tx.GetHash();
|
||||||
|
|
||||||
nInputs += tx.vin.size();
|
nInputs += tx.vin.size();
|
||||||
nSigOps += GetLegacySigOpCount(tx);
|
nSigOps += GetLegacySigOpCount(tx);
|
||||||
|
@ -2553,6 +2617,30 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||||
return state.DoS(100, error("ConnectBlock(): JoinSplit requirements not met"),
|
return state.DoS(100, error("ConnectBlock(): JoinSplit requirements not met"),
|
||||||
REJECT_INVALID, "bad-txns-joinsplit-requirements-not-met");
|
REJECT_INVALID, "bad-txns-joinsplit-requirements-not-met");
|
||||||
|
|
||||||
|
// insightexplorer
|
||||||
|
// https://github.com/bitpay/bitcoin/commit/017f548ea6d89423ef568117447e61dd5707ec42#diff-7ec3c68a81efff79b6ca22ac1f1eabbaR2597
|
||||||
|
if (fAddressIndex) {
|
||||||
|
for (size_t j = 0; j < tx.vin.size(); j++) {
|
||||||
|
|
||||||
|
const CTxIn input = tx.vin[j];
|
||||||
|
const CTxOut &prevout = view.GetOutputFor(tx.vin[j]);
|
||||||
|
int const scriptType = prevout.scriptPubKey.Type();
|
||||||
|
if (scriptType > 0) {
|
||||||
|
uint160 const addrHash = prevout.scriptPubKey.AddressHash();
|
||||||
|
|
||||||
|
// record spending activity
|
||||||
|
addressIndex.push_back(make_pair(
|
||||||
|
CAddressIndexKey(scriptType, addrHash, pindex->nHeight, i, hash, j, true),
|
||||||
|
prevout.nValue * -1));
|
||||||
|
|
||||||
|
// remove address from unspent index
|
||||||
|
addressUnspentIndex.push_back(make_pair(
|
||||||
|
CAddressUnspentKey(scriptType, addrHash, input.prevout.hash, input.prevout.n),
|
||||||
|
CAddressUnspentValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add in sigops done by pay-to-script-hash inputs;
|
// Add in sigops done by pay-to-script-hash inputs;
|
||||||
// this is to prevent a "rogue miner" from creating
|
// this is to prevent a "rogue miner" from creating
|
||||||
// an incredibly-expensive-to-validate block.
|
// an incredibly-expensive-to-validate block.
|
||||||
|
@ -2575,6 +2663,28 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||||
control.Add(vChecks);
|
control.Add(vChecks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// insightexplorer
|
||||||
|
// https://github.com/bitpay/bitcoin/commit/017f548ea6d89423ef568117447e61dd5707ec42#diff-7ec3c68a81efff79b6ca22ac1f1eabbaR2656
|
||||||
|
if (fAddressIndex) {
|
||||||
|
for (unsigned int k = 0; k < tx.vout.size(); k++) {
|
||||||
|
const CTxOut &out = tx.vout[k];
|
||||||
|
int const scriptType = out.scriptPubKey.Type();
|
||||||
|
if (scriptType > 0) {
|
||||||
|
uint160 const addrHash = out.scriptPubKey.AddressHash();
|
||||||
|
|
||||||
|
// record receiving activity
|
||||||
|
addressIndex.push_back(make_pair(
|
||||||
|
CAddressIndexKey(scriptType, addrHash, pindex->nHeight, i, hash, k, false),
|
||||||
|
out.nValue));
|
||||||
|
|
||||||
|
// record unspent output
|
||||||
|
addressUnspentIndex.push_back(make_pair(
|
||||||
|
CAddressUnspentKey(scriptType, addrHash, hash, k),
|
||||||
|
CAddressUnspentValue(out.nValue, out.scriptPubKey, pindex->nHeight)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CTxUndo undoDummy;
|
CTxUndo undoDummy;
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
blockundo.vtxundo.push_back(CTxUndo());
|
blockundo.vtxundo.push_back(CTxUndo());
|
||||||
|
@ -2666,6 +2776,16 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||||
if (!pblocktree->WriteTxIndex(vPos))
|
if (!pblocktree->WriteTxIndex(vPos))
|
||||||
return AbortNode(state, "Failed to write transaction index");
|
return AbortNode(state, "Failed to write transaction index");
|
||||||
|
|
||||||
|
// insightexplorer
|
||||||
|
if (fAddressIndex) {
|
||||||
|
if (!pblocktree->WriteAddressIndex(addressIndex)) {
|
||||||
|
return AbortNode(state, "Failed to write address index");
|
||||||
|
}
|
||||||
|
if (!pblocktree->UpdateAddressUnspentIndex(addressUnspentIndex)) {
|
||||||
|
return AbortNode(state, "Failed to write address unspent index");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// add this block to the view's block chain
|
// add this block to the view's block chain
|
||||||
view.SetBestBlock(pindex->GetBlockHash());
|
view.SetBestBlock(pindex->GetBlockHash());
|
||||||
|
|
||||||
|
@ -2860,7 +2980,8 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) {
|
||||||
int64_t nStart = GetTimeMicros();
|
int64_t nStart = GetTimeMicros();
|
||||||
{
|
{
|
||||||
CCoinsViewCache view(pcoinsTip);
|
CCoinsViewCache view(pcoinsTip);
|
||||||
if (DisconnectBlock(block, pindexDelete, view) != DISCONNECT_OK)
|
// insightexplorer: update indices (true)
|
||||||
|
if (DisconnectBlock(block, state, pindexDelete, view, true) != DISCONNECT_OK)
|
||||||
return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString());
|
return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString());
|
||||||
assert(view.Flush());
|
assert(view.Flush());
|
||||||
}
|
}
|
||||||
|
@ -4195,6 +4316,12 @@ bool static LoadBlockIndexDB()
|
||||||
pblocktree->ReadFlag("txindex", fTxIndex);
|
pblocktree->ReadFlag("txindex", fTxIndex);
|
||||||
LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled");
|
LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled");
|
||||||
|
|
||||||
|
// insightexplorer
|
||||||
|
// Check whether block explorer features are enabled
|
||||||
|
pblocktree->ReadFlag("insightexplorer", fInsightExplorer);
|
||||||
|
LogPrintf("%s: insight explorer %s\n", __func__, fAddressIndex ? "enabled" : "disabled");
|
||||||
|
fAddressIndex = fInsightExplorer;
|
||||||
|
|
||||||
// Fill in-memory data
|
// Fill in-memory data
|
||||||
BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex)
|
BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex)
|
||||||
{
|
{
|
||||||
|
@ -4283,7 +4410,8 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth
|
||||||
}
|
}
|
||||||
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
|
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
|
||||||
if (nCheckLevel >= 3 && pindex == pindexState && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) {
|
if (nCheckLevel >= 3 && pindex == pindexState && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) {
|
||||||
DisconnectResult res = DisconnectBlock(block, pindex, coins);
|
// insightexplorer: do not update indices (false)
|
||||||
|
DisconnectResult res = DisconnectBlock(block, state, pindex, coins, false);
|
||||||
if (res == DISCONNECT_FAILED) {
|
if (res == DISCONNECT_FAILED) {
|
||||||
return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
|
return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
|
||||||
}
|
}
|
||||||
|
@ -4530,6 +4658,12 @@ bool InitBlockIndex() {
|
||||||
// Use the provided setting for -txindex in the new database
|
// Use the provided setting for -txindex in the new database
|
||||||
fTxIndex = GetBoolArg("-txindex", false);
|
fTxIndex = GetBoolArg("-txindex", false);
|
||||||
pblocktree->WriteFlag("txindex", fTxIndex);
|
pblocktree->WriteFlag("txindex", fTxIndex);
|
||||||
|
|
||||||
|
// Use the provided setting for -insightexplorer in the new database
|
||||||
|
fInsightExplorer = GetBoolArg("-insightexplorer", false);
|
||||||
|
pblocktree->WriteFlag("insightexplorer", fInsightExplorer);
|
||||||
|
fAddressIndex = fInsightExplorer;
|
||||||
|
|
||||||
LogPrintf("Initializing databases...\n");
|
LogPrintf("Initializing databases...\n");
|
||||||
|
|
||||||
// Only add the genesis block if not reindexing (in which case we reuse the one already on disk)
|
// Only add the genesis block if not reindexing (in which case we reuse the one already on disk)
|
||||||
|
|
20
src/main.h
20
src/main.h
|
@ -26,6 +26,7 @@
|
||||||
#include "tinyformat.h"
|
#include "tinyformat.h"
|
||||||
#include "txmempool.h"
|
#include "txmempool.h"
|
||||||
#include "uint256.h"
|
#include "uint256.h"
|
||||||
|
#include "addressindex.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
@ -131,6 +132,19 @@ extern bool fImporting;
|
||||||
extern bool fReindex;
|
extern bool fReindex;
|
||||||
extern int nScriptCheckThreads;
|
extern int nScriptCheckThreads;
|
||||||
extern bool fTxIndex;
|
extern bool fTxIndex;
|
||||||
|
|
||||||
|
// START insightexplorer
|
||||||
|
extern bool fInsightExplorer;
|
||||||
|
|
||||||
|
// The following flags enable specific indices (DB tables), but are not exposed as
|
||||||
|
// separate command-line options; instead they are enabled by experimental feature "-insightexplorer"
|
||||||
|
// and are always equal to the overall controlling flag, fInsightExplorer.
|
||||||
|
|
||||||
|
// Maintain a full address index, used to query for the balance, txids and unspent outputs for addresses
|
||||||
|
extern bool fAddressIndex;
|
||||||
|
|
||||||
|
// END insightexplorer
|
||||||
|
|
||||||
extern bool fIsBareMultisigStd;
|
extern bool fIsBareMultisigStd;
|
||||||
extern bool fCheckBlockIndex;
|
extern bool fCheckBlockIndex;
|
||||||
extern bool fCheckpointsEnabled;
|
extern bool fCheckpointsEnabled;
|
||||||
|
@ -449,12 +463,6 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex);
|
||||||
|
|
||||||
/** Functions for validating blocks and updating the block tree */
|
/** Functions for validating blocks and updating the block tree */
|
||||||
|
|
||||||
/** Undo the effects of this block (with given index) on the UTXO set represented by coins.
|
|
||||||
* In case pfClean is provided, operation will try to be tolerant about errors, and *pfClean
|
|
||||||
* will be true if no problems were found. Otherwise, the return value will be false in case
|
|
||||||
* of problems. Note that in any case, coins may be modified. */
|
|
||||||
bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool* pfClean = NULL);
|
|
||||||
|
|
||||||
/** Apply the effects of this block (with given index) on the UTXO set represented by coins */
|
/** Apply the effects of this block (with given index) on the UTXO set represented by coins */
|
||||||
bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool fJustCheck = false);
|
bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool fJustCheck = false);
|
||||||
|
|
||||||
|
|
|
@ -201,6 +201,19 @@ unsigned int CScript::GetSigOpCount(const CScript& scriptSig) const
|
||||||
return subscript.GetSigOpCount(true);
|
return subscript.GetSigOpCount(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// insightexplorer
|
||||||
|
// https://github.com/bitpay/bitcoin/commit/017f548ea6d89423ef568117447e61dd5707ec42#diff-f7ca24fb80ddba0f291cb66344ca6fcbR204
|
||||||
|
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
|
bool CScript::IsPayToScriptHash() const
|
||||||
{
|
{
|
||||||
// Extra-fast test for pay-to-script-hash CScripts:
|
// Extra-fast test for pay-to-script-hash CScripts:
|
||||||
|
@ -227,3 +240,32 @@ bool CScript::IsPushOnly() const
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// insightexplorer
|
||||||
|
int CScript::Type() const
|
||||||
|
{
|
||||||
|
if (this->IsPayToPublicKeyHash())
|
||||||
|
return 1;
|
||||||
|
if (this->IsPayToScriptHash())
|
||||||
|
return 2;
|
||||||
|
// We don't know this script
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// insightexplorer
|
||||||
|
uint160 CScript::AddressHash() const
|
||||||
|
{
|
||||||
|
// where the address bytes begin depends on the script type
|
||||||
|
int start;
|
||||||
|
if (this->IsPayToPublicKeyHash())
|
||||||
|
start = 3;
|
||||||
|
else if (this->IsPayToScriptHash())
|
||||||
|
start = 2;
|
||||||
|
else {
|
||||||
|
// unknown script type; return an empty vector
|
||||||
|
vector<unsigned char> hashBytes;
|
||||||
|
return uint160(hashBytes);
|
||||||
|
}
|
||||||
|
vector<unsigned char> hashBytes(this->begin()+start, this->begin()+start+20);
|
||||||
|
return uint160(hashBytes);
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "uint256.h"
|
||||||
|
|
||||||
static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes
|
static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes
|
||||||
|
|
||||||
// Maximum script length in bytes
|
// Maximum script length in bytes
|
||||||
|
@ -565,8 +567,12 @@ public:
|
||||||
*/
|
*/
|
||||||
unsigned int GetSigOpCount(const CScript& scriptSig) const;
|
unsigned int GetSigOpCount(const CScript& scriptSig) const;
|
||||||
|
|
||||||
|
bool IsPayToPublicKeyHash() const;
|
||||||
bool IsPayToScriptHash() const;
|
bool IsPayToScriptHash() const;
|
||||||
|
|
||||||
|
int Type() const;
|
||||||
|
uint160 AddressHash() const;
|
||||||
|
|
||||||
/** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */
|
/** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */
|
||||||
bool IsPushOnly() const;
|
bool IsPushOnly() const;
|
||||||
|
|
||||||
|
|
|
@ -110,6 +110,11 @@ template<typename Stream> inline void ser_writedata32(Stream &s, uint32_t obj)
|
||||||
obj = htole32(obj);
|
obj = htole32(obj);
|
||||||
s.write((char*)&obj, 4);
|
s.write((char*)&obj, 4);
|
||||||
}
|
}
|
||||||
|
template<typename Stream> inline void ser_writedata32be(Stream &s, uint32_t obj)
|
||||||
|
{
|
||||||
|
obj = htobe32(obj);
|
||||||
|
s.write((char*)&obj, 4);
|
||||||
|
}
|
||||||
template<typename Stream> inline void ser_writedata64(Stream &s, uint64_t obj)
|
template<typename Stream> inline void ser_writedata64(Stream &s, uint64_t obj)
|
||||||
{
|
{
|
||||||
obj = htole64(obj);
|
obj = htole64(obj);
|
||||||
|
@ -133,6 +138,12 @@ template<typename Stream> inline uint32_t ser_readdata32(Stream &s)
|
||||||
s.read((char*)&obj, 4);
|
s.read((char*)&obj, 4);
|
||||||
return le32toh(obj);
|
return le32toh(obj);
|
||||||
}
|
}
|
||||||
|
template<typename Stream> inline uint32_t ser_readdata32be(Stream &s)
|
||||||
|
{
|
||||||
|
uint32_t obj;
|
||||||
|
s.read((char*)&obj, 4);
|
||||||
|
return be32toh(obj);
|
||||||
|
}
|
||||||
template<typename Stream> inline uint64_t ser_readdata64(Stream &s)
|
template<typename Stream> inline uint64_t ser_readdata64(Stream &s)
|
||||||
{
|
{
|
||||||
uint64_t obj;
|
uint64_t obj;
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
// Copyright (c) 2012-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.
|
||||||
|
|
||||||
|
#include "script/script.h"
|
||||||
|
#include "test/test_bitcoin.h"
|
||||||
|
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_SUITE(script_P2PKH_tests, BasicTestingSetup)
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(IsPayToPublicKeyHash)
|
||||||
|
{
|
||||||
|
// Test CScript::IsPayToPublicKeyHash()
|
||||||
|
uint160 dummy;
|
||||||
|
CScript p2pkh;
|
||||||
|
p2pkh << OP_DUP << OP_HASH160 << ToByteVector(dummy) << OP_EQUALVERIFY << OP_CHECKSIG;
|
||||||
|
BOOST_CHECK(p2pkh.IsPayToPublicKeyHash());
|
||||||
|
|
||||||
|
static const unsigned char direct[] = {
|
||||||
|
OP_DUP, OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUALVERIFY, OP_CHECKSIG
|
||||||
|
};
|
||||||
|
BOOST_CHECK(CScript(direct, direct+sizeof(direct)).IsPayToPublicKeyHash());
|
||||||
|
|
||||||
|
static const unsigned char notp2pkh1[] = {
|
||||||
|
OP_DUP, OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUALVERIFY, OP_CHECKSIG, OP_CHECKSIG
|
||||||
|
};
|
||||||
|
BOOST_CHECK(!CScript(notp2pkh1, notp2pkh1+sizeof(notp2pkh1)).IsPayToPublicKeyHash());
|
||||||
|
|
||||||
|
static const unsigned char p2sh[] = {
|
||||||
|
OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUAL
|
||||||
|
};
|
||||||
|
BOOST_CHECK(!CScript(p2sh, p2sh+sizeof(p2sh)).IsPayToPublicKeyHash());
|
||||||
|
|
||||||
|
static const unsigned char extra[] = {
|
||||||
|
OP_DUP, OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUALVERIFY, OP_CHECKSIG, OP_CHECKSIG
|
||||||
|
};
|
||||||
|
BOOST_CHECK(!CScript(extra, extra+sizeof(extra)).IsPayToPublicKeyHash());
|
||||||
|
|
||||||
|
static const unsigned char missing[] = {
|
||||||
|
OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUALVERIFY, OP_CHECKSIG, OP_RETURN
|
||||||
|
};
|
||||||
|
BOOST_CHECK(!CScript(missing, missing+sizeof(missing)).IsPayToPublicKeyHash());
|
||||||
|
|
||||||
|
static const unsigned char missing2[] = {
|
||||||
|
OP_DUP, OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
|
};
|
||||||
|
BOOST_CHECK(!CScript(missing2, missing2+sizeof(missing)).IsPayToPublicKeyHash());
|
||||||
|
|
||||||
|
static const unsigned char tooshort[] = {
|
||||||
|
OP_DUP, OP_HASH160, 2, 0,0, OP_EQUALVERIFY, OP_CHECKSIG
|
||||||
|
};
|
||||||
|
BOOST_CHECK(!CScript(tooshort, tooshort+sizeof(direct)).IsPayToPublicKeyHash());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
83
src/txdb.cpp
83
src/txdb.cpp
|
@ -35,6 +35,10 @@ static const char DB_FLAG = 'F';
|
||||||
static const char DB_REINDEX_FLAG = 'R';
|
static const char DB_REINDEX_FLAG = 'R';
|
||||||
static const char DB_LAST_BLOCK = 'l';
|
static const char DB_LAST_BLOCK = 'l';
|
||||||
|
|
||||||
|
// insightexplorer
|
||||||
|
static const char DB_ADDRESSINDEX = 'd';
|
||||||
|
static const char DB_ADDRESSUNSPENTINDEX = 'u';
|
||||||
|
|
||||||
|
|
||||||
CCoinsViewDB::CCoinsViewDB(std::string dbName, size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / dbName, nCacheSize, fMemory, fWipe) {
|
CCoinsViewDB::CCoinsViewDB(std::string dbName, size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / dbName, nCacheSize, fMemory, fWipe) {
|
||||||
}
|
}
|
||||||
|
@ -293,6 +297,85 @@ bool CBlockTreeDB::WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos>
|
||||||
return WriteBatch(batch);
|
return WriteBatch(batch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// START insightexplorer
|
||||||
|
// https://github.com/bitpay/bitcoin/commit/017f548ea6d89423ef568117447e61dd5707ec42#diff-81e4f16a1b5d5b7ca25351a63d07cb80R183
|
||||||
|
bool CBlockTreeDB::UpdateAddressUnspentIndex(const std::vector<CAddressUnspentDbEntry> &vect)
|
||||||
|
{
|
||||||
|
CDBBatch batch(*this);
|
||||||
|
for (std::vector<CAddressUnspentDbEntry>::const_iterator it=vect.begin(); it!=vect.end(); it++) {
|
||||||
|
if (it->second.IsNull()) {
|
||||||
|
batch.Erase(make_pair(DB_ADDRESSUNSPENTINDEX, it->first));
|
||||||
|
} else {
|
||||||
|
batch.Write(make_pair(DB_ADDRESSUNSPENTINDEX, it->first), it->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return WriteBatch(batch);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBlockTreeDB::ReadAddressUnspentIndex(uint160 addressHash, int type, std::vector<CAddressUnspentDbEntry> &unspentOutputs)
|
||||||
|
{
|
||||||
|
boost::scoped_ptr<CDBIterator> pcursor(NewIterator());
|
||||||
|
|
||||||
|
pcursor->Seek(make_pair(DB_ADDRESSUNSPENTINDEX, CAddressIndexIteratorKey(type, addressHash)));
|
||||||
|
|
||||||
|
while (pcursor->Valid()) {
|
||||||
|
boost::this_thread::interruption_point();
|
||||||
|
std::pair<char,CAddressUnspentKey> key;
|
||||||
|
if (!(pcursor->GetKey(key) && key.first == DB_ADDRESSUNSPENTINDEX && key.second.hashBytes == addressHash))
|
||||||
|
break;
|
||||||
|
CAddressUnspentValue nValue;
|
||||||
|
if (pcursor->GetValue(nValue))
|
||||||
|
return error("failed to get address unspent value");
|
||||||
|
unspentOutputs.push_back(make_pair(key.second, nValue));
|
||||||
|
pcursor->Next();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBlockTreeDB::WriteAddressIndex(const std::vector<CAddressIndexDbEntry> &vect) {
|
||||||
|
CDBBatch batch(*this);
|
||||||
|
for (std::vector<CAddressIndexDbEntry>::const_iterator it=vect.begin(); it!=vect.end(); it++)
|
||||||
|
batch.Write(make_pair(DB_ADDRESSINDEX, it->first), it->second);
|
||||||
|
return WriteBatch(batch);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBlockTreeDB::EraseAddressIndex(const std::vector<CAddressIndexDbEntry> &vect) {
|
||||||
|
CDBBatch batch(*this);
|
||||||
|
for (std::vector<CAddressIndexDbEntry>::const_iterator it=vect.begin(); it!=vect.end(); it++)
|
||||||
|
batch.Erase(make_pair(DB_ADDRESSINDEX, it->first));
|
||||||
|
return WriteBatch(batch);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBlockTreeDB::ReadAddressIndex(
|
||||||
|
uint160 addressHash, int type,
|
||||||
|
std::vector<CAddressIndexDbEntry> &addressIndex,
|
||||||
|
int start, int end)
|
||||||
|
{
|
||||||
|
boost::scoped_ptr<CDBIterator> pcursor(NewIterator());
|
||||||
|
|
||||||
|
if (start > 0 && end > 0) {
|
||||||
|
pcursor->Seek(make_pair(DB_ADDRESSINDEX, CAddressIndexIteratorHeightKey(type, addressHash, start)));
|
||||||
|
} else {
|
||||||
|
pcursor->Seek(make_pair(DB_ADDRESSINDEX, CAddressIndexIteratorKey(type, addressHash)));
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
break;
|
||||||
|
if (end > 0 && key.second.blockHeight > end)
|
||||||
|
break;
|
||||||
|
CAmount nValue;
|
||||||
|
if (!pcursor->GetValue(nValue))
|
||||||
|
return error("failed to get address index value");
|
||||||
|
addressIndex.push_back(make_pair(key.second, nValue));
|
||||||
|
pcursor->Next();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// END insightexplorer
|
||||||
|
|
||||||
bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) {
|
bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) {
|
||||||
return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0');
|
return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0');
|
||||||
}
|
}
|
||||||
|
|
21
src/txdb.h
21
src/txdb.h
|
@ -17,6 +17,18 @@
|
||||||
class CBlockFileInfo;
|
class CBlockFileInfo;
|
||||||
class CBlockIndex;
|
class CBlockIndex;
|
||||||
struct CDiskTxPos;
|
struct CDiskTxPos;
|
||||||
|
|
||||||
|
// START insightexplorer
|
||||||
|
struct CAddressUnspentKey;
|
||||||
|
struct CAddressUnspentValue;
|
||||||
|
struct CAddressIndexKey;
|
||||||
|
struct CAddressIndexIteratorKey;
|
||||||
|
struct CAddressIndexIteratorHeightKey;
|
||||||
|
|
||||||
|
typedef std::pair<CAddressUnspentKey, CAddressUnspentValue> CAddressUnspentDbEntry;
|
||||||
|
typedef std::pair<CAddressIndexKey, CAmount> CAddressIndexDbEntry;
|
||||||
|
// END insightexplorer
|
||||||
|
|
||||||
class uint256;
|
class uint256;
|
||||||
|
|
||||||
//! -dbcache default (MiB)
|
//! -dbcache default (MiB)
|
||||||
|
@ -70,6 +82,15 @@ public:
|
||||||
bool ReadReindexing(bool &fReindex);
|
bool ReadReindexing(bool &fReindex);
|
||||||
bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos);
|
bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos);
|
||||||
bool WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> > &list);
|
bool WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> > &list);
|
||||||
|
|
||||||
|
// START insightexplorer
|
||||||
|
bool UpdateAddressUnspentIndex(const std::vector<CAddressUnspentDbEntry> &vect);
|
||||||
|
bool ReadAddressUnspentIndex(uint160 addressHash, int type, std::vector<CAddressUnspentDbEntry> &vect);
|
||||||
|
bool WriteAddressIndex(const std::vector<CAddressIndexDbEntry> &vect);
|
||||||
|
bool EraseAddressIndex(const std::vector<CAddressIndexDbEntry> &vect);
|
||||||
|
bool ReadAddressIndex(uint160 addressHash, int type, std::vector<CAddressIndexDbEntry> &addressIndex, int start = 0, int end = 0);
|
||||||
|
// END insightexplorer
|
||||||
|
|
||||||
bool WriteFlag(const std::string &name, bool fValue);
|
bool WriteFlag(const std::string &name, bool fValue);
|
||||||
bool ReadFlag(const std::string &name, bool &fValue);
|
bool ReadFlag(const std::string &name, bool &fValue);
|
||||||
bool LoadBlockIndexGuts();
|
bool LoadBlockIndexGuts();
|
||||||
|
|
Loading…
Reference in New Issue