mirror of
https://github.com/dogecoin/dogecoin.git
synced 2026-02-13 00:38:44 +00:00
Merge pull request #1299 from rnicoll/1.10-dev-searchrawtransactions
Add searchrawtransactions support for Dogeparty
This commit is contained in:
commit
f41b19cbda
@ -1122,7 +1122,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache
|
||||
nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); // total cache cannot be greated than nMaxDbcache
|
||||
int64_t nBlockTreeDBCache = nTotalCache / 8;
|
||||
if (nBlockTreeDBCache > (1 << 21) && !GetBoolArg("-txindex", false))
|
||||
if (nBlockTreeDBCache > (1 << 21) && !GetBoolArg("-txindex", false) && !GetBoolArg("-addrindex", false))
|
||||
nBlockTreeDBCache = (1 << 21); // block tree db cache shouldn't be larger than 2 MiB
|
||||
nTotalCache -= nBlockTreeDBCache;
|
||||
int64_t nCoinDBCache = std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23)); // use 25%-50% of the remainder for disk cache
|
||||
@ -1190,6 +1190,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
break;
|
||||
}
|
||||
|
||||
// Check for changed -addrindex state
|
||||
if (fAddrIndex != GetBoolArg("-addrindex", false)) {
|
||||
strLoadError = _("You need to rebuild the database using -reindex to change -addrindex");
|
||||
break;
|
||||
}
|
||||
|
||||
uiInterface.InitMessage(_("Verifying blocks..."));
|
||||
if (fHavePruned && GetArg("-checkblocks", 288) > MIN_BLOCKS_TO_KEEP) {
|
||||
LogPrintf("Prune: pruned datadir may not have more than %d blocks; -checkblocks=%d may fail\n",
|
||||
|
||||
@ -43,6 +43,13 @@ static leveldb::Options GetOptions(size_t nCacheSize)
|
||||
return options;
|
||||
}
|
||||
|
||||
CLevelDBIterator::~CLevelDBIterator() { delete piter; }
|
||||
bool CLevelDBIterator::Valid() { return piter->Valid(); }
|
||||
void CLevelDBIterator::SeekToFirst() { piter->SeekToFirst(); }
|
||||
void CLevelDBIterator::SeekToLast() { piter->SeekToLast(); }
|
||||
void CLevelDBIterator::Next() { piter->Next(); }
|
||||
void CLevelDBIterator::Prev() { piter->Prev(); }
|
||||
|
||||
CLevelDBWrapper::CLevelDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory, bool fWipe)
|
||||
{
|
||||
penv = NULL;
|
||||
|
||||
@ -61,6 +61,63 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class CLevelDBIterator
|
||||
{
|
||||
private:
|
||||
leveldb::Iterator *piter;
|
||||
|
||||
public:
|
||||
CLevelDBIterator(leveldb::Iterator *piterIn) : piter(piterIn) {}
|
||||
~CLevelDBIterator();
|
||||
|
||||
bool Valid();
|
||||
|
||||
void SeekToFirst();
|
||||
void SeekToLast();
|
||||
|
||||
template<typename K> void Seek(const K& key) {
|
||||
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
||||
ssKey.reserve(ssKey.GetSerializeSize(key));
|
||||
ssKey << key;
|
||||
leveldb::Slice slKey(&ssKey[0], ssKey.size());
|
||||
piter->Seek(slKey);
|
||||
}
|
||||
|
||||
void Next();
|
||||
void Prev();
|
||||
|
||||
template<typename K> bool GetKey(K& key) {
|
||||
leveldb::Slice slKey = piter->key();
|
||||
try {
|
||||
CDataStream ssKey(slKey.data(), slKey.data() + slKey.size(), SER_DISK, CLIENT_VERSION);
|
||||
ssKey >> key;
|
||||
} catch(std::exception &e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int GetKeySize() {
|
||||
return piter->key().size();
|
||||
}
|
||||
|
||||
template<typename V> bool GetValue(V& value) {
|
||||
leveldb::Slice slValue = piter->value();
|
||||
try {
|
||||
CDataStream ssValue(slValue.data(), slValue.data() + slValue.size(), SER_DISK, CLIENT_VERSION);
|
||||
ssValue >> value;
|
||||
} catch(std::exception &e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int GetValueSize() {
|
||||
return piter->value().size();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class CLevelDBWrapper
|
||||
{
|
||||
private:
|
||||
@ -163,10 +220,8 @@ public:
|
||||
return WriteBatch(batch, true);
|
||||
}
|
||||
|
||||
// not exactly clean encapsulation, but it's easiest for now
|
||||
leveldb::Iterator* NewIterator()
|
||||
{
|
||||
return pdb->NewIterator(iteroptions);
|
||||
CLevelDBIterator *NewIterator() {
|
||||
return new CLevelDBIterator(pdb->NewIterator(iteroptions));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
123
src/main.cpp
123
src/main.cpp
@ -20,6 +20,7 @@
|
||||
#include "merkleblock.h"
|
||||
#include "net.h"
|
||||
#include "pow.h"
|
||||
#include "pubkey.h"
|
||||
#include "txdb.h"
|
||||
#include "txmempool.h"
|
||||
#include "ui_interface.h"
|
||||
@ -58,6 +59,7 @@ int nScriptCheckThreads = 0;
|
||||
bool fImporting = false;
|
||||
bool fReindex = false;
|
||||
bool fTxIndex = false;
|
||||
bool fAddrIndex = false;
|
||||
bool fHavePruned = false;
|
||||
bool fPruneMode = false;
|
||||
bool fIsBareMultisigStd = true;
|
||||
@ -1070,6 +1072,45 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadTransaction(CTransaction& txOut, const CDiskTxPos &pos, uint256 &hashBlock) {
|
||||
CAutoFile file(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION);
|
||||
if (file.IsNull())
|
||||
return error("%s: OpenBlockFile failed", __func__);
|
||||
CBlockHeader header;
|
||||
try {
|
||||
file >> header;
|
||||
fseek(file.Get(), pos.nTxOffset, SEEK_CUR);
|
||||
file >> txOut;
|
||||
} catch (std::exception &e) {
|
||||
return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__);
|
||||
}
|
||||
hashBlock = header.GetHash();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FindTransactionsByDestination(const CTxDestination &dest, std::set<CExtDiskTxPos> &setpos) {
|
||||
uint160 addrid;
|
||||
const CKeyID *pkeyid = boost::get<CKeyID>(&dest);
|
||||
if (pkeyid) {
|
||||
addrid = static_cast<uint160>(*pkeyid);
|
||||
} else {
|
||||
const CScriptID *pscriptid = boost::get<CScriptID>(&dest);
|
||||
if (pscriptid)
|
||||
addrid = static_cast<uint160>(*pscriptid);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
LOCK(cs_main);
|
||||
if (!fAddrIndex)
|
||||
return false;
|
||||
std::vector<CExtDiskTxPos> vPos;
|
||||
if (!pblocktree->ReadAddrIndex(addrid, vPos))
|
||||
return false;
|
||||
setpos.insert(vPos.begin(), vPos.end());
|
||||
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)
|
||||
{
|
||||
@ -1086,18 +1127,8 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock
|
||||
if (fTxIndex) {
|
||||
CDiskTxPos postx;
|
||||
if (pblocktree->ReadTxIndex(hash, postx)) {
|
||||
CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
|
||||
if (file.IsNull())
|
||||
return error("%s: OpenBlockFile failed", __func__);
|
||||
CBlockHeader header;
|
||||
try {
|
||||
file >> header;
|
||||
fseek(file.Get(), postx.nTxOffset, SEEK_CUR);
|
||||
file >> txOut;
|
||||
} catch (const std::exception& e) {
|
||||
return error("%s: Deserialize or I/O error - %s", __func__, e.what());
|
||||
}
|
||||
hashBlock = header.GetHash();
|
||||
if (!ReadTransaction(txOut, postx, hashBlock))
|
||||
return false;
|
||||
if (txOut.GetHash() != hash)
|
||||
return error("%s: txid mismatch", __func__);
|
||||
return true;
|
||||
@ -1804,6 +1835,32 @@ static int64_t nTimeIndex = 0;
|
||||
static int64_t nTimeCallbacks = 0;
|
||||
static int64_t nTimeTotal = 0;
|
||||
|
||||
// Index either: a) every data push >=8 bytes, b) if no such pushes, the entire script
|
||||
void static BuildAddrIndex(const CScript &script, const CExtDiskTxPos &pos, std::vector<std::pair<uint160, CExtDiskTxPos> > &out) {
|
||||
CScript::const_iterator pc = script.begin();
|
||||
CScript::const_iterator pend = script.end();
|
||||
std::vector<unsigned char> data;
|
||||
opcodetype opcode;
|
||||
bool fHaveData = false;
|
||||
while (pc < pend) {
|
||||
script.GetOp(pc, opcode, data);
|
||||
if (0 <= opcode && opcode <= OP_PUSHDATA4 && data.size() >= 8) { // data element
|
||||
uint160 addrid;
|
||||
if (data.size() <= 20) {
|
||||
memcpy(&addrid, &data[0], data.size());
|
||||
} else {
|
||||
addrid = Hash160(data);
|
||||
}
|
||||
out.push_back(std::make_pair(addrid, pos));
|
||||
fHaveData = true;
|
||||
}
|
||||
}
|
||||
if (!fHaveData) {
|
||||
uint160 addrid = Hash160(script);
|
||||
out.push_back(std::make_pair(addrid, pos));
|
||||
}
|
||||
}
|
||||
|
||||
bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck)
|
||||
{
|
||||
const CChainParams& chainparams = Params();
|
||||
@ -1868,11 +1925,18 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||
CAmount nFees = 0;
|
||||
int nInputs = 0;
|
||||
unsigned int nSigOps = 0;
|
||||
CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size()));
|
||||
std::vector<std::pair<uint256, CDiskTxPos> > vPos;
|
||||
vPos.reserve(block.vtx.size());
|
||||
blockundo.vtxundo.reserve(block.vtx.size() - 1);
|
||||
for (unsigned int i = 0; i < block.vtx.size(); i++)
|
||||
CExtDiskTxPos pos(CDiskTxPos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())), pindex->nHeight);
|
||||
std::vector<std::pair<uint256, CDiskTxPos> > vPosTxid;
|
||||
std::vector<std::pair<uint160, CExtDiskTxPos> > vPosAddrid;
|
||||
if (fTxIndex) {
|
||||
vPosTxid.reserve(block.vtx.size());
|
||||
blockundo.vtxundo.reserve(block.vtx.size() - 1);
|
||||
}
|
||||
if (fAddrIndex) {
|
||||
vPosAddrid.reserve(4*block.vtx.size());
|
||||
// We don't keep undo information, as we never remove address indexes
|
||||
}
|
||||
for (unsigned int i=0; i<block.vtx.size(); i++)
|
||||
{
|
||||
const CTransaction &tx = block.vtx[i];
|
||||
|
||||
@ -1906,13 +1970,26 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||
control.Add(vChecks);
|
||||
}
|
||||
|
||||
if (fTxIndex)
|
||||
vPosTxid.push_back(std::make_pair(block.vtx[i].GetHash(), pos));
|
||||
if (fAddrIndex) {
|
||||
if (!tx.IsCoinBase()) {
|
||||
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
|
||||
CCoins coins;
|
||||
view.GetCoins(txin.prevout.hash, coins);
|
||||
BuildAddrIndex(coins.vout[txin.prevout.n].scriptPubKey, pos, vPosAddrid);
|
||||
}
|
||||
}
|
||||
BOOST_FOREACH(const CTxOut &txout, tx.vout)
|
||||
BuildAddrIndex(txout.scriptPubKey, pos, vPosAddrid);
|
||||
}
|
||||
|
||||
CTxUndo undoDummy;
|
||||
if (i > 0) {
|
||||
blockundo.vtxundo.push_back(CTxUndo());
|
||||
}
|
||||
UpdateCoins(tx, state, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight);
|
||||
|
||||
vPos.push_back(std::make_pair(tx.GetHash(), pos));
|
||||
pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
|
||||
}
|
||||
int64_t nTime1 = GetTimeMicros(); nTimeConnect += nTime1 - nTimeStart;
|
||||
@ -1953,8 +2030,11 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||
}
|
||||
|
||||
if (fTxIndex)
|
||||
if (!pblocktree->WriteTxIndex(vPos))
|
||||
if (!pblocktree->WriteTxIndex(vPosTxid))
|
||||
return AbortNode(state, "Failed to write transaction index");
|
||||
if (fAddrIndex)
|
||||
if (!pblocktree->AddAddrIndex(vPosAddrid))
|
||||
return AbortNode(state, "Failed to write address index");
|
||||
|
||||
// add this block to the view's block chain
|
||||
view.SetBestBlock(pindex->GetBlockHash());
|
||||
@ -3277,6 +3357,9 @@ bool static LoadBlockIndexDB()
|
||||
pblocktree->ReadFlag("txindex", fTxIndex);
|
||||
LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled");
|
||||
|
||||
pblocktree->ReadFlag("addrindex", fAddrIndex);
|
||||
LogPrintf("%s: address index %s\n", __func__, fAddrIndex ? "enabled" : "disabled");
|
||||
|
||||
// Load pointer to end of best chain
|
||||
BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock());
|
||||
if (it == mapBlockIndex.end())
|
||||
@ -3430,6 +3513,8 @@ bool InitBlockIndex() {
|
||||
// Use the provided setting for -txindex in the new database
|
||||
fTxIndex = GetBoolArg("-txindex", false);
|
||||
pblocktree->WriteFlag("txindex", fTxIndex);
|
||||
fAddrIndex = GetBoolArg("-addrindex", false);
|
||||
pblocktree->WriteFlag("addrindex", fAddrIndex);
|
||||
LogPrintf("Initializing databases...\n");
|
||||
|
||||
// Only add the genesis block if not reindexing (in which case we reuse the one already on disk)
|
||||
|
||||
53
src/main.h
53
src/main.h
@ -110,6 +110,7 @@ extern bool fImporting;
|
||||
extern bool fReindex;
|
||||
extern int nScriptCheckThreads;
|
||||
extern bool fTxIndex;
|
||||
extern bool fAddrIndex;
|
||||
extern bool fIsBareMultisigStd;
|
||||
extern bool fCheckBlockIndex;
|
||||
extern bool fCheckpointsEnabled;
|
||||
@ -268,8 +269,58 @@ struct CDiskTxPos : public CDiskBlockPos
|
||||
CDiskBlockPos::SetNull();
|
||||
nTxOffset = 0;
|
||||
}
|
||||
|
||||
friend bool operator==(const CDiskTxPos &a, const CDiskTxPos &b) {
|
||||
return (a.nFile == b.nFile && a.nPos == b.nPos && a.nTxOffset == b.nTxOffset);
|
||||
}
|
||||
|
||||
friend bool operator!=(const CDiskTxPos &a, const CDiskTxPos &b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
friend bool operator<(const CDiskTxPos &a, const CDiskTxPos &b) {
|
||||
return (a.nFile < b.nFile || (
|
||||
(a.nFile == b.nFile) && (a.nPos < b.nPos || (
|
||||
(a.nPos == b.nPos) && (a.nTxOffset < b.nTxOffset)))));
|
||||
}
|
||||
};
|
||||
|
||||
struct CExtDiskTxPos : public CDiskTxPos
|
||||
{
|
||||
unsigned int nHeight;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
READWRITE(*(CDiskTxPos*)this);
|
||||
READWRITE(VARINT(nHeight));
|
||||
}
|
||||
|
||||
CExtDiskTxPos(const CDiskTxPos &pos, int nHeightIn) : CDiskTxPos(pos), nHeight(nHeightIn) {
|
||||
}
|
||||
|
||||
CExtDiskTxPos() {
|
||||
SetNull();
|
||||
}
|
||||
|
||||
void SetNull() {
|
||||
CDiskTxPos::SetNull();
|
||||
nHeight = 0;
|
||||
}
|
||||
|
||||
friend bool operator==(const CExtDiskTxPos &a, const CExtDiskTxPos &b) {
|
||||
return (a.nHeight == b.nHeight && a.nFile == b.nFile && a.nPos == b.nPos && a.nTxOffset == b.nTxOffset);
|
||||
}
|
||||
|
||||
friend bool operator!=(const CExtDiskTxPos &a, const CExtDiskTxPos &b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
friend bool operator<(const CExtDiskTxPos &a, const CExtDiskTxPos &b) {
|
||||
if (a.nHeight < b.nHeight) return true;
|
||||
if (a.nHeight > b.nHeight) return false;
|
||||
return ((const CDiskTxPos)a < (const CDiskTxPos)b);
|
||||
}
|
||||
};
|
||||
|
||||
CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree);
|
||||
|
||||
@ -378,8 +429,10 @@ public:
|
||||
|
||||
/** Functions for disk access for blocks */
|
||||
bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart);
|
||||
bool ReadTransaction(CTransaction& tx, const CDiskTxPos &pos, uint256 &hashBlock);
|
||||
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex);
|
||||
bool ReadBlockHeaderFromDisk(CBlockHeader& block, const CBlockIndex* pindex);
|
||||
bool FindTransactionsByDestination(const CTxDestination &dest, std::set<CExtDiskTxPos> &setpos);
|
||||
|
||||
|
||||
/** Functions for validating blocks and updating the block tree */
|
||||
|
||||
@ -71,6 +71,9 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
||||
{ "getblock", 1 },
|
||||
{ "gettransaction", 1 },
|
||||
{ "getrawtransaction", 1 },
|
||||
{ "searchrawtransactions", 1 },
|
||||
{ "searchrawtransactions", 2 },
|
||||
{ "searchrawtransactions", 3 },
|
||||
{ "createrawtransaction", 0 },
|
||||
{ "createrawtransaction", 1 },
|
||||
{ "signrawtransaction", 1 },
|
||||
|
||||
@ -106,6 +106,66 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
|
||||
}
|
||||
}
|
||||
|
||||
Value searchrawtransactions(const Array ¶ms, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() < 1 || params.size() > 4)
|
||||
throw runtime_error(
|
||||
"searchrawtransactions <address> [verbose=1] [skip=0] [count=100]\n");
|
||||
|
||||
if (!fAddrIndex)
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "Address index not enabled");
|
||||
|
||||
CBitcoinAddress address(params[0].get_str());
|
||||
if (!address.IsValid())
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
|
||||
CTxDestination dest = address.Get();
|
||||
|
||||
std::set<CExtDiskTxPos> setpos;
|
||||
if (!FindTransactionsByDestination(dest, setpos))
|
||||
throw JSONRPCError(RPC_DATABASE_ERROR, "Cannot search for address");
|
||||
|
||||
int nSkip = 0;
|
||||
int nCount = 100;
|
||||
bool fVerbose = true;
|
||||
if (params.size() > 1)
|
||||
fVerbose = (params[1].get_int() != 0);
|
||||
if (params.size() > 2)
|
||||
nSkip = params[2].get_int();
|
||||
if (params.size() > 3)
|
||||
nCount = params[3].get_int();
|
||||
|
||||
if (nSkip < 0)
|
||||
nSkip += setpos.size();
|
||||
if (nSkip < 0)
|
||||
nSkip = 0;
|
||||
if (nCount < 0)
|
||||
nCount = 0;
|
||||
|
||||
std::set<CExtDiskTxPos>::const_iterator it = setpos.begin();
|
||||
while (it != setpos.end() && nSkip--) it++;
|
||||
|
||||
Array result;
|
||||
while (it != setpos.end() && nCount--) {
|
||||
CTransaction tx;
|
||||
uint256 hashBlock;
|
||||
if (!ReadTransaction(tx, *it, hashBlock))
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Cannot read transaction from disk");
|
||||
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssTx << tx;
|
||||
string strHex = HexStr(ssTx.begin(), ssTx.end());
|
||||
if (fVerbose) {
|
||||
Object object;
|
||||
TxToJSON(tx, hashBlock, object);
|
||||
object.push_back(Pair("hex", strHex));
|
||||
result.push_back(object);
|
||||
} else {
|
||||
result.push_back(strHex);
|
||||
}
|
||||
it++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Value getrawtransaction(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() < 1 || params.size() > 2)
|
||||
|
||||
@ -328,6 +328,7 @@ static const CRPCCommand vRPCCommands[] =
|
||||
{ "rawtransactions", "decoderawtransaction", &decoderawtransaction, true },
|
||||
{ "rawtransactions", "decodescript", &decodescript, true },
|
||||
{ "rawtransactions", "getrawtransaction", &getrawtransaction, true },
|
||||
{ "rawtransactions", "searchrawtransactions", &searchrawtransactions, true },
|
||||
{ "rawtransactions", "sendrawtransaction", &sendrawtransaction, false },
|
||||
{ "rawtransactions", "signrawtransaction", &signrawtransaction, false }, /* uses wallet if enabled */
|
||||
|
||||
|
||||
@ -213,6 +213,7 @@ extern json_spirit::Value setmocktime(const json_spirit::Array& params, bool fHe
|
||||
extern json_spirit::Value resendwallettransactions(const json_spirit::Array& params, bool fHelp);
|
||||
|
||||
extern json_spirit::Value getrawtransaction(const json_spirit::Array& params, bool fHelp); // in rcprawtransaction.cpp
|
||||
extern json_spirit::Value searchrawtransactions(const json_spirit::Array& params, bool fHelp);
|
||||
extern json_spirit::Value listunspent(const json_spirit::Array& params, bool fHelp);
|
||||
extern json_spirit::Value lockunspent(const json_spirit::Array& params, bool fHelp);
|
||||
extern json_spirit::Value listlockunspent(const json_spirit::Array& params, bool fHelp);
|
||||
|
||||
101
src/txdb.cpp
101
src/txdb.cpp
@ -80,6 +80,10 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) {
|
||||
}
|
||||
|
||||
CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) {
|
||||
if (!Read('S', salt)) {
|
||||
salt = GetRandHash();
|
||||
Write('S', salt);
|
||||
}
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) {
|
||||
@ -106,8 +110,8 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) const {
|
||||
/* It seems that there are no "const iterators" for LevelDB. Since we
|
||||
only need read operations on it, use a const-cast to get around
|
||||
that restriction. */
|
||||
boost::scoped_ptr<leveldb::Iterator> pcursor(const_cast<CLevelDBWrapper*>(&db)->NewIterator());
|
||||
pcursor->SeekToFirst();
|
||||
boost::scoped_ptr<CLevelDBIterator> pcursor(const_cast<CLevelDBWrapper*>(&db)->NewIterator());
|
||||
pcursor->Seek('c');
|
||||
|
||||
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
|
||||
stats.hashBlock = GetBestBlock();
|
||||
@ -115,22 +119,10 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) const {
|
||||
arith_uint256 nTotalAmount = 0;
|
||||
while (pcursor->Valid()) {
|
||||
boost::this_thread::interruption_point();
|
||||
try {
|
||||
leveldb::Slice slKey = pcursor->key();
|
||||
CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION);
|
||||
char chType;
|
||||
ssKey >> chType;
|
||||
if (chType == DB_COINS) {
|
||||
leveldb::Slice slValue = pcursor->value();
|
||||
CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION);
|
||||
CCoins coins;
|
||||
ssValue >> coins;
|
||||
uint256 txhash;
|
||||
ssKey >> txhash;
|
||||
ss << txhash;
|
||||
ss << VARINT(coins.nVersion);
|
||||
ss << (coins.fCoinBase ? 'c' : 'n');
|
||||
ss << VARINT(coins.nHeight);
|
||||
std::pair<char, uint256> key;
|
||||
CCoins coins;
|
||||
if (pcursor->GetKey(key) && key.first == DB_COINS) {
|
||||
if (pcursor->GetValue(coins)) {
|
||||
stats.nTransactions++;
|
||||
for (unsigned int i=0; i<coins.vout.size(); i++) {
|
||||
const CTxOut &out = coins.vout[i];
|
||||
@ -141,13 +133,15 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) const {
|
||||
nTotalAmount += out.nValue;
|
||||
}
|
||||
}
|
||||
stats.nSerializedSize += 32 + slValue.size();
|
||||
stats.nSerializedSize += 32 + pcursor->GetKeySize();
|
||||
ss << VARINT(0);
|
||||
} else {
|
||||
return error("CCoinsViewDB::GetStats() : unable to read value");
|
||||
}
|
||||
pcursor->Next();
|
||||
} catch (const std::exception& e) {
|
||||
return error("%s: Deserialize or I/O error - %s", __func__, e.what());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
pcursor->Next();
|
||||
}
|
||||
stats.nHeight = mapBlockIndex.find(GetBestBlock())->second->nHeight;
|
||||
stats.hashSerialized = ss.GetHash();
|
||||
@ -178,6 +172,42 @@ bool CBlockTreeDB::WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos>
|
||||
return WriteBatch(batch);
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::ReadAddrIndex(const uint160 &addrid, std::vector<CExtDiskTxPos> &list) {
|
||||
CLevelDBIterator *iter = NewIterator();
|
||||
uint64_t lookupid;
|
||||
{
|
||||
CHashWriter ss(SER_GETHASH, 0);
|
||||
ss << salt;
|
||||
ss << addrid;
|
||||
lookupid = UintToArith256(ss.GetHash()).GetLow64();
|
||||
}
|
||||
iter->Seek(make_pair('a', lookupid));
|
||||
while (iter->Valid()) {
|
||||
std::pair<std::pair<char, uint64_t>, CExtDiskTxPos> key;
|
||||
if (iter->GetKey(key) && key.first.first == 'a' && key.first.second == lookupid) {
|
||||
list.push_back(key.second);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
iter->Next();
|
||||
}
|
||||
|
||||
delete iter;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::AddAddrIndex(const std::vector<std::pair<uint160, CExtDiskTxPos> > &list) {
|
||||
unsigned char foo[0];
|
||||
CLevelDBBatch batch;
|
||||
for (std::vector<std::pair<uint160, CExtDiskTxPos> >::const_iterator it=list.begin(); it!=list.end(); it++) {
|
||||
CHashWriter ss(SER_GETHASH, 0);
|
||||
ss << salt;
|
||||
ss << it->first;
|
||||
batch.Write(make_pair(make_pair('a', UintToArith256(ss.GetHash()).GetLow64()), it->second), FLATDATA(foo));
|
||||
}
|
||||
return WriteBatch(batch);
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) {
|
||||
return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0');
|
||||
}
|
||||
@ -192,26 +222,17 @@ bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) {
|
||||
|
||||
bool CBlockTreeDB::LoadBlockIndexGuts()
|
||||
{
|
||||
boost::scoped_ptr<leveldb::Iterator> pcursor(NewIterator());
|
||||
boost::scoped_ptr<CLevelDBIterator> pcursor(NewIterator());
|
||||
|
||||
CDataStream ssKeySet(SER_DISK, CLIENT_VERSION);
|
||||
ssKeySet << make_pair(DB_BLOCK_INDEX, uint256());
|
||||
pcursor->Seek(ssKeySet.str());
|
||||
pcursor->Seek(make_pair(DB_BLOCK_INDEX, uint256()));
|
||||
|
||||
// Load mapBlockIndex
|
||||
while (pcursor->Valid()) {
|
||||
boost::this_thread::interruption_point();
|
||||
try {
|
||||
leveldb::Slice slKey = pcursor->key();
|
||||
CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION);
|
||||
char chType;
|
||||
ssKey >> chType;
|
||||
if (chType == DB_BLOCK_INDEX) {
|
||||
leveldb::Slice slValue = pcursor->value();
|
||||
CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION);
|
||||
CDiskBlockIndex diskindex;
|
||||
ssValue >> diskindex;
|
||||
|
||||
std::pair<char, uint256> key;
|
||||
if (pcursor->GetKey(key) && key.first == DB_BLOCK_INDEX) {
|
||||
CDiskBlockIndex diskindex;
|
||||
if (pcursor->GetValue(diskindex)) {
|
||||
// Construct block index object
|
||||
CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash());
|
||||
pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev);
|
||||
@ -240,10 +261,10 @@ bool CBlockTreeDB::LoadBlockIndexGuts()
|
||||
|
||||
pcursor->Next();
|
||||
} else {
|
||||
break; // if shutdown requested or finished loading block index
|
||||
return error("LoadBlockIndex() : failed to read value");
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
return error("%s: Deserialize or I/O error - %s", __func__, e.what());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
class CBlockFileInfo;
|
||||
class CBlockIndex;
|
||||
struct CDiskTxPos;
|
||||
struct CExtDiskTxPos;
|
||||
class uint256;
|
||||
|
||||
//! -dbcache default (MiB)
|
||||
@ -47,6 +48,7 @@ class CBlockTreeDB : public CLevelDBWrapper
|
||||
public:
|
||||
CBlockTreeDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false);
|
||||
private:
|
||||
uint256 salt;
|
||||
CBlockTreeDB(const CBlockTreeDB&);
|
||||
void operator=(const CBlockTreeDB&);
|
||||
public:
|
||||
@ -57,6 +59,8 @@ public:
|
||||
bool ReadReindexing(bool &fReindex);
|
||||
bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos);
|
||||
bool WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> > &list);
|
||||
bool ReadAddrIndex(const uint160 &addrid, std::vector<CExtDiskTxPos> &list);
|
||||
bool AddAddrIndex(const std::vector<std::pair<uint160, CExtDiskTxPos> > &list);
|
||||
bool WriteFlag(const std::string &name, bool fValue);
|
||||
bool ReadFlag(const std::string &name, bool &fValue);
|
||||
bool LoadBlockIndexGuts();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user