Files
thehub/hub/server/thinblock.cpp
tomFlowee 008eb35f95 Make compile faster
The IDE include checker got to the point where it is actually useful and
this removes a lot of unneeded includes.
Naturally, especially for headers like util.h, this may mean we need to
re-add includes in consuming cpp files that bloats the diff a bit.
2026-05-14 13:27:17 +02:00

592 lines
24 KiB
C++

/*
* This file is part of the Flowee project
* Copyright (C) 2016 The Bitcoin Unlimited developers
* Copyright (C) 2016-2017 Tom Zander <tom@flowee.org>
*
* 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/>.
*/
#include "thinblock.h"
#include "Application.h"
#include <validation/Engine.h>
#include "primitives/Block.h"
#include "main.h"
#include "txmempool.h"
#include "BlocksDB.h"
#include "utiltime.h"
#include "txorphancache.h"
#include <merkle.h>
#include "consensus/validation.h"
#include "policy/policy.h"
#include <BitcoinVersion.h>
#include <boost/foreach.hpp>
#include <boost/thread.hpp>
#include <boost/lexical_cast.hpp>
std::map<uint256, uint64_t> mapThinBlockTimer;
std::vector<CNode*> xpeditedBlk; // Who requested expedited blocks from us
std::vector<CNode*> xpeditedBlkUp; // Who we requested expedited blocks from
std::vector<CNode*> xpeditedTxn;
CXThinBlock::CXThinBlock(const MutableBlock& block, CBloomFilter* filter)
: collision(false)
{
headerData = block;
unsigned int nTx = block.vtx.size();
vTxHashes.reserve(nTx);
std::set<uint64_t> setPartialTxHash;
for (unsigned int i = 0; i < nTx; i++) {
const uint256 hash256 = block.vtx[i].GetHash();
const uint64_t cheapHash = hash256.GetCheapHash();
vTxHashes.push_back(cheapHash);
if (collision || setPartialTxHash.count(cheapHash))
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 (i == 0 || (filter && !filter->contains(hash256)))
vMissingTx.push_back(block.vtx[i]);
}
}
CXThinBlock::CXThinBlock()
: collision(false)
{
}
bool CXThinBlock::process(CNode* pfrom)
{
pfrom->thinBlock = MutableBlock(headerData);
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;
std::uint32_t blockSize = ::GetSerializeSize(pfrom->thinBlock, SER_NETWORK, PROTOCOL_VERSION);
const std::uint32_t blockSizeAcceptLimit = Policy::blockSizeAcceptLimit();
{
LOCK2(cs_main, mempool.cs);
const bool isChainTip = (headerData.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;
}
}
blockSize += tx.GetSerializeSize(0, 0);
if (blockSize <= blockSizeAcceptLimit)
pfrom->thinBlock.vtx.push_back(tx);
if (tx.IsNull())
missingCount++;
}
}
pfrom->thinBlockWaitingForTxns = missingCount; // TODO can that variable be removed from the CNode?
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);
pfrom->mapThinBlocksInFlight.erase(pfrom->thinBlock.createHash());
LOCK(cs_main);
Misbehaving(pfrom->id, score);
return false;
}
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());
CTxOrphanCache::instance()->eraseOrphans(orphansUsed);
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
CXRequestThinBlockTx thinBlockTx(createHash(), setHashesToRequest);
pfrom->PushMessage(NetMsgType::GET_XBLOCKTX, thinBlockTx);
LogPrint("thin", "Missing %d transactions for xthinblock, re-requesting\n", pfrom->thinBlockWaitingForTxns);
return false;
}
BlockHeader CXThinBlock::header() const
{
BlockHeader bh;
bh.nVersion = headerData.nVersion;
bh.nTime = headerData.nTime;
bh.nBits = headerData.nBits;
bh.nNonce = headerData.nNonce;
bh.hashPrevBlock = headerData.hashPrevBlock;
bh.hashMerkleRoot = headerData.hashMerkleRoot;
return bh;
}
CXThinBlockTx::CXThinBlockTx(uint256 blockHash, std::vector<CTransaction>& vTx)
{
blockhash = blockHash;
vMissingTx = vTx;
}
CXRequestThinBlockTx::CXRequestThinBlockTx(uint256 blockHash, std::set<uint64_t>& setHashesToRequest)
{
blockhash = blockHash;
setCheapHashesToRequest = setHashesToRequest;
}
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)
{
if (!filter->isWithinSizeConstraints()) {
// There is no excuse for sending a too-large filter
LOCK(cs_main);
Misbehaving(pfrom->GetId(), 100);
} else {
LOCK(pfrom->cs_filter);
delete pfrom->pThinBlockFilter;
pfrom->pThinBlockFilter = new CBloomFilter(*filter);
pfrom->pThinBlockFilter->updateEmptyFull();
}
uint64_t nSizeFilter = ::GetSerializeSize(*pfrom->pThinBlockFilter, SER_NETWORK, PROTOCOL_VERSION);
LogPrint("thin", "Thinblock Bloom filter size: %d\n", nSizeFilter);
}
void HandleBlockMessage(CNode *pfrom, const std::string &strCommand, const MutableBlock &block, const CInv &inv) // TODO pass hash, not CInv
{
logDebug(107) << strCommand << block.createHash();
auto *bv = Application::instance()->validation();
auto settings = bv->addBlock(Block::fromOldBlock(block),
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
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, (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);
#endif
// 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) {
if (pnode->mapThinBlocksInFlight.erase(inv.hash)) {
pnode->thinBlockWaitingForTxns = -1;
pnode->thinBlock.setNull();
}
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();
}
}
}
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 {
LogPrint("thin", "Sending expedited block %s to %s.\n", thinBlock.createHash().ToString(), node->addrName.c_str());
node->PushMessage(NetMsgType::XPEDITEDBLK, (unsigned char) EXPEDITED_MSG_XTHIN, hops, thinBlock);
// ++node->blocksSent;
}
}
}
}
void SendExpeditedBlock(const MutableBlock& block,const CNode* skip)
{
if (!IsRecentlyExpeditedAndStore(block.createHash())) {
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;
CBlockIndex *blockIndex = Blocks::Index::get(thinBlock.createHash());
unsigned int status = 0;
if (blockIndex) {
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
const CInv inv(MSG_BLOCK, thinBlock.createHash());
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
if (IsRecentlyExpeditedAndStore(thinBlock.createHash()))
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);
}
}