diff -uNr a/bitcoin/manifest b/bitcoin/manifest --- a/bitcoin/manifest a1eae616970b1886043d5fd2d8477d5f02f08c97d0ae65c3537e4267c1981470bdb501a806b3c05941a6fdf2b42d73188dc743c47578254b5b2750d0261d721b +++ b/bitcoin/manifest 8e13fc151b01d30d5551cf8b587081009982dd47302aa33b98bdd65d75f9ab46a9f414cca26bdaa031db3283c907c0e19bfb83e233217f8e1b0ef88a05cfef91 @@ -45,3 +45,4 @@ 732509 bitcoin_drop_online_build jfw Remove code and comments pertaining to the ONLINE=1 option which fetched dependency libs from deedbot, and upgrade their manifest hashes from sha512sum to keksum format. 735223 bitcoin_response_size_limits_1 jfw Simple adjustments to prepare the code for more significant ones to improve response size limiting at the source. CBlockLocator::GetBlockIndex never returns NULL unless something is very wrong. Don't log when the getblocks response stops at the requested hash: it's uninteresting since that hash was already logged just before, and this allows simplifying loop bounds. Differentiate getheaders implementation between the null locator (single block) case and the normal loop. Always log starting heights for getblocks/getheaders rather than punting to -1 just because a non-null pointer wasn't immediately available. 735223 bitcoin_response_size_limits_2 jfw getblocks: switch from nonsensical block count based limit (which didn't really limit at all) and heavy-weight yet still inexact bytes based limit to a simple conservative bound. getheaders: switch from nonsensical limit (which didn't really limit at all) to a sending buffer size derived limit to prevent flooding or at least give peer the chance to do so. +735324 bitcoin_pushmessage_cleanup_1 jfw Tame the PushMessage template explosion by pushing CDataStream serialization out to callers. Permanently disable undocumented -allowreceivebyip and otherwise dead code turned up when auditing PushMessage callers, namely generic network request tracking and publish/subscribe infrastructure. Simplify initialization of vSend/vRecv fields: type (since SER_NETWORK is the default) and version (since the indicated flag day is long past; it wasn't quite clear to remove the field altogether). Add unit test for assumption noted in one of the PushMessage calls. diff -uNr a/bitcoin/src/main.cpp b/bitcoin/src/main.cpp --- a/bitcoin/src/main.cpp 00dc0778ec8886372de6a2f33a914f4e5d0360d7bb690b242f770f259ac562bfd3f663c9bd554f911a0b935522ee1377ffd0eaef0c65d525ae17ebd60a9a23ed +++ b/bitcoin/src/main.cpp 5b796065e6bdfce53478af230763776d8342f0da923744cc8f76b9802ad8d01f6bdde0149acc784491130d3033662c9d1602bde39ec213c958cd788cdc3ffef6 @@ -1679,7 +1679,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { - static map > mapReuseKey; RandAddSeedPerfmon(); if (fDebug) { printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); @@ -1911,7 +1910,7 @@ { CBlock block; block.ReadFromDisk((*mi).second); - pfrom->PushMessage("block", block); + pfrom->PushMessage("block", pfrom->SendingStream() << block); // Trigger them to send a getblocks request for the next batch of inventory if (inv.hash == pfrom->hashContinue) @@ -1921,7 +1920,7 @@ // wait for other stuff first. vector vInv; vInv.push_back(CInv(MSG_BLOCK, hashBestChain)); - pfrom->PushMessage("inv", vInv); + pfrom->PushMessage("inv", pfrom->SendingStream() << vInv); pfrom->hashContinue = 0; } } @@ -1933,6 +1932,7 @@ { map::iterator mi = mapRelay.find(inv); if (mi != mapRelay.end()) + // It's OK to pass the existing CDataStream directly. With the prior family of PushMessage templates it got re-serialized (<<) into vSend while now it's appended (+=), but these are equivalent as per the "Special case" in serialize.h. pfrom->PushMessage(inv.GetCommand(), (*mi).second); } } @@ -2025,7 +2025,7 @@ break; } } - pfrom->PushMessage("headers", vHeaders); + pfrom->PushMessage("headers", pfrom->SendingStream() << vHeaders); } @@ -2101,45 +2101,8 @@ uint256 hashReply; vRecv >> hashReply; - if (!GetBoolArg("-allowreceivebyip")) - { - pfrom->PushMessage("reply", hashReply, (int)2, string("")); - return true; - } - - CWalletTx order; - vRecv >> order; - - /// we have a chance to check the order here - - // Keep giving the same key to the same ip until they use it - if (!mapReuseKey.count(pfrom->addr.ip)) - pwalletMain->GetKeyFromPool(mapReuseKey[pfrom->addr.ip], true); - - // Send back approval of order and pubkey to use - CScript scriptPubKey; - scriptPubKey << mapReuseKey[pfrom->addr.ip] << OP_CHECKSIG; - pfrom->PushMessage("reply", hashReply, (int)0, scriptPubKey); - } - - - else if (strCommand == "reply") - { - uint256 hashReply; - vRecv >> hashReply; - - CRequestTracker tracker; - CRITICAL_BLOCK(pfrom->cs_mapRequests) - { - map::iterator mi = pfrom->mapRequests.find(hashReply); - if (mi != pfrom->mapRequests.end()) - { - tracker = (*mi).second; - pfrom->mapRequests.erase(mi); - } - } - if (!tracker.IsNull()) - tracker.fn(tracker.param1, vRecv); + // receive-by-IP feature permanently disabled + pfrom->PushMessage("reply", pfrom->SendingStream() << hashReply << (int)2 << string("")); } @@ -2367,14 +2330,14 @@ // receiver rejects addr messages larger than 1000 if (vAddr.size() >= 1000) { - pto->PushMessage("addr", vAddr); + pto->PushMessage("addr", pto->SendingStream() << vAddr); vAddr.clear(); } } } pto->vAddrToSend.clear(); if (!vAddr.empty()) - pto->PushMessage("addr", vAddr); + pto->PushMessage("addr", pto->SendingStream() << vAddr); } @@ -2425,7 +2388,7 @@ vInv.push_back(inv); if (vInv.size() >= 1000) { - pto->PushMessage("inv", vInv); + pto->PushMessage("inv", pto->SendingStream() << vInv); vInv.clear(); } } @@ -2433,7 +2396,7 @@ pto->vInventoryToSend = vInvWait; } if (!vInv.empty()) - pto->PushMessage("inv", vInv); + pto->PushMessage("inv", pto->SendingStream() << vInv); // @@ -2451,7 +2414,7 @@ vGetData.push_back(inv); if (vGetData.size() >= 1000) { - pto->PushMessage("getdata", vGetData); + pto->PushMessage("getdata", pto->SendingStream() << vGetData); vGetData.clear(); } } @@ -2459,7 +2422,7 @@ pto->mapAskFor.erase(pto->mapAskFor.begin()); } if (!vGetData.empty()) - pto->PushMessage("getdata", vGetData); + pto->PushMessage("getdata", pto->SendingStream() << vGetData); } return true; diff -uNr a/bitcoin/src/main.h b/bitcoin/src/main.h --- a/bitcoin/src/main.h e1aa413b33f7ec0c3943aae831a5a2fc3795360707d44a31ebb5f810300d8b17ca1dbfd5ddc7785f02366f857694d0eb2746aa27efdd2d40b220387d2baa88ca +++ b/bitcoin/src/main.h d70c7dd93b2bea85ffe52d440b60d629c4cf125d9ae91afef3aa6c854981c19070663d3a8839455b700e410435634f59fb3ccaf7642595e4374c4bb323261267 @@ -23,7 +23,6 @@ class CAddress; class CInv; -class CRequestTracker; class CNode; class CBlockIndex; diff -uNr a/bitcoin/src/net.cpp b/bitcoin/src/net.cpp --- a/bitcoin/src/net.cpp 6d7c7634cce09792942fc69cf945b64e8f8e77b496ad6bbaed12dccbc5e13c31fc2f1162735cae7951893fb6b3635955703059b1e8ba1607cc483c494c0a126c +++ b/bitcoin/src/net.cpp b5ef3c960b24f41ac4cac25eef4dd17f6da3b10d7d404b8048dd30519e994908b11c64f94115cd55a732164c15c4b62ff5f454fe771a5843a7474f239d6f87ee @@ -59,7 +59,7 @@ void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) { - PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd); + PushMessage("getblocks", SendingStream() << CBlockLocator(pindexBegin) << hashEnd); } @@ -319,111 +319,6 @@ } } - - - - -void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1) -{ - // If the dialog might get closed before the reply comes back, - // call this in the destructor so it doesn't get called after it's deleted. - CRITICAL_BLOCK(cs_vNodes) - { - BOOST_FOREACH(CNode* pnode, vNodes) - { - CRITICAL_BLOCK(pnode->cs_mapRequests) - { - for (map::iterator mi = pnode->mapRequests.begin(); mi != pnode->mapRequests.end();) - { - CRequestTracker& tracker = (*mi).second; - if (tracker.fn == fn && tracker.param1 == param1) - pnode->mapRequests.erase(mi++); - else - mi++; - } - } - } - } -} - - - - - - - -// -// Subscription methods for the broadcast and subscription system. -// Channel numbers are message numbers, i.e. MSG_TABLE and MSG_PRODUCT. -// -// The subscription system uses a meet-in-the-middle strategy. -// With 100,000 nodes, if senders broadcast to 1000 random nodes and receivers -// subscribe to 1000 random nodes, 99.995% (1 - 0.99^1000) of messages will get through. -// - -bool AnySubscribed(unsigned int nChannel) -{ - if (pnodeLocalHost->IsSubscribed(nChannel)) - return true; - CRITICAL_BLOCK(cs_vNodes) - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode->IsSubscribed(nChannel)) - return true; - return false; -} - -bool CNode::IsSubscribed(unsigned int nChannel) -{ - if (nChannel >= vfSubscribe.size()) - return false; - return vfSubscribe[nChannel]; -} - -void CNode::Subscribe(unsigned int nChannel, unsigned int nHops) -{ - if (nChannel >= vfSubscribe.size()) - return; - - if (!AnySubscribed(nChannel)) - { - // Relay subscribe - CRITICAL_BLOCK(cs_vNodes) - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode != this) - pnode->PushMessage("subscribe", nChannel, nHops); - } - - vfSubscribe[nChannel] = true; -} - -void CNode::CancelSubscribe(unsigned int nChannel) -{ - if (nChannel >= vfSubscribe.size()) - return; - - // Prevent from relaying cancel if wasn't subscribed - if (!vfSubscribe[nChannel]) - return; - vfSubscribe[nChannel] = false; - - if (!AnySubscribed(nChannel)) - { - // Relay subscription cancel - CRITICAL_BLOCK(cs_vNodes) - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode != this) - pnode->PushMessage("sub-cancel", nChannel); - } -} - - - - - - - - - CNode* FindNode(unsigned int ip) { CRITICAL_BLOCK(cs_vNodes) @@ -513,18 +408,6 @@ } } -void CNode::Cleanup() -{ - // All of a nodes broadcasts and subscriptions are automatically torn down - // when it goes down, so a node has to stay up to keep its broadcast going. - - // Cancel subscriptions - for (unsigned int nChannel = 0; nChannel < vfSubscribe.size(); nChannel++) - if (vfSubscribe[nChannel]) - CancelSubscribe(nChannel); -} - - std::map CNode::setBanned; CCriticalSection CNode::cs_setBanned; @@ -626,7 +509,6 @@ // close socket and cleanup pnode->CloseSocketDisconnect(); - pnode->Cleanup(); // hold in disconnected pool until all refs are released pnode->nReleaseTime = max(pnode->nReleaseTime, GetTime() + 15 * 60); @@ -646,7 +528,6 @@ bool fDelete = false; TRY_CRITICAL_BLOCK(pnode->cs_vSend) TRY_CRITICAL_BLOCK(pnode->cs_vRecv) - TRY_CRITICAL_BLOCK(pnode->cs_mapRequests) TRY_CRITICAL_BLOCK(pnode->cs_inventory) fDelete = true; if (fDelete) diff -uNr a/bitcoin/src/net.h b/bitcoin/src/net.h --- a/bitcoin/src/net.h 492c9cc92a504bb8174d75fafcbee6980986182a459efc9bfa1d64766320d98ba2fa971d78d00a777c6cc50f82a5d424997927378e99738b1b3b550bdaa727f7 +++ b/bitcoin/src/net.h 446bb5f05b776039fd25680c8b46d8aada9447be55d56d882dbf52a5c1d527b02241a2fdf9985836e344128dbcf6fb3f118afac37b80109867cb05856906a598 @@ -13,7 +13,6 @@ #include "protocol.h" class CAddrDB; -class CRequestTracker; class CNode; class CBlockIndex; extern int nBestHeight; @@ -23,7 +22,6 @@ inline unsigned int ReceiveBufferSize() { return 1000*GetArg("-maxreceivebuffer", 10*1000); } inline unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 10*1000); } -static const unsigned int PUBLISH_HOPS = 5; bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet, int nTimeout=nConnectTimeout); bool Lookup(const char *pszName, std::vector& vaddr, int nServices, int nMaxSolutions, int portDefault = 0, bool fAllowPort = false); @@ -32,8 +30,6 @@ void AddressCurrentlyConnected(const CAddress& addr); CNode* FindNode(unsigned int ip); CNode* ConnectNode(CAddress addrConnect, int64 nTimeout=0); -void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1); -bool AnySubscribed(unsigned int nChannel); void MapPort(bool fMapPort); bool BindListenPort(std::string& strError=REF(std::string())); void StartNode(void* parg); @@ -45,28 +41,6 @@ MSG_BLOCK, }; -class CRequestTracker -{ -public: - void (*fn)(void*, CDataStream&); - void* param1; - - explicit CRequestTracker(void (*fnIn)(void*, CDataStream&)=NULL, void* param1In=NULL) - { - fn = fnIn; - param1 = param1In; - } - - bool IsNull() - { - return fn == NULL; - } -}; - - - - - extern bool fClient; extern bool fAllowDNS; extern uint64 nLocalServices; @@ -127,8 +101,6 @@ public: int64 nReleaseTime; - std::map mapRequests; - CCriticalSection cs_mapRequests; uint256 hashContinue; int nStartingHeight; @@ -144,23 +116,13 @@ CCriticalSection cs_inventory; std::multimap mapAskFor; - // publish and subscription - std::vector vfSubscribe; - CNode(SOCKET hSocketIn, CAddress addrIn, bool fInboundIn=false) { nServices = 0; hSocket = hSocketIn; - vSend.SetType(SER_NETWORK); - vSend.SetVersion(0); - vRecv.SetType(SER_NETWORK); - vRecv.SetVersion(0); // Version 0.2 obsoletes 20 Feb 2012 - if (GetTime() > 1329696000) - { - vSend.SetVersion(209); - vRecv.SetVersion(209); - } + vSend.SetVersion(209); + vRecv.SetVersion(209); nLastSend = 0; nLastRecv = 0; nLastSendEmpty = GetTime(); @@ -180,7 +142,6 @@ hashContinue = 0; nStartingHeight = -1; fGetAddr = false; - vfSubscribe.assign(256, false); nMisbehavior = 0; // Be shy and don't send version until we hear @@ -271,7 +232,11 @@ mapAskFor.insert(std::make_pair(nRequestTime, inv)); } - + // Construct a compatible output stream for passing to PushMessage + CDataStream SendingStream() + { + return CDataStream(SER_NETWORK, vSend.GetVersion()); + } void BeginMessage(const char* pszCommand) { @@ -355,8 +320,15 @@ CAddress addrYou = (fUseProxy ? CAddress("0.0.0.0") : addr); CAddress addrMe = (fUseProxy || !addrLocalHost.IsRoutable() ? CAddress("0.0.0.0") : addrLocalHost); RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); - PushMessage("version", VERSION, nLocalServices, nTime, addrYou, addrMe, - nLocalHostNonce, FormatSubVersion(CLIENT_NAME, VERSION), nBestHeight); + PushMessage("version", SendingStream() + << VERSION + << nLocalServices + << nTime + << addrYou + << addrMe + << nLocalHostNonce + << FormatSubVersion(CLIENT_NAME, VERSION) + << nBestHeight); } @@ -364,41 +336,15 @@ void PushMessage(const char* pszCommand) { - try - { - BeginMessage(pszCommand); - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template - void PushMessage(const char* pszCommand, const T1& a1) - { - try - { - BeginMessage(pszCommand); - vSend << a1; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } + PushMessage(pszCommand, SendingStream()); } - template - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2) + void PushMessage(const char* pszCommand, const CDataStream& payload) { try { BeginMessage(pszCommand); - vSend << a1 << a2; + vSend += payload; EndMessage(); } catch (...) @@ -408,165 +354,8 @@ } } - template - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3) - { - try - { - BeginMessage(pszCommand); - vSend << a1 << a2 << a3; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4) - { - try - { - BeginMessage(pszCommand); - vSend << a1 << a2 << a3 << a4; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5) - { - try - { - BeginMessage(pszCommand); - vSend << a1 << a2 << a3 << a4 << a5; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6) - { - try - { - BeginMessage(pszCommand); - vSend << a1 << a2 << a3 << a4 << a5 << a6; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7) - { - try - { - BeginMessage(pszCommand); - vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7, const T8& a8) - { - try - { - BeginMessage(pszCommand); - vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7, const T8& a8, const T9& a9) - { - try - { - BeginMessage(pszCommand); - vSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8 << a9; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - - void PushRequest(const char* pszCommand, - void (*fn)(void*, CDataStream&), void* param1) - { - uint256 hashReply; - RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); - - CRITICAL_BLOCK(cs_mapRequests) - mapRequests[hashReply] = CRequestTracker(fn, param1); - - PushMessage(pszCommand, hashReply); - } - - template - void PushRequest(const char* pszCommand, const T1& a1, - void (*fn)(void*, CDataStream&), void* param1) - { - uint256 hashReply; - RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); - - CRITICAL_BLOCK(cs_mapRequests) - mapRequests[hashReply] = CRequestTracker(fn, param1); - - PushMessage(pszCommand, hashReply, a1); - } - - template - void PushRequest(const char* pszCommand, const T1& a1, const T2& a2, - void (*fn)(void*, CDataStream&), void* param1) - { - uint256 hashReply; - RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); - - CRITICAL_BLOCK(cs_mapRequests) - mapRequests[hashReply] = CRequestTracker(fn, param1); - - PushMessage(pszCommand, hashReply, a1, a2); - } - - - void PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd); - bool IsSubscribed(unsigned int nChannel); - void Subscribe(unsigned int nChannel, unsigned int nHops=0); - void CancelSubscribe(unsigned int nChannel); void CloseSocketDisconnect(); - void Cleanup(); // Denial-of-service detection/prevention @@ -634,59 +423,4 @@ RelayInventory(inv); } - - - - - - - -// -// Templates for the publish and subscription system. -// The object being published as T& obj needs to have: -// a set setSources member -// specializations of AdvertInsert and AdvertErase -// Currently implemented for CTable and CProduct. -// - -template -void AdvertStartPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj) -{ - // Add to sources - obj.setSources.insert(pfrom->addr.ip); - - if (!AdvertInsert(obj)) - return; - - // Relay - CRITICAL_BLOCK(cs_vNodes) - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel))) - pnode->PushMessage("publish", nChannel, nHops, obj); -} - -template -void AdvertStopPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj) -{ - uint256 hash = obj.GetHash(); - - CRITICAL_BLOCK(cs_vNodes) - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel))) - pnode->PushMessage("pub-cancel", nChannel, nHops, hash); - - AdvertErase(obj); -} - -template -void AdvertRemoveSource(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj) -{ - // Remove a source - obj.setSources.erase(pfrom->addr.ip); - - // If no longer supported by any sources, cancel it - if (obj.setSources.empty()) - AdvertStopPublish(pfrom, nChannel, nHops, obj); -} - #endif diff -uNr a/bitcoin/src/test/serialize_tests.cpp b/bitcoin/src/test/serialize_tests.cpp --- a/bitcoin/src/test/serialize_tests.cpp false +++ b/bitcoin/src/test/serialize_tests.cpp 4f0bc192b6224dc1536527732a6de9be37d0675129b57c7abe62d418f0414f73a762fae26c047aeceb39387886480ecf8687b888a56648adbae1b4196138fe4a @@ -0,0 +1,19 @@ +#include + +#include "../serialize.h" + +BOOST_AUTO_TEST_SUITE(serialize_tests) + +BOOST_AUTO_TEST_CASE(serialize_datastream) +{ + CDataStream ds1, ds2, ds3, ds4; + ds1 << string("testing") << 123; + ds2 << ds1; + ds3 += ds1; + ds4 = ds1; + BOOST_CHECK(ds1.str() == ds2.str()); + BOOST_CHECK(ds1.str() == ds3.str()); + BOOST_CHECK(ds1.str() == ds4.str()); +} + +BOOST_AUTO_TEST_SUITE_END() diff -uNr a/bitcoin/src/test/test_bitcoin.cpp b/bitcoin/src/test/test_bitcoin.cpp --- a/bitcoin/src/test/test_bitcoin.cpp 4d8a50d4a2e7f073b219665c6022d68ab1a8aa4d9adc991e253357f5a6bdfb6a091be5eeccd376dc86491442a2f20d4598d721f3732c102d22918abffd27f30f +++ b/bitcoin/src/test/test_bitcoin.cpp 074799dfa9668d680742080c34d3aaf19a71f5c21b436d3e0e5be805664adc436c4d73cf23eb9d05adfe1c9c46fc6dcf5a48a110259306180af5a7805245e137 @@ -14,6 +14,7 @@ #include "base58_tests.cpp" #include "miner_tests.cpp" #include "Checkpoints_tests.cpp" +#include "serialize_tests.cpp" CWallet* pwalletMain;