|
|
@ -63,6 +63,9 @@ bool fExperimentalMode = false; |
|
|
|
bool fImporting = false; |
|
|
|
bool fReindex = false; |
|
|
|
bool fTxIndex = false; |
|
|
|
bool fAddressIndex = false; |
|
|
|
bool fTimestampIndex = false; |
|
|
|
bool fSpentIndex = false; |
|
|
|
bool fHavePruned = false; |
|
|
|
bool fPruneMode = false; |
|
|
|
bool fIsBareMultisigStd = true; |
|
|
@ -1257,6 +1260,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); |
|
|
@ -1264,6 +1277,55 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
bool GetTimestampIndex(const unsigned int &high, const unsigned int &low, const bool fActiveOnly, std::vector<std::pair<uint256, unsigned int> > &hashes) |
|
|
|
{ |
|
|
|
if (!fTimestampIndex) |
|
|
|
return error("Timestamp index not enabled"); |
|
|
|
|
|
|
|
if (!pblocktree->ReadTimestampIndex(high, low, fActiveOnly, hashes)) |
|
|
|
return error("Unable to get hashes for timestamps"); |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
bool GetSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value) |
|
|
|
{ |
|
|
|
if (!fSpentIndex) |
|
|
|
return false; |
|
|
|
|
|
|
|
if (mempool.getSpentIndex(key, value)) |
|
|
|
return true; |
|
|
|
|
|
|
|
if (!pblocktree->ReadSpentIndex(key, value)) |
|
|
|
return false; |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
bool GetAddressIndex(uint160 addressHash, int type, |
|
|
|
std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex, int start, int end) |
|
|
|
{ |
|
|
|
if (!fAddressIndex) |
|
|
|
return error("address index not enabled"); |
|
|
|
|
|
|
|
if (!pblocktree->ReadAddressIndex(addressHash, type, addressIndex, start, end)) |
|
|
|
return error("unable to get txids for address"); |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
bool GetAddressUnspent(uint160 addressHash, int type, |
|
|
|
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > &unspentOutputs) |
|
|
|
{ |
|
|
|
if (!fAddressIndex) |
|
|
|
return error("address index not enabled"); |
|
|
|
|
|
|
|
if (!pblocktree->ReadAddressUnspentIndex(addressHash, type, unspentOutputs)) |
|
|
|
return error("unable to get txids for address"); |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
/** Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock */ |
|
|
|
bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow) |
|
|
|
{ |
|
|
@ -1887,11 +1949,47 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex |
|
|
|
if (blockUndo.vtxundo.size() + 1 != block.vtx.size()) |
|
|
|
return error("DisconnectBlock(): block and undo data inconsistent"); |
|
|
|
|
|
|
|
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex; |
|
|
|
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > addressUnspentIndex; |
|
|
|
std::vector<std::pair<CSpentIndexKey, CSpentIndexValue> > spentIndex; |
|
|
|
|
|
|
|
|
|
|
|
// undo transactions in reverse order
|
|
|
|
for (int i = block.vtx.size() - 1; i >= 0; i--) { |
|
|
|
const CTransaction &tx = block.vtx[i]; |
|
|
|
uint256 hash = tx.GetHash(); |
|
|
|
|
|
|
|
if (fAddressIndex) { |
|
|
|
|
|
|
|
for (unsigned int k = tx.vout.size(); k-- > 0;) { |
|
|
|
const CTxOut &out = tx.vout[k]; |
|
|
|
|
|
|
|
if (out.scriptPubKey.IsPayToScriptHash()) { |
|
|
|
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+2, out.scriptPubKey.begin()+22); |
|
|
|
|
|
|
|
// undo receiving activity
|
|
|
|
addressIndex.push_back(make_pair(CAddressIndexKey(2, uint160(hashBytes), pindex->nHeight, i, hash, k, false), out.nValue)); |
|
|
|
|
|
|
|
// undo unspent index
|
|
|
|
addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(2, uint160(hashBytes), hash, k), CAddressUnspentValue())); |
|
|
|
|
|
|
|
} else if (out.scriptPubKey.IsPayToPublicKeyHash()) { |
|
|
|
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23); |
|
|
|
|
|
|
|
// undo receiving activity
|
|
|
|
addressIndex.push_back(make_pair(CAddressIndexKey(1, uint160(hashBytes), pindex->nHeight, i, hash, k, false), out.nValue)); |
|
|
|
|
|
|
|
// undo unspent index
|
|
|
|
addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, uint160(hashBytes), hash, k), CAddressUnspentValue())); |
|
|
|
|
|
|
|
} else { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Check that all outputs are available and match the outputs in the block itself
|
|
|
|
// exactly.
|
|
|
|
{ |
|
|
@ -1928,6 +2026,39 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex |
|
|
|
const CTxInUndo &undo = txundo.vprevout[j]; |
|
|
|
if (!ApplyTxInUndo(undo, view, out)) |
|
|
|
fClean = false; |
|
|
|
|
|
|
|
const CTxIn input = tx.vin[j]; |
|
|
|
|
|
|
|
if (fSpentIndex) { |
|
|
|
// undo and delete the spent index
|
|
|
|
spentIndex.push_back(make_pair(CSpentIndexKey(input.prevout.hash, input.prevout.n), CSpentIndexValue())); |
|
|
|
} |
|
|
|
|
|
|
|
if (fAddressIndex) { |
|
|
|
const CTxOut &prevout = view.GetOutputFor(tx.vin[j]); |
|
|
|
if (prevout.scriptPubKey.IsPayToScriptHash()) { |
|
|
|
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22); |
|
|
|
|
|
|
|
// undo spending activity
|
|
|
|
addressIndex.push_back(make_pair(CAddressIndexKey(2, uint160(hashBytes), pindex->nHeight, i, hash, j, true), prevout.nValue * -1)); |
|
|
|
|
|
|
|
// restore unspent index
|
|
|
|
addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(2, uint160(hashBytes), input.prevout.hash, input.prevout.n), CAddressUnspentValue(prevout.nValue, prevout.scriptPubKey, undo.nHeight))); |
|
|
|
|
|
|
|
|
|
|
|
} else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) { |
|
|
|
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23); |
|
|
|
|
|
|
|
// undo spending activity
|
|
|
|
addressIndex.push_back(make_pair(CAddressIndexKey(1, uint160(hashBytes), pindex->nHeight, i, hash, j, true), prevout.nValue * -1)); |
|
|
|
|
|
|
|
// restore unspent index
|
|
|
|
addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, uint160(hashBytes), input.prevout.hash, input.prevout.n), CAddressUnspentValue(prevout.nValue, prevout.scriptPubKey, undo.nHeight))); |
|
|
|
|
|
|
|
} else { |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -1943,6 +2074,15 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
if (fAddressIndex) { |
|
|
|
if (!pblocktree->EraseAddressIndex(addressIndex)) { |
|
|
|
return AbortNode(state, "Failed to delete address index"); |
|
|
|
} |
|
|
|
if (!pblocktree->UpdateAddressUnspentIndex(addressUnspentIndex)) { |
|
|
|
return AbortNode(state, "Failed to write address unspent index"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return fClean; |
|
|
|
} |
|
|
|
|
|
|
|