changeset 23681:dd9390ced363

Xtreme Thinblocks Sends a seeded bloom filter along with a getdata and receives an xthinblock transaction in return which includes all missing transactions and tx hashes. Once received the block is reconstructed. Additional bandwidth is saved by using a 64 bit Tx hash instead of all 256 bits. Hash collisions in the memory pool will be extremely rare however if one should happen then a regular thinblock will be re-requested. Transactions still missing will be re-requested and retrieved from a block rather than the mempool. They will be sent back in one thinblocktx object. Originally Inspired by Mike Hearn xthinblock + coinbase (dagurval)
author Peter Tschipper <peter.tschipper@gmailcom>
date Fri, 15 Jan 2016 21:37:25 -0800
parents 456b5bdf4011
children 343b908ab77c
files src/Makefile.am src/main.cpp src/net.cpp src/net.h src/protocol.cpp src/protocol.h src/thinblock.cpp src/thinblock.h src/unlimited.cpp src/unlimited.h src/version.h
diffstat 11 files changed, 806 insertions(+), 64 deletions(-) [+]
line wrap: on
line diff
--- a/src/Makefile.am	Thu Feb 04 15:00:56 2016 -0500
+++ b/src/Makefile.am	Fri Jan 15 21:37:25 2016 -0800
@@ -149,6 +149,7 @@
   support/cleanse.h \
   support/pagelocker.h \
   sync.h \
+  thinblock.h \
   threadsafety.h \
   timedata.h \
   tinyformat.h \
@@ -211,6 +212,7 @@
   rpcrawtransaction.cpp \
   rpcserver.cpp \
   script/sigcache.cpp \
+  thinblock.cpp \
   timedata.cpp \
   torcontrol.cpp \
   txdb.cpp \
--- a/src/main.cpp	Thu Feb 04 15:00:56 2016 -0500
+++ b/src/main.cpp	Fri Jan 15 21:37:25 2016 -0800
@@ -25,6 +25,7 @@
 #include "script/script.h"
 #include "script/sigcache.h"
 #include "script/standard.h"
+#include "thinblock.h"
 #include "tinyformat.h"
 #include "txdb.h"
 #include "txmempool.h"
@@ -212,6 +213,7 @@
     set<int> setDirtyFileInfo;
 } // anon namespace
 
+
 //////////////////////////////////////////////////////////////////////////////
 //
 // Registration of network node signals.
@@ -345,6 +347,11 @@
 bool MarkBlockAsReceived(const uint256& hash) {
     map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash);
     if (itInFlight != mapBlocksInFlight.end()) {
+        // BUIP010 Xtreme Thinblocks: begin section
+        int64_t getdataTime = itInFlight->second.second->nTime;
+        int64_t now = GetTimeMicros();
+        LogPrint("thin", "Received block %s in %.2f seconds\n", hash.ToString(), (now - getdataTime) / 1000000.0);
+        // BUIP010 Xtreme Thinblocks: end section
         CNodeState *state = State(itInFlight->second.first);
         nQueuedValidatedHeaders -= itInFlight->second.second->fValidatedHeaders;
         state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders;
@@ -4133,7 +4140,8 @@
             boost::this_thread::interruption_point();
             it++;
 
-            if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK)
+            // BUIP010 Xtreme Thinblocks: if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK)
+            if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_THINBLOCK || inv.type == MSG_XTHINBLOCK)
             {
                 bool send = false;
                 BlockMap::iterator mi = mapBlockIndex.find(inv.hash);
@@ -4180,6 +4188,12 @@
                         assert(!"cannot load block from disk");
                     if (inv.type == MSG_BLOCK)
                         pfrom->PushMessage(NetMsgType::BLOCK, block);
+
+                    // BUIP010 Xtreme Thinblocks: begin section
+                    else if (inv.type == MSG_THINBLOCK || inv.type == MSG_XTHINBLOCK)
+                        SendXThinBlock(block, pfrom, inv);
+                    // BUIP010 Xtreme Thinblocks: end section
+
                     else // MSG_FILTERED_BLOCK)
                     {
                         LOCK(pfrom->cs_filter);
@@ -4244,7 +4258,8 @@
             // Track requests for our stuff.
             GetMainSignals().Inventory(inv.hash);
 
-            if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK)
+            // BUIP010 Xtreme Thinblocks: if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK)
+            if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_THINBLOCK || inv.type == MSG_XTHINBLOCK)
                 break;
         }
     }
@@ -4275,20 +4290,20 @@
         return true;
     }
 
-
-    if (!(nLocalServices & NODE_BLOOM) &&
-              (strCommand == NetMsgType::FILTERLOAD ||
-               strCommand == NetMsgType::FILTERADD ||
-               strCommand == NetMsgType::FILTERCLEAR))
-    {
-        if (pfrom->nVersion >= NO_BLOOM_VERSION) {
-            Misbehaving(pfrom->GetId(), 100);
-            return false;
-        } else if (GetBoolArg("-enforcenodebloom", false)) {
-            pfrom->fDisconnect = true;
-            return false;
-        }
-    }
+// BUIP010 Extrem Thinblocks:  We need bloom filtering. We do not turn bloom filtering off
+//    if (!(nLocalServices & NODE_BLOOM) &&
+//              (strCommand == NetMsgType::FILTERLOAD ||
+//               strCommand == NetMsgType::FILTERADD ||
+//               strCommand == NetMsgType::FILTERCLEAR))
+//    {
+//        if (pfrom->nVersion >= NO_BLOOM_VERSION) {
+//            Misbehaving(pfrom->GetId(), 100);
+//            return false;
+//        } else if (GetBoolArg("-enforcenodebloom", false)) {
+//            pfrom->fDisconnect = true;
+//            return false;
+//        }
+//    }
 
 
     if (strCommand == NetMsgType::VERSION)
@@ -4306,6 +4321,9 @@
         CAddress addrFrom;
         uint64_t nNonce = 1;
         vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe;
+
+        CheckNodeSupportForThinBlocks(); // BUIP010 Xtreme Thinblocks
+
         if (pfrom->nVersion < MIN_PEER_PROTO_VERSION)
         {
             // disconnect from peers older than this proto version
@@ -4437,7 +4455,10 @@
             // We send this to non-NODE NETWORK peers as well, because even
             // non-NODE NETWORK peers can announce blocks (such as pruning
             // nodes)
-            pfrom->PushMessage(NetMsgType::SENDHEADERS);
+
+            // BUIP010 Extreme Thinblocks: We only do inv/getdata for xthinblocks and so we must have headersfirst turned off
+            if (!IsThinBlocksEnabled)
+                pfrom->PushMessage(NetMsgType::SENDHEADERS);
         }
     }
 
@@ -4511,7 +4532,11 @@
     else if (strCommand == NetMsgType::SENDHEADERS)
     {
         LOCK(cs_main);
-        State(pfrom->GetId())->fPreferHeaders = true;
+        // BUIP010 Xtreme Thinblocks: We only do inv/getdata for xthinblocks and so we must have headersfirst turned off
+        if (IsThinBlocksEnabled)
+            State(pfrom->GetId())->fPreferHeaders = false;
+        else
+            State(pfrom->GetId())->fPreferHeaders = true;
     }
 
 
@@ -4560,10 +4585,40 @@
                     CNodeState *nodestate = State(pfrom->GetId());
                     if (CanDirectFetch(chainparams.GetConsensus()) &&
                         nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
-                        vToFetch.push_back(inv);
-                        // Mark block as in flight already, even though the actual "getdata" message only goes out
-                        // later (within the same cs_main lock, though).
-                        MarkBlockAsInFlight(pfrom->GetId(), inv.hash, chainparams.GetConsensus());
+                        // BUIP010 Xtreme Thinblocks: begin section
+                        CInv inv2(inv);
+                        if (IsThinBlocksEnabled() && IsChainNearlySyncd()) {
+                            if (HaveThinblockNodeConnections()) {
+                                // Must download a block from a ThinBlock peer
+                                if (pfrom->mapThinBlocksInFlight.size() < 1 && pfrom->nVersion >= THINBLOCKS_VERSION) { // We can only send one thinblock per peer at a time
+                                    pfrom->mapThinBlocksInFlight[inv2.hash] = GetTime();
+                                    inv2.type = MSG_XTHINBLOCK;
+                                    vToFetch.push_back(inv2);
+                                    SendSeededBloomFilter(pfrom);
+                                    MarkBlockAsInFlight(pfrom->GetId(), inv.hash, chainparams.GetConsensus());
+                                    LogPrint("thin", "Requesting Thinblock %s peer=%d\n", inv2.hash.ToString(), pfrom->id);
+                                }
+                            }
+                            else {
+                                // Try to download a thinblock if possible otherwise just download a regular block
+                                if (pfrom->mapThinBlocksInFlight.size() < 1 && pfrom->nVersion >= THINBLOCKS_VERSION) { // We can only send one thinblock per peer at a time
+                                    pfrom->mapThinBlocksInFlight[inv2.hash] = GetTime();
+                                    inv2.type = MSG_XTHINBLOCK;
+                                    SendSeededBloomFilter(pfrom);
+                                    LogPrint("thin", "Requesting Thinblock %s peer=%d\n", inv2.hash.ToString(), pfrom->id);
+                                }
+                                else
+                                    LogPrint("thin", "Requesting Regular Block %s peer=%d\n", inv2.hash.ToString(), pfrom->id);
+                                MarkBlockAsInFlight(pfrom->GetId(), inv.hash, chainparams.GetConsensus());
+                                vToFetch.push_back(inv2);
+                            }
+                        }
+                        else {
+                            vToFetch.push_back(inv2);
+                            MarkBlockAsInFlight(pfrom->GetId(), inv.hash, chainparams.GetConsensus());
+                            LogPrint("thin", "Requesting Regular Block %s peer=%d\n", inv2.hash.ToString(), pfrom->id);
+                        }
+                        // BUIP010 Xtreme Thinblocks: end section
                     }
                     LogPrint("net", "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->id);
                 }
@@ -4918,7 +4973,8 @@
                 LogPrint("net", "Large reorg, won't direct fetch to %s (%d)\n",
                         pindexLast->GetBlockHash().ToString(),
                         pindexLast->nHeight);
-            } else {
+            //} else {   BU: We don't support headers first for XThinblocks.
+            } else if (!IsThinBlocksEnabled) {
                 vector<CInv> vGetData;
                 // Download as much as possible, from earliest to latest.
                 BOOST_REVERSE_FOREACH(CBlockIndex *pindex, vToFetch) {
@@ -4944,6 +5000,249 @@
         CheckBlockIndex(chainparams.GetConsensus());
     }
 
+    // BUIP010 Xtreme Thinblocks: begin section
+    else if (strCommand == NetMsgType::XTHINBLOCK  && !fImporting && !fReindex) // Ignore blocks received while importing
+    {
+        CXThinBlock thinBlock;
+        vRecv >> thinBlock;
+
+        CInv inv(MSG_BLOCK, thinBlock.header.GetHash());
+        int nSizeThinBlock = ::GetSerializeSize(thinBlock, SER_NETWORK, PROTOCOL_VERSION);
+        LogPrint("thin", "received thinblock %s peer=%d (%d bytes)\n", inv.hash.ToString(), pfrom->id, nSizeThinBlock);
+        if (!pfrom->mapThinBlocksInFlight.count(inv.hash)) {
+            LogPrint("thin", "Thinblock received but not requested %s  peer=%d\n",inv.hash.ToString(), pfrom->id);
+            Misbehaving(pfrom->GetId(), 20);
+        }
+
+        pfrom->thinBlock = CBlock();
+        pfrom->thinBlock.nVersion = thinBlock.header.nVersion;
+        pfrom->thinBlock.nBits = thinBlock.header.nBits;
+        pfrom->thinBlock.nNonce = thinBlock.header.nNonce;
+        pfrom->thinBlock.nTime = thinBlock.header.nTime;
+        pfrom->thinBlock.hashMerkleRoot = thinBlock.header.hashMerkleRoot;
+        pfrom->thinBlock.hashPrevBlock = thinBlock.header.hashPrevBlock;
+        pfrom->xThinBlockHashes = thinBlock.vTxHashes;
+
+        // Create a map of all 8 bytes tx hashes pointing to their full tx hash counterpart 
+        bool collision = false;
+        std::map<uint64_t, uint256> mapPartialTxHash;
+        LOCK(cs_main);
+        std::vector<uint256> memPoolHashes;
+        mempool.queryHashes(memPoolHashes);
+        for (uint64_t i = 0; i < memPoolHashes.size(); i++) {
+            uint64_t cheapHash = memPoolHashes[i].GetCheapHash();
+            if(mapPartialTxHash.count(cheapHash)) //Check for collisions
+                collision = true;
+            mapPartialTxHash[cheapHash] = memPoolHashes[i];
+        }
+        for (map<uint256, CTransaction>::iterator mi = thinBlock.mapMissingTx.begin(); mi != thinBlock.mapMissingTx.end(); ++mi) {
+            uint64_t cheapHash = (*mi).first.GetCheapHash();
+            if(mapPartialTxHash.count(cheapHash)) //Check for collisions
+                collision = true;
+            mapPartialTxHash[cheapHash] = (*mi).first;
+        }
+
+        // There is a remote possiblity of a Tx hash collision therefore if it occurs we re-request a normal
+        // thinblock which has the full Tx hash data rather than just the truncated hash.
+        if (collision) {
+            vector<CInv> vGetData;
+            vGetData.push_back(CInv(MSG_THINBLOCK, thinBlock.header.GetHash())); 
+            pfrom->PushMessage("getdata", vGetData);
+            LogPrintf("TX HASH COLLISION for xthinblock: re-requesting a thinblock\n");
+            return true;
+        }
+
+        int missingCount = 0;
+        // Look for each transaction in our various pools and buffers.
+        // With xThinBlocks the vTxHashes contains only the first 8 bytes of the tx hash.
+        BOOST_FOREACH(uint64_t &cheapHash, thinBlock.vTxHashes) 
+        {
+            // Replace the truncated hash with the full hash value if it exists
+            const uint256 hash = mapPartialTxHash[cheapHash];
+
+            CTransaction tx;
+            if (!hash.IsNull() && !mempool.lookup(hash, tx)) {
+                if (thinBlock.mapMissingTx.count(hash))
+                    tx = thinBlock.mapMissingTx[hash];
+            }
+            if (tx.IsNull())
+                missingCount++;
+            // This will push an empty/invalid transaction if we don't have it yet
+            pfrom->thinBlock.vtx.push_back(tx);
+        }
+        pfrom->thinBlockWaitingForTxns = missingCount;
+        LogPrint("thin", "waiting for: %d block txs: %d\n", pfrom->thinBlockWaitingForTxns, pfrom->thinBlock.vtx.size());
+
+        if (pfrom->thinBlockWaitingForTxns == 0) {
+            // We have all the transactions now that are in this block: try to reassemble and process.
+            pfrom->thinBlockWaitingForTxns = -1;
+            pfrom->AddInventoryKnown(inv); 
+            LogPrint("thin", "Reassembled thin block for %s (%d bytes)\n", pfrom->thinBlock.GetHash().ToString(),
+                       pfrom->thinBlock.GetSerializeSize(SER_NETWORK, CBlock::CURRENT_VERSION));
+            HandleBlockMessage(pfrom, strCommand, pfrom->thinBlock, inv);
+        }
+        else if (pfrom->thinBlockWaitingForTxns > 0) {
+            // This marks the end of the transactions we've received. If we get this and we have NOT been able to
+            // finish reassembling the block, we need to re-request the transactions we're missing:
+            std::vector<uint64_t> vHashesToRequest;
+            for (size_t i = 0; i < pfrom->thinBlock.vtx.size(); i++) {
+                 if (pfrom->thinBlock.vtx[i].IsNull()) {
+                     vHashesToRequest.push_back(pfrom->xThinBlockHashes[i]);
+                     LogPrint("thin", "Re-requesting tx ==> 8 byte hash %d\n", pfrom->xThinBlockHashes[i]);
+                 }
+            }
+            // Re-request transactions that we are still missing
+            CXThinBlockTx thinBlockTx(thinBlock.header.GetHash(), vHashesToRequest);
+            pfrom->PushMessage(NetMsgType::GET_XBLOCKTX, thinBlockTx);
+            LogPrint("thin", "Missing %d transactions for xthinblock, re-requesting (consider adjusting relay policies)\n", 
+                      pfrom->thinBlockWaitingForTxns);
+        }
+    }
+
+    else if (strCommand == NetMsgType::THINBLOCK && !fImporting && !fReindex) // Ignore blocks received while importing
+    {
+        CThinBlock thinBlock;
+        vRecv >> thinBlock;
+
+        CInv inv(MSG_BLOCK, thinBlock.header.GetHash());
+        int nSizeThinBlock = ::GetSerializeSize(thinBlock, SER_NETWORK, PROTOCOL_VERSION);
+        LogPrint("thin", "received thinblock %s peer=%d (%d bytes)\n", inv.hash.ToString(), pfrom->id, nSizeThinBlock);
+        if (!pfrom->mapThinBlocksInFlight.count(inv.hash)) {
+            LogPrint("thin", "Thinblock received but not requested %s  peer=%d\n",inv.hash.ToString(), pfrom->id);
+            LOCK(cs_main);
+            Misbehaving(pfrom->GetId(), 20);
+        }
+
+        pfrom->thinBlock = CBlock();
+        pfrom->thinBlock.nVersion = thinBlock.header.nVersion;
+        pfrom->thinBlock.nBits = thinBlock.header.nBits;
+        pfrom->thinBlock.nNonce = thinBlock.header.nNonce;
+        pfrom->thinBlock.nTime = thinBlock.header.nTime;
+        pfrom->thinBlock.hashMerkleRoot = thinBlock.header.hashMerkleRoot;
+        pfrom->thinBlock.hashPrevBlock = thinBlock.header.hashPrevBlock;
+        pfrom->thinBlockHashes = thinBlock.vTxHashes;
+
+        int missingCount = 0;
+        LOCK(cs_main);
+        // Look for each transaction in our various pools and buffers.
+        BOOST_FOREACH(const uint256 &hash, thinBlock.vTxHashes) 
+        {
+            CTransaction tx;
+            if (!mempool.lookup(hash, tx)) {
+                if (thinBlock.mapMissingTx.count(hash))
+                    tx = thinBlock.mapMissingTx[hash];
+            }
+            if (tx.IsNull())
+                missingCount++;
+            // This will push an empty/invalid transaction if we don't have it yet
+            pfrom->thinBlock.vtx.push_back(tx);
+        }
+        pfrom->thinBlockWaitingForTxns = missingCount;
+
+        if (pfrom->thinBlockWaitingForTxns == 0) {
+            // We have all the transactions now that are in this block: try to reassemble and process.
+            pfrom->thinBlockWaitingForTxns = -1;
+            pfrom->AddInventoryKnown(inv); 
+            LogPrint("thin", "Reassembled thin block for %s (%d bytes)\n", pfrom->thinBlock.GetHash().ToString(),
+                       pfrom->thinBlock.GetSerializeSize(SER_NETWORK, CBlock::CURRENT_VERSION));
+            HandleBlockMessage(pfrom, strCommand, pfrom->thinBlock, inv);
+        }
+        else if (pfrom->thinBlockWaitingForTxns > 0) {
+            // This marks the end of the transactions we've received. If we get this and we have NOT been able to
+            // finish reassembling the block, we need to re-request the full regular block:
+            vector<CInv> vGetData;
+            vGetData.push_back(CInv(MSG_BLOCK, thinBlock.header.GetHash())); 
+            pfrom->PushMessage("getdata", vGetData);
+            LogPrintf("thin", "Missing %d Thinblock transactions, re-requesting a regular block\n",  
+                       pfrom->thinBlockWaitingForTxns);
+        }
+    }
+
+
+    else if (strCommand == NetMsgType::XBLOCKTX && !fImporting && !fReindex) // handle Re-requested thinblock transactions
+    {
+        CXThinBlockTx thinBlockTx;
+        vRecv >> thinBlockTx;
+
+        CInv inv(MSG_XTHINBLOCK, thinBlockTx.blockhash);
+        LogPrint("net", "received blocktxs for %s peer=%d\n", inv.hash.ToString(), pfrom->id);
+        if (!pfrom->mapThinBlocksInFlight.count(inv.hash)) {
+            LogPrint("thin", "ThinblockTx received but not requested %s  peer=%d\n",inv.hash.ToString(), pfrom->id);
+            LOCK(cs_main);
+            Misbehaving(pfrom->GetId(), 20);
+        }
+
+        for (size_t i = 0; i < pfrom->thinBlock.vtx.size(); i++) {
+             if (pfrom->thinBlock.vtx[i].IsNull()) {
+                 pfrom->thinBlock.vtx[i] = thinBlockTx.mapTx[pfrom->xThinBlockHashes[i]];
+                 pfrom->thinBlockWaitingForTxns--;
+                 LogPrint("thin", "Got Re-requested tx ==> 8 byte hash %d\n", pfrom->xThinBlockHashes[i]);
+             }
+        }
+        if (pfrom->thinBlockWaitingForTxns == 0) {
+            // We have all the transactions now that are in this block: try to reassemble and process.
+            pfrom->thinBlockWaitingForTxns = -1;
+            pfrom->AddInventoryKnown(inv); 
+            LogPrint("thin", "Reassembled thin block for %s (%d bytes)\n", pfrom->thinBlock.GetHash().ToString(),
+                      pfrom->thinBlock.GetSerializeSize(SER_NETWORK, CBlock::CURRENT_VERSION));
+            HandleBlockMessage(pfrom, strCommand, pfrom->thinBlock, inv);
+        }
+        else {
+            LogPrint("thin", "Failed to retrieve all transactions for block - DOS Banned\n");
+            LOCK(cs_main);
+            Misbehaving(pfrom->GetId(), 100);
+        }
+    }
+
+
+    else if (strCommand == NetMsgType::GET_XBLOCKTX && !fImporting && !fReindex) // return Re-requested xthinblock transactions
+    {
+        CXThinBlockTx thinBlockTx;
+        vRecv >> thinBlockTx;
+
+        // We use MSG_TX here even though we refer to blockhash because we need to track 
+        // how many xblocktx requests we make in case of DOS
+        CInv inv(MSG_TX, thinBlockTx.blockhash); 
+        LogPrint("thin", "received get_xblocktx for %s peer=%d\n", inv.hash.ToString(), pfrom->id);
+
+        // Check for Misbehaving and DOS
+        // If they make more than 20 requests in 10 minutes then disconnect them
+        {
+            if (pfrom->nGetXBlockTxLastTime <= 0)
+                pfrom->nGetXBlockTxLastTime = GetTime();
+            uint64_t nNow = GetTime();
+            pfrom->nGetXBlockTxCount *= pow(1.0 - 1.0/600.0, (double)(nNow - pfrom->nGetXBlockTxLastTime));
+            pfrom->nGetXBlockTxLastTime = nNow;
+            pfrom->nGetXBlockTxCount += 1;
+            LogPrint("thin", "nGetXBlockTxCount is %f\n", pfrom->nGetXBlockTxCount);
+            if (pfrom->nGetXBlockTxCount >= 20) {
+                LogPrintf("DOS: Misbehaving - requesting too many xblocktx: %s\n", inv.hash.ToString());
+                LOCK(cs_main);
+                Misbehaving(pfrom->GetId(), 100);  // If they exceed the limit then disconnect them
+            }
+        }
+
+        {
+        LOCK(cs_main);
+        BlockMap::iterator mi = mapBlockIndex.find(inv.hash);
+        CBlock block;
+        const Consensus::Params& consensusParams = Params().GetConsensus();
+        if (!ReadBlockFromDisk(block, (*mi).second, consensusParams))
+            assert(!"cannot load block from disk");
+
+        for (unsigned int i = 0; i < block.vtx.size(); i++)
+        { 
+            uint64_t cheapHash = block.vtx[i].GetHash().GetCheapHash();
+            if(thinBlockTx.mapTx.count(cheapHash))
+                thinBlockTx.mapTx[cheapHash] = block.vtx[i];
+        }
+        }
+        pfrom->AddInventoryKnown(inv);
+        pfrom->PushMessage(NetMsgType::XBLOCKTX, thinBlockTx);
+    }
+    // BUIP010 Xtreme Thinblocks: end section
+
+
     else if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex) // Ignore blocks received while importing
     {
         CBlock block;
@@ -4955,24 +5254,8 @@
 
         pfrom->AddInventoryKnown(inv);
 
-        CValidationState state;
-        // Process all blocks from whitelisted peers, even if not requested,
-        // unless we're still syncing with the network.
-        // Such an unrequested block may still be processed, subject to the
-        // conditions in AcceptBlock().
-        bool forceProcessing = pfrom->fWhitelisted && !IsInitialBlockDownload();
-        ProcessNewBlock(state, chainparams, pfrom, &block, forceProcessing, NULL);
-        int nDoS;
-        if (state.IsInvalid(nDoS)) {
-            assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
-            pfrom->PushMessage(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(),
-                               state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash);
-            if (nDoS > 0) {
-                LOCK(cs_main);
-                Misbehaving(pfrom->GetId(), nDoS);
-            }
-        }
-
+        // BUIP010 Extreme Thinblocks: Handle Block Message
+        HandleBlockMessage(pfrom, strCommand, block, inv);
     }
 
 
@@ -5215,6 +5498,7 @@
     return true;
 }
 
+
 // requires LOCK(cs_vRecvMsg)
 bool ProcessMessages(CNode* pfrom)
 {
@@ -5649,10 +5933,43 @@
             NodeId staller = -1;
             FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller);
             BOOST_FOREACH(CBlockIndex *pindex, vToDownload) {
-                vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash()));
-                MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex);
-                LogPrint("net", "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(),
-                    pindex->nHeight, pto->id);
+                // BUIP010 Xtreme Thinblocks: begin section
+                if (IsThinBlocksEnabled() && IsChainNearlySyncd()) {
+                    if (HaveThinblockNodeConnections()) {
+                        // Must download a block from a ThinBlock peer
+                        if (pto->mapThinBlocksInFlight.size() < 1 && pto->nVersion >= THINBLOCKS_VERSION) { // We can only send one thinblock per peer at a time
+                            pto->mapThinBlocksInFlight[pindex->GetBlockHash()] = GetTime();
+                            vGetData.push_back(CInv(MSG_XTHINBLOCK, pindex->GetBlockHash())); 
+                            SendSeededBloomFilter(pto);
+                            MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex);
+                            LogPrint("thin", "Requesting Thinblock %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(),
+                                             pindex->nHeight, pto->id);
+                        }
+                    }
+                    else {
+                        // Try to download a thinblock if possible otherwise just download a regular block
+                        if (pto->mapThinBlocksInFlight.size() < 1 && pto->nVersion >= THINBLOCKS_VERSION) { // We can only send one thinblock per peer at a time
+                            pto->mapThinBlocksInFlight[pindex->GetBlockHash()] = GetTime();
+                            vGetData.push_back(CInv(MSG_XTHINBLOCK, pindex->GetBlockHash())); 
+                            SendSeededBloomFilter(pto);
+                            LogPrint("thin", "Requesting Thinblock %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(),
+                                             pindex->nHeight, pto->id);
+                        }
+                        else {
+                            vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); 
+                            LogPrint("net", "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(),
+                                             pindex->nHeight, pto->id);
+                        }
+                        MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex);
+                    }
+                }
+                else {
+                    vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); 
+                    MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex);
+                    LogPrint("net", "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(),
+                                     pindex->nHeight, pto->id);
+                }
+                // BUIP010 Xtreme Thinblocks: end section
             }
             if (state.nBlocksInFlight == 0 && staller != -1) {
                 if (State(staller)->nStallingSince == 0) {
@@ -5695,8 +6012,6 @@
      return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst), DateTimeStrFormat("%Y-%m-%d", nTimeLast));
  }
 
-
-
 class CMainCleanup
 {
 public:
--- a/src/net.cpp	Thu Feb 04 15:00:56 2016 -0500
+++ b/src/net.cpp	Fri Jan 15 21:37:25 2016 -0800
@@ -1469,7 +1469,8 @@
 void ThreadOpenConnections()
 {
     // Connect to specific addresses
-    if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0)
+    if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0 ||
+        mapArgs.count("-connect-thinblock") && mapMultiArgs["-connect-thinblock"].size() > 0) // BUIP010 Xtreme Thinblocks
     {
         for (int64_t nLoop = 0;; nLoop++)
         {
@@ -1484,6 +1485,10 @@
                 }
             }
             MilliSleep(500);
+
+           // BUIP010 Xtreme Thinblocks: begin section
+           ConnectToThinBlockNodes();
+           // BUIP010 Xtreme Thinblocks: end section
         }
     }
 
@@ -2311,6 +2316,7 @@
     nPingUsecTime = 0;
     fPingQueued = false;
     nMinPingUsecTime = std::numeric_limits<int64_t>::max();
+    thinBlockWaitingForTxns = -1; // BUIP010 Xtreme Thinblocks
 
     {
         LOCK(cs_nLastNodeId);
--- a/src/net.h	Thu Feb 04 15:00:56 2016 -0500
+++ b/src/net.h	Fri Jan 15 21:37:25 2016 -0800
@@ -10,6 +10,7 @@
 #include "compat.h"
 #include "limitedmap.h"
 #include "netbase.h"
+#include "primitives/block.h"
 #include "protocol.h"
 #include "random.h"
 #include "streams.h"
@@ -367,6 +368,16 @@
     int nRefCount;
     NodeId id;
 
+    // BUIP010 Xtreme Thinblocks: begin section
+    CBlock thinBlock;
+    std::vector<uint256> thinBlockHashes;
+    std::vector<uint64_t> xThinBlockHashes;
+    int thinBlockWaitingForTxns;   // if -1 then not currently waiting
+    std::map<uint256, uint64_t> mapThinBlocksInFlight; // map of the hashes of thin blocks in flight with the time they were requested.
+    double nGetXBlockTxCount; // Count how many get_xblocktx requests are made
+    uint64_t nGetXBlockTxLastTime;  // The last time a get_xblocktx request was made
+    // BUIP010 Xtreme Thinblocks: end section
+
 protected:
     // Denial-of-service detection/prevention
     // Key is IP address, value is banned-until-time
--- a/src/protocol.cpp	Thu Feb 04 15:00:56 2016 -0500
+++ b/src/protocol.cpp	Fri Jan 15 21:37:25 2016 -0800
@@ -35,6 +35,12 @@
 const char *FILTERCLEAR="filterclear";
 const char *REJECT="reject";
 const char *SENDHEADERS="sendheaders";
+// BUIP010 Xtreme Thinblocks - begin section
+const char *THINBLOCK="thinblock";
+const char *XTHINBLOCK="xthinblock";
+const char *XBLOCKTX="xblocktx";
+const char *GET_XBLOCKTX="get_xblocktx";
+// BUIP010 Xtreme Thinblocks - end section
 };
 
 static const char* ppszTypeName[] =
@@ -42,7 +48,13 @@
     "ERROR", // Should never occur
     NetMsgType::TX,
     NetMsgType::BLOCK,
-    "filtered block" // Should never occur
+    "filtered block", // Should never occur
+    // BUIP010 Xtreme Thinblocks - begin section
+    NetMsgType::THINBLOCK,
+    NetMsgType::XTHINBLOCK,
+    NetMsgType::XBLOCKTX,
+    NetMsgType::GET_XBLOCKTX,
+    // BUIP010 Xtreme Thinblocks - end section
 };
 
 /** All known message types. Keep this in the same order as the list of
@@ -70,7 +82,13 @@
     NetMsgType::FILTERADD,
     NetMsgType::FILTERCLEAR,
     NetMsgType::REJECT,
-    NetMsgType::SENDHEADERS
+    NetMsgType::SENDHEADERS,
+    // BUIP010 Xtreme Thinbocks - begin section
+    NetMsgType::THINBLOCK,
+    NetMsgType::XTHINBLOCK,
+    NetMsgType::XBLOCKTX,
+    NetMsgType::GET_XBLOCKTX,
+    // BUIP010 Xtreme Thinbocks - end section
 };
 const static std::vector<std::string> allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes));
 
--- a/src/protocol.h	Thu Feb 04 15:00:56 2016 -0500
+++ b/src/protocol.h	Fri Jan 15 21:37:25 2016 -0800
@@ -138,6 +138,22 @@
  */
 extern const char *BLOCK;
 /**
+ * BUIP010 Xtreme Thinblocks: The thinblock message transmits a single serialized thinblock.
+ */
+extern const char *THINBLOCK;
+/**
+ * BUIP010 Xtreme Thinblocks: The xthinblock message transmits a single serializexd xthinblock.
+ */
+extern const char *XTHINBLOCK;
+/**
+ * BUIP010 Xtreme Thinblocks: The xblocktx message transmits a single serialized xblocktx.
+ */
+extern const char *XBLOCKTX;
+/**
+ * BUIP010 Xtreme Thinblocks: The get_xblocktx message transmits a single serialized get_xblocktx.
+ */
+extern const char *GET_XBLOCKTX;
+/**
  * The getaddr message requests an addr message from the receiving node,
  * preferably one with lots of IP addresses of other receiving nodes.
  * @see https://bitcoin.org/en/developer-reference#getaddr
@@ -316,6 +332,12 @@
     // Nodes may always request a MSG_FILTERED_BLOCK in a getdata, however,
     // MSG_FILTERED_BLOCK should not appear in any invs except as a part of getdata.
     MSG_FILTERED_BLOCK,
+    // BUIP010 Xtreme Thinblocks: a thin block contains all the transactions hashes in a block
+    // and also provides the missing transactions that are needed at the other end to reconstruct the block
+    MSG_THINBLOCK,
+    // BUIP010 Xtreme Thinblocks: an Xtreme thin block contains the first 8 bytes of all the tx hashes 
+    // and also provides the missing transactions that are needed at the other end to reconstruct the block
+    MSG_XTHINBLOCK,
 };
 
 #endif // BITCOIN_PROTOCOL_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/thinblock.cpp	Fri Jan 15 21:37:25 2016 -0800
@@ -0,0 +1,59 @@
+// Copyright (c) 2016 The Bitcoin Unlimited developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "thinblock.h"
+
+CThinBlock::CThinBlock(const CBlock& block, CBloomFilter& filter)
+{
+    header = block.GetBlockHeader();
+
+    vTxHashes.reserve(block.vtx.size());
+    for (unsigned int i = 0; i < block.vtx.size(); i++)
+    {
+        const uint256& hash = block.vtx[i].GetHash();
+        vTxHashes.push_back(hash);
+
+        // Find the transactions that do not match the filter.
+        // These are the ones we need to relay back to the requesting peer.
+        // NOTE: We always add the first tx, the coinbase as it is the one
+        //       most often missing.
+        if (!filter.contains(hash) || i == 0)
+            mapMissingTx[hash] = block.vtx[i];
+    }
+}
+
+CXThinBlock::CXThinBlock(const CBlock& block, CBloomFilter& filter)
+{
+    header = block.GetBlockHeader();
+    this->collision = false;
+
+    vTxHashes.reserve(block.vtx.size());
+    std::set<uint64_t> setPartialTxHash;
+    for (unsigned int i = 0; i < block.vtx.size(); i++)
+    {
+        const uint256 hash256 = block.vtx[i].GetHash();
+        uint64_t cheapHash = hash256.GetCheapHash();
+        vTxHashes.push_back(cheapHash);
+
+        if (setPartialTxHash.count(cheapHash))
+                this->collision = true;
+        setPartialTxHash.insert(cheapHash);
+
+        // Find the transactions that do not match the filter.
+        // These are the ones we need to relay back to the requesting peer.
+        // NOTE: We always add the first tx, the coinbase as it is the one
+        //       most often missing.
+        if (!filter.contains(hash256) || i == 0)
+            mapMissingTx[hash256] = block.vtx[i];
+    }
+}
+
+CXThinBlockTx::CXThinBlockTx(uint256 blockHash, std::vector<uint64_t>& vHashesToRequest)
+{
+    blockhash = blockHash;
+
+    CTransaction tx;
+    for (unsigned int i = 0; i < vHashesToRequest.size(); i++)
+        mapTx[vHashesToRequest[i]] = tx;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/thinblock.h	Fri Jan 15 21:37:25 2016 -0800
@@ -0,0 +1,80 @@
+// Copyright (c) 2016 The Bitcoin Unlimited developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_THINBLOCK_H
+#define BITCOIN_THINBLOCK_H
+
+#include "serialize.h"
+#include "uint256.h"
+#include "primitives/block.h"
+#include "bloom.h"
+
+#include <vector>
+
+class CThinBlock
+{
+public:
+    CBlockHeader header;
+    std::vector<uint256> vTxHashes; // List of all transactions id's in the block
+    std::map<uint256, CTransaction> mapMissingTx; // map of transactions that did not match the bloom filter
+
+public:
+    CThinBlock(const CBlock& block, CBloomFilter& filter);
+    CThinBlock() {}
+
+    ADD_SERIALIZE_METHODS;
+
+    template <typename Stream, typename Operation>
+    inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+        READWRITE(header);
+        READWRITE(vTxHashes);
+        READWRITE(mapMissingTx);
+    }
+};
+
+class CXThinBlock
+{
+public:
+    CBlockHeader header;
+    std::vector<uint64_t> vTxHashes; // List of all transactions id's in the block
+    std::map<uint256, CTransaction> mapMissingTx; // map of transactions that did not match the bloom filter
+    bool collision;
+
+public:
+    CXThinBlock(const CBlock& block, CBloomFilter& filter);
+    CXThinBlock() {}
+
+    ADD_SERIALIZE_METHODS;
+
+    template <typename Stream, typename Operation>
+    inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+        READWRITE(header);
+        READWRITE(vTxHashes);
+        READWRITE(mapMissingTx);
+    }
+};
+
+// This class is used for retrieving a list of still missing transactions after receiving a "thinblock" message.
+// The CXThinBlockTx when recieved can be used to fill in the missing transactions after which it is sent
+// back to the requestor.  This class uses a 64bit hash as opposed to the normal 256bit hash.
+class CXThinBlockTx
+{
+public:
+    /** Public only for unit testing */
+    uint256 blockhash;
+    std::map<uint64_t, CTransaction> mapTx; // map of missing transactions
+
+public:
+    CXThinBlockTx(uint256 blockHash, std::vector<uint64_t>& vHashesToRequest);
+    CXThinBlockTx() {}
+
+    ADD_SERIALIZE_METHODS;
+
+    template <typename Stream, typename Operation>
+    inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+        READWRITE(blockhash);
+        READWRITE(mapTx);
+    }
+};
+#endif // BITCOIN_THINBLOCK_H
--- a/src/unlimited.cpp	Thu Feb 04 15:00:56 2016 -0500
+++ b/src/unlimited.cpp	Fri Jan 15 21:37:25 2016 -0800
@@ -2,22 +2,29 @@
 // Distributed under the MIT software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
-#include <inttypes.h>
+#include "chain.h"
+#include "clientversion.h"
+#include "chainparams.h"
+#include "consensus/consensus.h"
+#include "consensus/params.h"
+#include "consensus/validation.h"
+#include "leakybucket.h"
+#include "main.h"
+#include "net.h"
+#include "primitives/block.h"
+#include "rpcserver.h"
+#include "thinblock.h"
+#include "tinyformat.h"
+#include "txmempool.h"
+#include "unlimited.h"
+#include "utilstrencodings.h"
+#include "util.h"
+#include "validationinterface.h"
+#include "version.h"
 
-#include "util.h"
-#include "version.h"
-#include "unlimited.h"
-#include "clientversion.h"
-#include "rpcserver.h"
-#include "utilstrencodings.h"
-#include "tinyformat.h"
-#include "leakybucket.h"
-#include "primitives/block.h"
-#include "chain.h"
-#include "net.h"
-#include "txmempool.h"
 #include <boost/foreach.hpp>
 #include <boost/lexical_cast.hpp>
+#include <inttypes.h>
 
 using namespace std;
 
@@ -45,6 +52,8 @@
     strUsage += HelpMessageOpt("-sendburst", _("The maximum rate that data can be sent in kB/s.  If there has been a period of lower than average data rates, the client may send extra data to bring the average back to '-receiveavg' but the data rate will not exceed this parameter."));
     strUsage += HelpMessageOpt("-receiveavg", _("The average rate that data can be received in kB/s"));
     strUsage += HelpMessageOpt("-sendavg", _("The maximum rate that data can be sent in kB/s"));
+    strUsage += HelpMessageOpt("-use-thinblocks=<n>", strprintf(_("Turn Thinblocks on or off (off: 0, on: 1, default: %d)"), 1));
+    strUsage += HelpMessageOpt("-connect-thinblock=<ip:port>", _("Connect to a thinblock node(s). Blocks will only be downloaded from a thinblock peer.  If no connections are possible then regular blocks will then be downloaded form any other connected peers."));
     return strUsage;
 }
 
@@ -480,3 +489,209 @@
     return NullUniValue;
 }
 
+/**
+ *  BUIP010 Xtreme Thinblocks Section 
+ */
+bool HaveThinblockNodeConnections()
+{
+    // Strip the port from then list of all the current in and outbound ip addresses
+    std::vector<std::string> vNodesIP;
+    {
+        LOCK(cs_vNodes);
+        BOOST_FOREACH (CNode* pnode, vNodes) {
+           int pos = pnode->addrName.find(":");
+           if (pos <= 0 )
+               vNodesIP.push_back(pnode->addrName);
+           else
+               vNodesIP.push_back(pnode->addrName.substr(0, pos));
+        }
+    }
+
+    // Create a set used to check for cross connected nodes.
+    // A cross connected node is one where we have a connect-thinblock connection to
+    // but we also have another inbound connection which is also using
+    // connect-thinblock. In those cases we have created a dead-lock where no blocks 
+    // can be downloaded unless we also have at least one additional connect-thinblock 
+    // connection to a different node.
+    std::set<std::string> nNotCrossConnected;
+ 
+    int nConnectionsOpen = 0;
+    BOOST_FOREACH(const std::string& strAddrNode, mapMultiArgs["-connect-thinblock"]) {
+        std::string strThinblockNode;
+        int pos = strAddrNode.find(":");
+        if (pos <= 0 )
+            strThinblockNode = strAddrNode;
+        else
+            strThinblockNode = strAddrNode.substr(0, pos);
+        BOOST_FOREACH(std::string strAddr, vNodesIP) {
+            if (strAddr == strThinblockNode) {
+                nConnectionsOpen++;
+                if (!nNotCrossConnected.count(strAddr))
+                    nNotCrossConnected.insert(strAddr);
+                else
+                    nNotCrossConnected.erase(strAddr);
+            }
+        }
+    }
+    if (nNotCrossConnected.size() > 0)
+        return true;
+    else if (nConnectionsOpen > 0)
+        LogPrint("thin", "You have a cross connected thinblock node - we may download regular blocks until you resolve the issue\n");
+    return false; // Connections are either not open or they are cross connected.
+} 
+
+bool IsThinBlocksEnabled() 
+{
+    return GetBoolArg("-use-thinblocks", true);
+}
+
+bool IsChainNearlySyncd() 
+{
+    LOCK(cs_main);
+    if(chainActive.Height() < pindexBestHeader->nHeight - 2)
+        return false;
+    return true;
+}
+
+void SendSeededBloomFilter(CNode *pto)
+{
+    LogPrint("thin", "Starting creation of bloom filter\n");
+    seed_insecure_rand();
+    CBloomFilter memPoolFilter;
+    double nBloomPoolSize = (double)mempool.mapTx.size();
+    if (nBloomPoolSize > MAX_BLOOM_FILTER_SIZE / 1.8)
+        nBloomPoolSize = MAX_BLOOM_FILTER_SIZE / 1.8;
+    double nBloomDecay = 1.5 - (nBloomPoolSize * 1.8 / MAX_BLOOM_FILTER_SIZE);  // We should never go below 0.5 as we will start seeing re-requests for tx's
+    int nElements = std::max((int)((int)mempool.mapTx.size() * nBloomDecay), 1); // Must make sure nElements is greater than zero or will assert
+                                                                // TODO: we should probably rather fix the bloom.cpp constructor
+    double nFPRate = .001 + (((double)nElements * 1.8 / MAX_BLOOM_FILTER_SIZE) * .004); // The false positive rate in percent decays as the mempool grows
+    memPoolFilter = CBloomFilter(nElements, nFPRate, insecure_rand(), BLOOM_UPDATE_ALL);
+    LogPrint("thin", "Bloom multiplier: %f FPrate: %f Num elements in bloom filter: %d num mempool entries: %d\n", nBloomDecay, nFPRate, nElements, (int)mempool.mapTx.size());
+
+    // Seed the filter with the transactions in the memory pool, orphan pool and relay pool
+    LOCK2(cs_main, pto->cs_filter);
+    std::vector<uint256> memPoolHashes;
+    mempool.queryHashes(memPoolHashes);
+    for (uint64_t i = 0; i < memPoolHashes.size(); i++)
+         memPoolFilter.insert(memPoolHashes[i]);
+
+    LogPrint("thin", "Sending bloom filter: %d bytes peer=%d\n",::GetSerializeSize(memPoolFilter, SER_NETWORK, PROTOCOL_VERSION), pto->id);
+    pto->PushMessage(NetMsgType::FILTERLOAD, memPoolFilter);
+}
+
+void HandleBlockMessage(CNode *pfrom, const string &strCommand, CBlock &block, const CInv &inv)
+{
+    int64_t startTime = GetTimeMicros();
+    CValidationState state;
+    // Process all blocks from whitelisted peers, even if not requested,
+    // unless we're still syncing with the network.
+    // Such an unrequested block may still be processed, subject to the
+    // conditions in AcceptBlock().
+    bool forceProcessing = pfrom->fWhitelisted && !IsInitialBlockDownload();
+    const CChainParams& chainparams = Params();
+    ProcessNewBlock(state, chainparams, pfrom, &block, forceProcessing, NULL);
+    int nDoS;
+    if (state.IsInvalid(nDoS)) {
+        LogPrintf("Invalid block due to %s\n", state.GetRejectReason().c_str());
+        pfrom->PushMessage("reject", strCommand, state.GetRejectCode(),
+                           state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash);
+        if (nDoS > 0) {
+            LOCK(cs_main);
+            Misbehaving(pfrom->GetId(), nDoS);
+        }
+    }
+    LogPrint("thin", "Processed block %s in %.2f seconds\n", inv.hash.ToString(), (double)(GetTimeMicros() - startTime) / 1000000.0);
+
+    // When we request a thinblock we may get back a regular block if it is smaller than a thinblock
+    // Therefore we have to remove thinblock in flight if it exists and we also need to check that 
+    // the block didn't arrive from some other peer.
+    {
+        LOCK(cs_vNodes);
+        BOOST_FOREACH(CNode* pnode, vNodes) {
+            if (pnode->mapThinBlocksInFlight.count(inv.hash)) {
+                pnode->mapThinBlocksInFlight.erase(inv.hash); 
+                pnode->thinBlockWaitingForTxns = -1;
+                LogPrintf("Removing Thinblock in flight %s  peer=%d\n",inv.hash.ToString(), pnode->id);
+            }
+        }
+    }
+}
+
+void ConnectToThinBlockNodes()
+{
+    // Connect to specific addresses
+    if (mapArgs.count("-connect-thinblock") && mapMultiArgs["-connect-thinblock"].size() > 0)
+    {
+        BOOST_FOREACH(const std::string& strAddr, mapMultiArgs["-connect-thinblock"])
+        {
+            CAddress addr;
+            OpenNetworkConnection(addr, NULL, strAddr.c_str());
+            MilliSleep(500);
+        }
+    }
+}
+
+void CheckNodeSupportForThinBlocks()
+{
+    // Check that a nodes pointed to with connect-thinblock actually supports thinblocks
+    BOOST_FOREACH(string& strAddr, mapMultiArgs["-connect-thinblock"]) {
+        if(CNode* pnode = FindNode(strAddr)) {
+            if(pnode->nVersion < THINBLOCKS_VERSION && pnode->nVersion > 0) {
+                LogPrintf("ERROR: You are trying to use connect-thinblocks but to a node that does not support it - Protocol Version: %d peer=%d\n", 
+                           pnode->nVersion, pnode->id);
+            }
+        }
+    }
+}
+
+void SendXThinBlock(CBlock &block, CNode* pfrom, const CInv &inv)
+{
+    if (inv.type == MSG_XTHINBLOCK)
+    {
+        CXThinBlock xThinBlock(block, *pfrom->pfilter);
+        int nSizeBlock = ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION);
+        if (xThinBlock.collision == true) // If there is a cheapHash collision in this block then send a normal thinblock
+        {
+            CThinBlock thinBlock(block, *pfrom->pfilter);
+            int nSizeThinBlock = ::GetSerializeSize(xThinBlock, SER_NETWORK, PROTOCOL_VERSION);
+            if (nSizeThinBlock < nSizeBlock) {
+                pfrom->PushMessage(NetMsgType::THINBLOCK, thinBlock);
+                LogPrint("thin", "TX HASH COLLISION: Sent thinblock - size: %d vs block size: %d => tx hashes: %d transactions: %d  peerid=%d\n", nSizeThinBlock, nSizeBlock, xThinBlock.vTxHashes.size(), xThinBlock.mapMissingTx.size(), pfrom->id);
+            }
+            else {
+                pfrom->PushMessage(NetMsgType::BLOCK, block);
+                LogPrint("thin", "Sent regular block instead - xthinblock size: %d vs block size: %d => tx hashes: %d transactions: %d  peerid=%d\n", nSizeThinBlock, nSizeBlock, xThinBlock.vTxHashes.size(), xThinBlock.mapMissingTx.size(), pfrom->id);
+            }
+        }
+        else // Send an xThinblock
+        {
+            // Only send a thinblock if smaller than a regular block
+            int nSizeThinBlock = ::GetSerializeSize(xThinBlock, SER_NETWORK, PROTOCOL_VERSION);
+            if (nSizeThinBlock < nSizeBlock) {
+                pfrom->PushMessage(NetMsgType::XTHINBLOCK, xThinBlock);
+                LogPrint("thin", "Sent xthinblock - size: %d vs block size: %d => tx hashes: %d transactions: %d  peerid=%d\n", nSizeThinBlock, nSizeBlock, xThinBlock.vTxHashes.size(), xThinBlock.mapMissingTx.size(), pfrom->id);
+            }
+            else {
+                pfrom->PushMessage(NetMsgType::BLOCK, block);
+                LogPrint("thin", "Sent regular block instead - xthinblock size: %d vs block size: %d => tx hashes: %d transactions: %d  peerid=%d\n", nSizeThinBlock, nSizeBlock, xThinBlock.vTxHashes.size(), xThinBlock.mapMissingTx.size(), pfrom->id);
+            }
+        }
+    }
+    else if (inv.type == MSG_THINBLOCK)
+    {
+        CThinBlock thinBlock(block, *pfrom->pfilter);
+        int nSizeBlock = ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION);
+        int nSizeThinBlock = ::GetSerializeSize(thinBlock, SER_NETWORK, PROTOCOL_VERSION);
+        if (nSizeThinBlock < nSizeBlock) { // Only send a thinblock if smaller than a regular block
+            pfrom->PushMessage(NetMsgType::THINBLOCK, thinBlock);
+            LogPrint("thin", "Sent thinblock - size: %d vs block size: %d => tx hashes: %d transactions: %d  peerid=%d\n", nSizeThinBlock, nSizeBlock, thinBlock.vTxHashes.size(), thinBlock.mapMissingTx.size(), pfrom->id);
+        }
+        else {
+            pfrom->PushMessage(NetMsgType::BLOCK, block);
+            LogPrint("thin", "Sent regular block instead - thinblock size: %d vs block size: %d => tx hashes: %d transactions: %d  peerid=%d\n", nSizeThinBlock, nSizeBlock, thinBlock.vTxHashes.size(), thinBlock.mapMissingTx.size(), pfrom->id);
+        }
+    }
+
+}
+
+
--- a/src/unlimited.h	Thu Feb 04 15:00:56 2016 -0500
+++ b/src/unlimited.h	Fri Jan 15 21:37:25 2016 -0800
@@ -6,6 +6,7 @@
 #define BITCOIN_UNLIMITED_H
 
 #include "leakybucket.h"
+#include "net.h"
 #include <univalue.h>
 
 enum {
@@ -19,6 +20,7 @@
 class CBlockIndex;
 class CValidationState;
 class CDiskBlockPos;
+class CNode;
 
 extern uint64_t maxGeneratedBlock;
 extern unsigned int excessiveBlockSize;
@@ -54,5 +56,14 @@
 extern CLeakyBucket receiveShaper;
 extern CLeakyBucket sendShaper;
 
+// BUIP010 Xtreme Thinblocks:
+extern bool HaveThinblockNodeConnections();
+extern bool IsThinBlocksEnabled();
+extern bool IsChainNearlySyncd();
+extern void SendSeededBloomFilter(CNode *pto);
+extern void HandleBlockMessage(CNode *pfrom, const std::string &strCommand, CBlock &block, const CInv &inv);
+extern void ConnectToThinBlockNodes();
+extern void CheckNodeSupportForThinBlocks();
+extern void SendXThinBlock(CBlock &block, CNode* pfrom, const CInv &inv);
 
 #endif
--- a/src/version.h	Thu Feb 04 15:00:56 2016 -0500
+++ b/src/version.h	Fri Jan 15 21:37:25 2016 -0800
@@ -9,7 +9,7 @@
  * network protocol versioning
  */
 
-static const int PROTOCOL_VERSION = 70012;
+static const int PROTOCOL_VERSION = 80000;
 
 //! initial proto version, to be increased after version/verack negotiation
 static const int INIT_PROTO_VERSION = 209;
@@ -40,4 +40,7 @@
 //! "sendheaders" command and announcing blocks with headers starts with this version
 static const int SENDHEADERS_VERSION = 70012;
 
+//! Thinblocks enabled in this version
+static const int THINBLOCKS_VERSION = 80000;
+
 #endif // BITCOIN_VERSION_H