Add a getutxos command to the p2p protocol. It allows querying of the UTXO set

given a set of outpoints.
This commit is contained in:
Mike Hearn 2014-05-19 22:25:17 +02:00
parent 36065cc529
commit da2ec100f3
4 changed files with 89 additions and 2 deletions

View file

@ -20,6 +20,7 @@
#include <sstream> #include <sstream>
#include <boost/dynamic_bitset.hpp>
#include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/replace.hpp>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp> #include <boost/filesystem/fstream.hpp>
@ -3512,6 +3513,75 @@ void static ProcessGetData(CNode* pfrom)
} }
} }
struct CCoin {
uint32_t nTxVer; // Don't call this nVersion, that name has a special meaning inside IMPLEMENT_SERIALIZE
uint32_t nHeight;
CTxOut out;
IMPLEMENT_SERIALIZE(
READWRITE(nTxVer);
READWRITE(nHeight);
READWRITE(out);
)
};
bool ProcessGetUTXOs(const vector<COutPoint> &vOutPoints, bool fCheckMemPool, vector<unsigned char> *result, vector<CCoin> *resultCoins)
{
// Defined by BIP 64.
//
// Allows a peer to retrieve the CTxOut structures corresponding to the given COutPoints.
// Note that this data is not authenticated by anything: this code could just invent any
// old rubbish and hand it back, with the peer being unable to tell unless they are checking
// the outpoints against some out of band data.
//
// Also the answer could change the moment after we give it. However some apps can tolerate
// this, because they're only using the result as a hint or are willing to trust the results
// based on something else. For example we may be a "trusted node" for the peer, or it may
// be checking the results given by several nodes for consistency, it may
// run the UTXOs returned against scriptSigs of transactions obtained elsewhere (after checking
// for a standard script form), and because the height in which the UTXO was defined is provided
// a client that has a map of heights to block headers (as SPV clients do, for recent blocks)
// can request the creating block via hash.
//
// IMPORTANT: Clients expect ordering to be preserved!
if (vOutPoints.size() > MAX_INV_SZ)
return error("message getutxos size() = %u", vOutPoints.size());
LogPrint("net", "getutxos for %d queries %s mempool\n", vOutPoints.size(), fCheckMemPool ? "with" : "without");
boost::dynamic_bitset<unsigned char> hits(vOutPoints.size());
{
LOCK2(cs_main, mempool.cs);
CCoinsViewMemPool cvMemPool(*pcoinsTip, mempool);
CCoinsViewCache view(fCheckMemPool ? cvMemPool : *pcoinsTip);
for (size_t i = 0; i < vOutPoints.size(); i++)
{
CCoins coins;
uint256 hash = vOutPoints[i].hash;
if (view.GetCoins(hash, coins))
{
mempool.pruneSpent(hash, coins);
if (coins.IsAvailable(vOutPoints[i].n))
{
hits[i] = true;
// Safe to index into vout here because IsAvailable checked if it's off the end of the array, or if
// n is valid but points to an already spent output (IsNull).
CCoin coin;
coin.nTxVer = coins.nVersion;
coin.nHeight = coins.nHeight;
coin.out = coins.vout.at(vOutPoints[i].n);
assert(!coin.out.IsNull());
resultCoins->push_back(coin);
}
}
}
}
boost::to_block_range(hits, std::back_inserter(*result));
return true;
}
bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived) bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived)
{ {
RandAddSeedPerfmon(); RandAddSeedPerfmon();
@ -3860,6 +3930,22 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
} }
else if (strCommand == "getutxos")
{
bool fCheckMemPool;
vector<COutPoint> vOutPoints;
vRecv >> fCheckMemPool;
vRecv >> vOutPoints;
vector<unsigned char> bitmap;
vector<CCoin> outs;
if (ProcessGetUTXOs(vOutPoints, fCheckMemPool, &bitmap, &outs))
pfrom->PushMessage("utxos", chainActive.Height(), chainActive.Tip()->GetBlockHash(), bitmap, outs);
else
Misbehaving(pfrom->GetId(), 20);
}
else if (strCommand == "tx") else if (strCommand == "tx")
{ {
vector<uint256> vWorkQueue; vector<uint256> vWorkQueue;

View file

@ -66,7 +66,7 @@ namespace {
// //
bool fDiscover = true; bool fDiscover = true;
bool fListen = true; bool fListen = true;
uint64_t nLocalServices = NODE_NETWORK; uint64_t nLocalServices = NODE_NETWORK | NODE_GETUTXOS;
CCriticalSection cs_mapLocalHost; CCriticalSection cs_mapLocalHost;
map<CNetAddr, LocalServiceInfo> mapLocalHost; map<CNetAddr, LocalServiceInfo> mapLocalHost;
static bool vfReachable[NET_MAX] = {}; static bool vfReachable[NET_MAX] = {};

View file

@ -64,6 +64,7 @@ class CMessageHeader
enum enum
{ {
NODE_NETWORK = (1 << 0), NODE_NETWORK = (1 << 0),
NODE_GETUTXOS = (1 << 1),
// Bits 24-31 are reserved for temporary experiments. Just pick a bit that // Bits 24-31 are reserved for temporary experiments. Just pick a bit that
// isn't getting used, or one not being used much, and notify the // isn't getting used, or one not being used much, and notify the

View file

@ -26,7 +26,7 @@ extern const std::string CLIENT_DATE;
// network protocol versioning // network protocol versioning
// //
static const int PROTOCOL_VERSION = 70002; static const int PROTOCOL_VERSION = 70003;
// initial proto version, to be increased after version/verack negotiation // initial proto version, to be increased after version/verack negotiation
static const int INIT_PROTO_VERSION = 209; static const int INIT_PROTO_VERSION = 209;