diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 829c8ee2d..725037add 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -256,6 +256,8 @@ static const CRPCCommand vRPCCommands[] = { "decoderawtransaction", &decoderawtransaction, false, false }, { "signrawtransaction", &signrawtransaction, false, false }, { "sendrawtransaction", &sendrawtransaction, false, false }, + { "gettxoutsetinfo", &gettxoutsetinfo, true, false }, + { "gettxout", &gettxout, true, false }, }; CRPCTable::CRPCTable() @@ -1166,6 +1168,8 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector 1) ConvertTo(params[1]); if (strMethod == "signrawtransaction" && n > 1) ConvertTo(params[1], true); if (strMethod == "signrawtransaction" && n > 2) ConvertTo(params[2], true); + if (strMethod == "gettxout" && n > 1) ConvertTo(params[1]); + if (strMethod == "gettxout" && n > 2) ConvertTo(params[2]); return params; } diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h index 89ab976f5..929069766 100644 --- a/src/bitcoinrpc.h +++ b/src/bitcoinrpc.h @@ -187,5 +187,7 @@ extern json_spirit::Value settxfee(const json_spirit::Array& params, bool fHelp) extern json_spirit::Value getrawmempool(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getblockhash(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getblock(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value gettxoutsetinfo(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value gettxout(const json_spirit::Array& params, bool fHelp); #endif diff --git a/src/main.cpp b/src/main.cpp index 94f1a9315..064ac7e10 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -171,6 +171,7 @@ bool CCoinsView::HaveCoins(uint256 txid) { return false; } CBlockIndex *CCoinsView::GetBestBlock() { return NULL; } bool CCoinsView::SetBestBlock(CBlockIndex *pindex) { return false; } bool CCoinsView::BatchWrite(const std::map &mapCoins, CBlockIndex *pindex) { return false; } +bool CCoinsView::GetStats(CCoinsStats &stats) { return false; } CCoinsViewBacked::CCoinsViewBacked(CCoinsView &viewIn) : base(&viewIn) { } @@ -181,6 +182,7 @@ CBlockIndex *CCoinsViewBacked::GetBestBlock() { return base->GetBestBlock(); } bool CCoinsViewBacked::SetBestBlock(CBlockIndex *pindex) { return base->SetBestBlock(pindex); } void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } bool CCoinsViewBacked::BatchWrite(const std::map &mapCoins, CBlockIndex *pindex) { return base->BatchWrite(mapCoins, pindex); } +bool CCoinsViewBacked::GetStats(CCoinsStats &stats) { return base->GetStats(stats); } CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), pindexTip(NULL) { } diff --git a/src/main.h b/src/main.h index 1126feb09..75ee7a9be 100644 --- a/src/main.h +++ b/src/main.h @@ -1791,6 +1791,16 @@ public: extern CTxMemPool mempool; +struct CCoinsStats +{ + int nHeight; + uint64 nTransactions; + uint64 nTransactionOutputs; + uint64 nSerializedSize; + + CCoinsStats() : nHeight(0), nTransactions(0), nTransactionOutputs(0), nSerializedSize(0) {} +}; + /** Abstract view on the open txout dataset. */ class CCoinsView { @@ -1811,6 +1821,7 @@ public: // Modify the currently active block index virtual bool SetBestBlock(CBlockIndex *pindex); virtual bool BatchWrite(const std::map &mapCoins, CBlockIndex *pindex); + virtual bool GetStats(CCoinsStats &stats); }; /** CCoinsView backed by another CCoinsView */ @@ -1828,6 +1839,7 @@ public: bool SetBestBlock(CBlockIndex *pindex); void SetBackend(CCoinsView &viewIn); bool BatchWrite(const std::map &mapCoins, CBlockIndex *pindex); + bool GetStats(CCoinsStats &stats); }; /** CCoinsView that adds a memory cache for transactions to another CCoinsView */ diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 2dfdf5842..3fde463cd 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -159,7 +159,69 @@ Value getblock(const Array& params, bool fHelp) return blockToJSON(block, pblockindex); } +Value gettxoutsetinfo(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "gettxoutsetinfo\n" + "Returns statistics about the unspent transaction output set."); + + Object ret; + + CCoinsStats stats; + if (pcoinsTip->GetStats(stats)) { + ret.push_back(Pair("bestblock", pcoinsTip->GetBestBlock()->GetBlockHash().GetHex())); + ret.push_back(Pair("transactions", (boost::int64_t)stats.nTransactions)); + ret.push_back(Pair("txouts", (boost::int64_t)stats.nTransactionOutputs)); + ret.push_back(Pair("bytes_serialized", (boost::int64_t)stats.nSerializedSize)); + } + return ret; +} +Value gettxout(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 3) + throw runtime_error( + "gettxout [includemempool=true]\n" + "Returns details about an unspent transaction output."); + Object ret; + + std::string strHash = params[0].get_str(); + uint256 hash(strHash); + int n = params[1].get_int(); + bool fMempool = true; + if (params.size() > 2) + fMempool = params[2].get_bool(); + + CCoins coins; + if (fMempool) { + LOCK(mempool.cs); + CCoinsViewMemPool view(*pcoinsTip, mempool); + if (!view.GetCoins(hash, coins)) + return Value::null; + mempool.pruneSpent(hash, coins); // TODO: this should be done by the CCoinsViewMemPool + } else { + if (!pcoinsTip->GetCoins(hash, coins)) + return Value::null; + } + if (n<0 || (unsigned int)n>=coins.vout.size() || coins.vout[n].IsNull()) + return Value::null; + + ret.push_back(Pair("bestblock", pcoinsTip->GetBestBlock()->GetBlockHash().GetHex())); + if ((unsigned int)coins.nHeight == MEMPOOL_HEIGHT) + ret.push_back(Pair("confirmations", 0)); + else + ret.push_back(Pair("confirmations", pcoinsTip->GetBestBlock()->nHeight - coins.nHeight + 1)); + ret.push_back(Pair("amount", (boost::int64_t)coins.vout[n].nValue)); + Object o; + o.push_back(Pair("asm", coins.vout[n].scriptPubKey.ToString())); + o.push_back(Pair("hex", HexStr(coins.vout[n].scriptPubKey.begin(), coins.vout[n].scriptPubKey.end()))); + ret.push_back(Pair("scriptPubKey", o)); + ret.push_back(Pair("version", coins.nVersion)); + ret.push_back(Pair("coinbase", coins.fCoinBase)); + + return ret; +} diff --git a/src/txdb-leveldb.cpp b/src/txdb-leveldb.cpp index 4e2abe63d..7c72ccac0 100644 --- a/src/txdb-leveldb.cpp +++ b/src/txdb-leveldb.cpp @@ -97,6 +97,41 @@ bool CBlockTreeDB::ReadLastBlockFile(int &nFile) { return Read('l', nFile); } +bool CCoinsViewDB::GetStats(CCoinsStats &stats) { + leveldb::Iterator *pcursor = db.NewIterator(); + pcursor->SeekToFirst(); + + while (pcursor->Valid()) { + try { + leveldb::Slice slKey = pcursor->key(); + CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); + char chType; + ssKey >> chType; + if (chType == 'c' && !fRequestShutdown) { + 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; + + stats.nTransactions++; + BOOST_FOREACH(const CTxOut &out, coins.vout) { + if (!out.IsNull()) + stats.nTransactionOutputs++; + } + stats.nSerializedSize += 32 + slValue.size(); + } + pcursor->Next(); + } catch (std::exception &e) { + return error("%s() : deserialize error", __PRETTY_FUNCTION__); + } + } + delete pcursor; + stats.nHeight = GetBestBlock()->nHeight; + return true; +} + bool CBlockTreeDB::LoadBlockIndexGuts() { leveldb::Iterator *pcursor = NewIterator(); diff --git a/src/txdb-leveldb.h b/src/txdb-leveldb.h index 1254422cf..123ec00d2 100644 --- a/src/txdb-leveldb.h +++ b/src/txdb-leveldb.h @@ -22,6 +22,7 @@ public: CBlockIndex *GetBestBlock(); bool SetBestBlock(CBlockIndex *pindex); bool BatchWrite(const std::map &mapCoins, CBlockIndex *pindex); + bool GetStats(CCoinsStats &stats); }; /** Access to the block database (blktree/) */