2017-11-09 19:34:51 +01:00
/*
* This file is part of the Flowee project
* Copyright (C) 2016 The Bitcoin Unlimited developers
2021-06-20 22:44:44 +02:00
* Copyright (C) 2016-2017 Tom Zander <tom@flowee.org>
2017-11-09 19:34:51 +01:00
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
2016-01-15 21:37:25 -08:00
# include "thinblock.h"
2016-03-08 13:54:41 -08:00
# include <sstream>
# include <iomanip>
2018-01-15 15:26:12 +00:00
# include "Application.h"
# include <validation/Engine.h>
2021-11-02 10:23:33 +01:00
# include "primitives/Block.h"
2016-07-20 12:40:07 +02:00
# include "main.h"
# include "txmempool.h"
2017-02-14 11:17:46 +01:00
# include "BlocksDB.h"
2017-01-31 10:03:43 +01:00
# include "utilstrencodings.h"
2017-02-08 17:28:21 +01:00
# include "txorphancache.h"
2020-04-01 22:51:47 +02:00
# include <merkle.h>
2017-02-08 17:28:21 +01:00
# include "consensus/validation.h"
2017-04-25 13:21:55 +02:00
# include "policy/policy.h"
2016-07-20 12:40:07 +02:00
# include <boost/foreach.hpp>
# include <boost/thread.hpp>
2017-02-08 17:28:21 +01:00
# include <boost/lexical_cast.hpp>
2016-07-20 12:40:07 +02:00
std : : map < uint256 , uint64_t > mapThinBlockTimer ;
2017-02-08 17:28:21 +01:00
std : : vector < CNode * > xpeditedBlk ; // Who requested expedited blocks from us
std : : vector < CNode * > xpeditedBlkUp ; // Who we requested expedited blocks from
std : : vector < CNode * > xpeditedTxn ;
2021-11-02 09:36:09 +01:00
CXThinBlock : : CXThinBlock ( const MutableBlock & block , CBloomFilter * filter )
2017-01-31 10:03:43 +01:00
: collision ( false )
2016-01-15 21:37:25 -08:00
{
2021-11-02 09:36:09 +01:00
header = block . blockHeader ( ) ;
2016-01-15 21:37:25 -08:00
2016-03-03 15:59:24 -08:00
unsigned int nTx = block . vtx . size ( ) ;
vTxHashes . reserve ( nTx ) ;
2016-01-15 21:37:25 -08:00
std : : set < uint64_t > setPartialTxHash ;
2017-01-31 10:03:43 +01:00
for ( unsigned int i = 0 ; i < nTx ; i + + ) {
2016-01-15 21:37:25 -08:00
const uint256 hash256 = block . vtx [ i ] . GetHash ( ) ;
2017-02-01 15:43:54 +01:00
const uint64_t cheapHash = hash256 . GetCheapHash ( ) ;
2016-01-15 21:37:25 -08:00
vTxHashes . push_back ( cheapHash ) ;
2017-01-31 10:03:43 +01:00
if ( collision | | setPartialTxHash . count ( cheapHash ) )
collision = true ;
2016-01-15 21:37:25 -08:00
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.
2017-01-31 10:03:43 +01:00
if ( i = = 0 | | ( filter & & ! filter - > contains ( hash256 ) ) )
2016-03-08 10:32:53 -08:00
vMissingTx . push_back ( block . vtx [ i ] ) ;
2016-01-15 21:37:25 -08:00
}
}
2017-01-31 10:03:43 +01:00
CXThinBlock : : CXThinBlock ( )
: collision ( false )
2016-02-18 10:05:23 -05:00
{
}
2017-02-08 17:28:21 +01:00
bool CXThinBlock : : process ( CNode * pfrom )
{
2021-11-02 09:36:09 +01:00
pfrom - > thinBlock = MutableBlock ( header ) ;
2017-02-08 17:28:21 +01:00
pfrom - > xThinBlockHashes = vTxHashes ;
// Create the mapMissingTx from all the supplied tx's in the xthinblock
std : : map < uint64_t , CTransaction > mapMissingTx ;
BOOST_FOREACH ( CTransaction tx , vMissingTx ) {
mapMissingTx [ tx . GetHash ( ) . GetCheapHash ( ) ] = tx ;
}
std : : map < uint64_t , uint256 > orphanLookup ;
{
auto orphans = CTxOrphanCache : : instance ( ) - > mapOrphanTransactions ( ) ;
BOOST_FOREACH ( auto iter , orphans ) {
orphanLookup . insert ( std : : make_pair ( iter . first . GetCheapHash ( ) , iter . first ) ) ;
}
}
std : : map < uint64_t , uint256 > mempoolLookup ;
{
std : : vector < uint256 > memPoolHashes ;
mempool . queryHashes ( memPoolHashes ) ;
for ( size_t i = 0 ; i < memPoolHashes . size ( ) ; + + i ) {
const uint256 & hash = memPoolHashes . at ( i ) ;
mempoolLookup . insert ( std : : make_pair ( hash . GetCheapHash ( ) , hash ) ) ;
}
}
std : : vector < uint256 > orphansUsed ;
int missingCount = 0 ;
int collisionCount = 0 ;
2017-04-25 13:21:55 +02:00
std : : uint32_t blockSize = : : GetSerializeSize ( pfrom - > thinBlock , SER_NETWORK , PROTOCOL_VERSION ) ;
const std : : uint32_t blockSizeAcceptLimit = Policy : : blockSizeAcceptLimit ( ) ;
2017-02-08 17:28:21 +01:00
{
LOCK2 ( cs_main , mempool . cs ) ;
const bool isChainTip = ( header . hashPrevBlock = = chainActive . Tip ( ) - > GetBlockHash ( ) ) ? true : false ;
for ( size_t i = 0 ; i < vTxHashes . size ( ) ; + + i ) {
const uint64_t cheapHash = vTxHashes . at ( i ) ;
// Now we find the full transaction.
CTransaction tx ;
auto foundInMissing = mapMissingTx . find ( cheapHash ) ;
if ( foundInMissing ! = mapMissingTx . end ( ) ) {
tx = foundInMissing - > second ;
}
auto foundInMempool = mempoolLookup . find ( cheapHash ) ;
if ( foundInMempool ! = mempoolLookup . end ( ) ) {
if ( tx . IsNull ( ) ) {
if ( isChainTip ) // only skip validation if we are constructing the new chaintip
setPreVerifiedTxHash . insert ( foundInMempool - > second ) ;
/*bool found =*/ mempool . lookup ( foundInMempool - > second , tx ) ;
} else {
+ + collisionCount ;
}
}
auto foundInOrphan = orphanLookup . find ( cheapHash ) ;
if ( foundInOrphan ! = orphanLookup . end ( ) ) {
if ( tx . IsNull ( ) ) {
bool found = CTxOrphanCache : : value ( foundInOrphan - > second , tx ) ;
if ( found ) // a race condition may have caused it to be removed from the orphans cache
orphansUsed . push_back ( foundInOrphan - > second ) ;
} else {
+ + collisionCount ;
}
}
2017-04-25 13:21:55 +02:00
blockSize + = tx . GetSerializeSize ( 0 , 0 ) ;
if ( blockSize < = blockSizeAcceptLimit )
pfrom - > thinBlock . vtx . push_back ( tx ) ;
2017-02-08 17:28:21 +01:00
if ( tx . IsNull ( ) )
missingCount + + ;
}
}
pfrom - > thinBlockWaitingForTxns = missingCount ; // TODO can that variable be removed from the CNode?
2017-04-25 13:21:55 +02:00
if ( blockSize > blockSizeAcceptLimit ) {
pfrom - > thinBlockWaitingForTxns = - 1 ;
pfrom - > thinBlock . vtx . clear ( ) ;
const float punishment = ( blockSize - blockSizeAcceptLimit ) / ( float ) blockSizeAcceptLimit ;
const int score = 10 * punishment + 0.5 ;
LogPrintf ( " thinblock (partially) reconstructed is over accept limits; (%d > %d), Dropping block and punishing (%d) peer %d \n " ,
blockSize , blockSizeAcceptLimit , score , pfrom - > id ) ;
2021-11-02 09:28:35 +01:00
pfrom - > mapThinBlocksInFlight . erase ( pfrom - > thinBlock . createHash ( ) ) ;
2017-04-25 13:21:55 +02:00
LOCK ( cs_main ) ;
Misbehaving ( pfrom - > id , score ) ;
return false ;
}
2017-02-08 17:28:21 +01:00
if ( missingCount = = 0 ) {
bool mutated ;
uint256 hashMerkleRoot2 = BlockMerkleRoot ( pfrom - > thinBlock , & mutated ) ;
if ( pfrom - > thinBlock . hashMerkleRoot ! = hashMerkleRoot2 ) {
LogPrint ( " thin " , " thinblock fully constructed, but merkle hash failed. Rejecting \n " ) ;
// If we hit this often, we should consider writing more code above to remember duplicate
// short hashes in our maps and also remember duplicate results from the different sources
// like the orphans and the mempool etc.
// With all these options we can then try different combinations and see which one gives us a proper merkle root.
// We'll wait for an INV to get this block, rejecting it for now.
pfrom - > thinBlockWaitingForTxns = - 1 ;
return false ;
}
}
LogPrint ( " thin " , " thinblock waiting for: %d, txs: %d full: %d \n " , pfrom - > thinBlockWaitingForTxns , pfrom - > thinBlock . vtx . size ( ) , mapMissingTx . size ( ) ) ;
if ( missingCount = = 0 ) {
// We have all the transactions now that are in this block: try to reassemble and process.
pfrom - > thinBlockWaitingForTxns = - 1 ;
pfrom - > AddInventoryKnown ( GetInv ( ) ) ;
2019-11-21 20:03:26 +01:00
CTxOrphanCache : : instance ( ) - > eraseOrphans ( orphansUsed ) ;
2017-02-08 17:28:21 +01:00
return true ;
}
// 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 : : set < uint64_t > setHashesToRequest ;
for ( size_t i = 0 ; i < pfrom - > thinBlock . vtx . size ( ) ; i + + ) {
if ( pfrom - > thinBlock . vtx [ i ] . IsNull ( ) )
setHashesToRequest . insert ( pfrom - > xThinBlockHashes [ i ] ) ;
}
// Re-request transactions that we are still missing
2021-11-02 09:28:35 +01:00
CXRequestThinBlockTx thinBlockTx ( header . createHash ( ) , setHashesToRequest ) ;
2017-02-08 17:28:21 +01:00
pfrom - > PushMessage ( NetMsgType : : GET_XBLOCKTX , thinBlockTx ) ;
LogPrint ( " thin " , " Missing %d transactions for xthinblock, re-requesting \n " , pfrom - > thinBlockWaitingForTxns ) ;
return false ;
}
2016-03-08 10:52:19 -08:00
CXThinBlockTx : : CXThinBlockTx ( uint256 blockHash , std : : vector < CTransaction > & vTx )
2016-01-15 21:37:25 -08:00
{
blockhash = blockHash ;
2016-03-08 10:52:19 -08:00
vMissingTx = vTx ;
}
2016-01-15 21:37:25 -08:00
2016-03-08 10:52:19 -08:00
CXRequestThinBlockTx : : CXRequestThinBlockTx ( uint256 blockHash , std : : set < uint64_t > & setHashesToRequest )
{
blockhash = blockHash ;
setCheapHashesToRequest = setHashesToRequest ;
2016-01-15 21:37:25 -08:00
}
2016-07-20 12:40:07 +02:00
bool HaveThinblockNodes ( )
{
LOCK ( cs_vNodes ) ;
BOOST_FOREACH ( CNode * pnode , vNodes ) {
if ( pnode - > ThinBlockCapable ( ) )
return true ;
}
return false ;
}
bool CheckThinblockTimer ( const uint256 & hash )
{
if ( ! mapThinBlockTimer . count ( hash ) ) {
mapThinBlockTimer [ hash ] = GetTimeMillis ( ) ;
LogPrint ( " thin " , " Starting Preferential Thinblock timer \n " ) ;
}
else {
// Check that we have not exceeded the 10 second limit.
// If we have then we want to return false so that we can
// proceed to download a regular block instead.
uint64_t elapsed = GetTimeMillis ( ) - mapThinBlockTimer [ hash ] ;
if ( elapsed > 10000 ) {
LogPrint ( " thin " , " Preferential Thinblock timer exceeded - downloading regular block instead \n " ) ;
return false ;
}
}
return true ;
}
bool IsChainNearlySyncd ( )
{
LOCK ( cs_main ) ;
if ( chainActive . Height ( ) < pindexBestHeader - > nHeight - 2 )
return false ;
return true ;
}
CBloomFilter createSeededBloomFilter ( const std : : vector < uint256 > & vOrphanHashes )
{
LogPrint ( " thin " , " Starting creation of bloom filter \n " ) ;
seed_insecure_rand ( ) ;
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 ( ) + ( int ) vOrphanHashes . size ( ) ) * nBloomDecay ) , 1 ) ; // Must make sure nElements is greater than zero or will assert
double nFPRate = .001 + ( ( ( double ) nElements * 1.8 / MAX_BLOOM_FILTER_SIZE ) * .004 ) ; // The false positive rate in percent decays as the mempool grows
CBloomFilter filterMemPool ( 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
LOCK ( cs_main ) ;
std : : vector < uint256 > vMemPoolHashes ;
mempool . queryHashes ( vMemPoolHashes ) ;
for ( uint64_t i = 0 ; i < vMemPoolHashes . size ( ) ; i + + )
filterMemPool . insert ( vMemPoolHashes [ i ] ) ;
for ( uint64_t i = 0 ; i < vOrphanHashes . size ( ) ; i + + )
filterMemPool . insert ( vOrphanHashes [ i ] ) ;
LogPrint ( " thin " , " Created bloom filter: %d bytes \n " , : : GetSerializeSize ( filterMemPool , SER_NETWORK , PROTOCOL_VERSION ) ) ;
return filterMemPool ;
}
void LoadFilter ( CNode * pfrom , CBloomFilter * filter )
{
2021-05-23 17:49:57 +02:00
if ( ! filter - > isWithinSizeConstraints ( ) ) {
2016-07-20 12:40:07 +02:00
// There is no excuse for sending a too-large filter
2017-02-08 17:28:21 +01:00
LOCK ( cs_main ) ;
2016-07-20 12:40:07 +02:00
Misbehaving ( pfrom - > GetId ( ) , 100 ) ;
2017-02-08 17:28:21 +01:00
} else {
2016-07-20 12:40:07 +02:00
LOCK ( pfrom - > cs_filter ) ;
delete pfrom - > pThinBlockFilter ;
pfrom - > pThinBlockFilter = new CBloomFilter ( * filter ) ;
2020-04-10 12:21:49 +02:00
pfrom - > pThinBlockFilter - > updateEmptyFull ( ) ;
2016-07-20 12:40:07 +02:00
}
uint64_t nSizeFilter = : : GetSerializeSize ( * pfrom - > pThinBlockFilter , SER_NETWORK , PROTOCOL_VERSION ) ;
LogPrint ( " thin " , " Thinblock Bloom filter size: %d \n " , nSizeFilter ) ;
}
2021-11-02 09:36:09 +01:00
void HandleBlockMessage ( CNode * pfrom , const std : : string & strCommand , const MutableBlock & block , const CInv & inv ) // TODO pass hash, not CInv
2016-07-20 12:40:07 +02:00
{
2021-11-02 09:28:35 +01:00
logDebug ( 107 ) < < strCommand < < block . createHash ( ) ;
2018-01-15 15:26:12 +00:00
auto * bv = Application : : instance ( ) - > validation ( ) ;
2021-11-02 10:18:24 +01:00
auto settings = bv - > addBlock ( Block : : fromOldBlock ( block ) ,
2018-01-15 15:26:12 +00:00
Validation : : ForwardGoodToPeers | Validation : : SaveGoodToDisk | Validation : : PunishBadNode , pfrom ) ;
// Clear the thinblock timer used for preferential download
mapThinBlockTimer . erase ( inv . hash ) ;
// settings.setPreApprovedTx(vector<int>) // TODO
// settings.setIsReassembledBlock(true); // TODO, avoid punishing a peer when the error is only merkle-root based
// settings.setOriginatingCommand(string); // to allow sending reject messages.
settings . start ( ) ;
/* TODO
* If the incoming block is due to xthin, I should be able to add data stating which
* transactions have been pre-validated.
*
* Do we have code returning a "reject" message to peer?
*
* Add a flag stating the block comes from xthin and should the error be in the merkle-root, then
* we need to re-request block instead of punishing peer.
*/
#if 0
2016-07-20 12:40:07 +02:00
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();
2018-01-15 15:26:12 +00:00
// const CChainParams& chainparams = Params();
2016-07-20 12:40:07 +02:00
ProcessNewBlock(state, chainparams, pfrom, &block, forceProcessing, NULL);
2018-01-15 15:26:12 +00:00
2016-07-20 12:40:07 +02:00
int nDoS;
if (state.IsInvalid(nDoS)) {
LogPrintf("Invalid block due to %s\n", state.GetRejectReason().c_str());
pfrom->PushMessage("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);
}
}
LogPrint("thin", "Processed Block %s in %.2f seconds\n", inv.hash.ToString(), (double)(GetTimeMicros() - startTime) / 1000000.0);
2018-01-15 15:26:12 +00:00
#endif
2016-07-20 12:40:07 +02:00
// When we request a thinblock we may get back a regular block if it is smaller than a thinblock
// Therefore we have to remove the thinblock in flight if it exists and we also need to check that
// the block didn't arrive from some other peer. This code ALSO cleans up the thin block that
// was passed to us (&block), so do not use it after this.
{
int nTotalThinBlocksInFlight = 0 ;
LOCK ( cs_vNodes ) ;
BOOST_FOREACH ( CNode * pnode , vNodes ) {
2018-01-15 15:26:12 +00:00
if ( pnode - > mapThinBlocksInFlight . erase ( inv . hash ) ) {
2016-07-20 12:40:07 +02:00
pnode - > thinBlockWaitingForTxns = - 1 ;
2021-11-02 09:28:35 +01:00
pnode - > thinBlock . setNull ( ) ;
2016-07-20 12:40:07 +02:00
}
if ( pnode - > mapThinBlocksInFlight . size ( ) > 0 )
nTotalThinBlocksInFlight + + ;
}
// When we no longer have any thinblocks in flight then clear the set
// just to make sure we don't somehow get growth over time.
if ( nTotalThinBlocksInFlight = = 0 ) {
setPreVerifiedTxHash . clear ( ) ;
setUnVerifiedOrphanTxHash . clear ( ) ;
}
}
}
2017-02-08 17:28:21 +01:00
void CheckAndRequestExpeditedBlocks ( CNode * pfrom )
{
if ( pfrom - > nVersion > = EXPEDITED_VERSION ) {
BOOST_FOREACH ( std : : string & strAddr , mapMultiArgs [ " -expeditedblock " ] ) {
// Add the peer's listening port if it is empty
int pos1 = strAddr . rfind ( " : " ) ;
int pos2 = strAddr . rfind ( " ]: " ) ;
if ( pos1 < = 0 & & pos2 < = 0 )
strAddr + = ' : ' + boost : : lexical_cast < std : : string > ( pfrom - > addrFromPort ) ;
std : : string strListeningPeerIP ;
std : : string strPeerIP = pfrom - > addr . ToString ( ) ;
pos1 = strPeerIP . rfind ( " : " ) ;
pos2 = strPeerIP . rfind ( " ]: " ) ;
// Handle both ipv4 and ipv6 cases
if ( pos1 < = 0 & & pos2 < = 0 )
strListeningPeerIP = strPeerIP + ' : ' + boost : : lexical_cast < std : : string > ( pfrom - > addrFromPort ) ;
else if ( pos1 > 0 )
strListeningPeerIP = strPeerIP . substr ( 0 , pos1 ) + ' : ' + boost : : lexical_cast < std : : string > ( pfrom - > addrFromPort ) ;
else
strListeningPeerIP = strPeerIP . substr ( 0 , pos2 ) + ' : ' + boost : : lexical_cast < std : : string > ( pfrom - > addrFromPort ) ;
if ( strAddr = = strListeningPeerIP ) {
if ( ! IsThinBlocksEnabled ( ) ) {
LogPrintf ( " You do not have Thinblocks enabled. You can not request expedited blocks from peer %s (%d). \n " , strListeningPeerIP , pfrom - > id ) ;
}
else if ( ! pfrom - > ThinBlockCapable ( ) ) {
LogPrintf ( " Thinblocks is not enabled on remote peer. You can not request expedited blocks from peer %s (%d). \n " , strListeningPeerIP , pfrom - > id ) ;
} else {
LogPrintf ( " Requesting expedited blocks from peer %s (%d). \n " , strListeningPeerIP , pfrom - > id ) ;
pfrom - > PushMessage ( NetMsgType : : XPEDITEDREQUEST , ( ( uint64_t ) EXPEDITED_BLOCKS ) ) ;
xpeditedBlkUp . push_back ( pfrom ) ;
}
return ;
}
}
}
}
void SendExpeditedBlock ( CXThinBlock & thinBlock , unsigned char hops , const CNode * skip )
{
std : : vector < CNode * > : : iterator end = xpeditedBlk . end ( ) ;
for ( std : : vector < CNode * > : : iterator it = xpeditedBlk . begin ( ) ; it ! = end ; it + + ) {
CNode * node = * it ;
if ( node & & node ! = skip ) { // Don't send it back in case there is a forwarding loop
if ( node - > fDisconnect ) {
* it = nullptr ;
node - > Release ( ) ;
} else {
2021-11-02 09:28:35 +01:00
LogPrint ( " thin " , " Sending expedited block %s to %s. \n " , thinBlock . header . createHash ( ) . ToString ( ) , node - > addrName . c_str ( ) ) ;
2017-02-08 17:28:21 +01:00
node - > PushMessage ( NetMsgType : : XPEDITEDBLK , ( unsigned char ) EXPEDITED_MSG_XTHIN , hops , thinBlock ) ;
// ++node->blocksSent;
}
}
}
}
2021-11-02 09:36:09 +01:00
void SendExpeditedBlock ( const MutableBlock & block , const CNode * skip )
2017-02-08 17:28:21 +01:00
{
2021-11-02 09:28:35 +01:00
if ( ! IsRecentlyExpeditedAndStore ( block . createHash ( ) ) ) {
2017-02-08 17:28:21 +01:00
CXThinBlock thinBlock ( block ) ;
SendExpeditedBlock ( thinBlock , 0 , skip ) ;
}
}
void HandleExpeditedRequest ( CDataStream & vRecv , CNode * pfrom )
{
// TODO locks
uint64_t options ;
vRecv > > options ;
bool stop = ( ( options & EXPEDITED_STOP ) ! = 0 ) ; // Are we starting or stopping expedited service?
if ( options & EXPEDITED_BLOCKS )
{
if ( stop ) // If stopping, find the array element and clear it.
{
LogPrint ( " blk " , " Stopping expedited blocks to peer %s (%d). \n " , pfrom - > addrName . c_str ( ) , pfrom - > id ) ;
std : : vector < CNode * > : : iterator it = std : : find ( xpeditedBlk . begin ( ) , xpeditedBlk . end ( ) , pfrom ) ;
if ( it ! = xpeditedBlk . end ( ) )
{
* it = NULL ;
pfrom - > Release ( ) ;
}
}
else // Otherwise, add the new node to the end
{
std : : vector < CNode * > : : iterator it1 = std : : find ( xpeditedBlk . begin ( ) , xpeditedBlk . end ( ) , pfrom ) ;
if ( it1 = = xpeditedBlk . end ( ) ) // don't add it twice
{
unsigned int maxExpedited = GetArg ( " -maxexpeditedblockrecipients " , 32 ) ;
if ( xpeditedBlk . size ( ) < maxExpedited )
{
LogPrint ( " blk " , " Starting expedited blocks to peer %s (%d). \n " , pfrom - > addrName . c_str ( ) , pfrom - > id ) ;
std : : vector < CNode * > : : iterator it = std : : find ( xpeditedBlk . begin ( ) , xpeditedBlk . end ( ) , ( ( CNode * ) NULL ) ) ;
if ( it ! = xpeditedBlk . end ( ) )
* it = pfrom ;
else
xpeditedBlk . push_back ( pfrom ) ;
pfrom - > AddRef ( ) ;
}
else
{
LogPrint ( " blk " , " Expedited blocks requested from peer %s (%d), but I am full. \n " , pfrom - > addrName . c_str ( ) , pfrom - > id ) ;
}
}
}
}
if ( options & EXPEDITED_TXNS )
{
if ( stop ) // If stopping, find the array element and clear it.
{
LogPrint ( " blk " , " Stopping expedited transactions to peer %s (%d). \n " , pfrom - > addrName . c_str ( ) , pfrom - > id ) ;
std : : vector < CNode * > : : iterator it = std : : find ( xpeditedTxn . begin ( ) , xpeditedTxn . end ( ) , pfrom ) ;
if ( it ! = xpeditedTxn . end ( ) )
{
* it = NULL ;
pfrom - > Release ( ) ;
}
}
else // Otherwise, add the new node to the end
{
std : : vector < CNode * > : : iterator it1 = std : : find ( xpeditedTxn . begin ( ) , xpeditedTxn . end ( ) , pfrom ) ;
if ( it1 = = xpeditedTxn . end ( ) ) // don't add it twice
{
unsigned int maxExpedited = GetArg ( " -maxexpeditedtxrecipients " , 32 ) ;
if ( xpeditedTxn . size ( ) < maxExpedited )
{
LogPrint ( " blk " , " Starting expedited transactions to peer %s (%d). \n " , pfrom - > addrName . c_str ( ) , pfrom - > id ) ;
std : : vector < CNode * > : : iterator it = std : : find ( xpeditedTxn . begin ( ) , xpeditedTxn . end ( ) , ( ( CNode * ) NULL ) ) ;
if ( it ! = xpeditedTxn . end ( ) )
* it = pfrom ;
else
xpeditedTxn . push_back ( pfrom ) ;
pfrom - > AddRef ( ) ;
}
else
{
LogPrint ( " blk " , " Expedited transactions requested from peer %s (%d), but I am full. \n " , pfrom - > addrName . c_str ( ) , pfrom - > id ) ;
}
}
}
}
}
// TODO make this not so ugly
# define NUM_XPEDITED_STORE 10
uint256 xpeditedBlkSent [ NUM_XPEDITED_STORE ] ; // Just save the last few expedited sent blocks so we don't resend (uint256 zeros on construction)
int xpeditedBlkSendPos = 0 ;
bool IsRecentlyExpeditedAndStore ( const uint256 & hash )
{
for ( int i = 0 ; i < NUM_XPEDITED_STORE ; i + + )
if ( xpeditedBlkSent [ i ] = = hash ) return true ;
xpeditedBlkSent [ xpeditedBlkSendPos ] = hash ;
xpeditedBlkSendPos + + ;
if ( xpeditedBlkSendPos > = NUM_XPEDITED_STORE ) xpeditedBlkSendPos = 0 ;
return false ;
}
void HandleExpeditedBlock ( CDataStream & vRecv , CNode * pfrom )
{
unsigned char hops ;
unsigned char msgType ;
vRecv > > msgType > > hops ;
if ( msgType = = EXPEDITED_MSG_XTHIN ) {
CXThinBlock thinBlock ;
vRecv > > thinBlock ;
2021-11-02 09:28:35 +01:00
CBlockIndex * blockIndex = Blocks : : Index : : get ( thinBlock . header . createHash ( ) ) ;
2017-02-08 17:28:21 +01:00
unsigned int status = 0 ;
2018-01-15 15:26:12 +00:00
if ( blockIndex ) {
2017-02-08 17:28:21 +01:00
status = blockIndex - > nStatus ;
}
bool isNewBlock = blockIndex = = nullptr | | ( ! ( blockIndex - > nStatus & BLOCK_HAVE_DATA ) ) ; // If I have never seen the block or just seen an INV, treat the block as new
const int nSizeThinBlock = : : GetSerializeSize ( thinBlock , SER_NETWORK , PROTOCOL_VERSION ) ; // TODO replace with size of vRecv for efficiency
2021-11-02 09:28:35 +01:00
const CInv inv ( MSG_BLOCK , thinBlock . header . createHash ( ) ) ;
2017-02-08 17:28:21 +01:00
LogPrint ( " thin " , " Received %s expedited thinblock %s from peer %s (%d). Hop %d. Size %d bytes. (status %d,0x%x) \n " , isNewBlock ? " new " : " repeated " ,
inv . hash . ToString ( ) , pfrom - > addrName . c_str ( ) , pfrom - > id , hops , nSizeThinBlock , status , status ) ;
// Skip if we've already seen this block
2021-11-02 09:28:35 +01:00
if ( IsRecentlyExpeditedAndStore ( thinBlock . header . createHash ( ) ) )
2017-02-08 17:28:21 +01:00
return ;
if ( ! isNewBlock ) {
// TODO determine if we have the block or just have an INV to it.
return ;
}
CValidationState state ;
if ( ! CheckBlockHeader ( thinBlock . header , state , true ) ) { // block header is bad
LOCK ( cs_main ) ;
Misbehaving ( pfrom - > id , 100 ) ;
return ;
}
// TODO: Start optimistic mining now
SendExpeditedBlock ( thinBlock , hops + 1 , pfrom ) ; // I should push the vRecv rather than reserialize
if ( thinBlock . process ( pfrom ) )
HandleBlockMessage ( pfrom , NetMsgType : : XPEDITEDBLK , pfrom - > thinBlock , thinBlock . GetInv ( ) ) ; // clears the thin block
} else {
LogPrint ( " thin " , " Received unknown (0x%x) expedited message from peer %s (%d). Hop %d. \n " , msgType , pfrom - > addrName . c_str ( ) , pfrom - > id , hops ) ;
}
}