/** We got some data for a ledger we are no longer acquiring Since we paid the price to receive it, we might as well stash it in case we need it. Nodes are received in wire format and must be stashed/hashed in prefix format */ void gotStaleData (std::shared_ptr<protocol::TMLedgerData> packet_ptr) { const uint256 uZero; Serializer s; try { for (int i = 0; i < packet_ptr->nodes ().size (); ++i) { auto const& node = packet_ptr->nodes (i); if (!node.has_nodeid () || !node.has_nodedata ()) return; SHAMapTreeNode newNode( SHAMapNode (node.nodeid().data(), node.nodeid().size()), Blob (node.nodedata().begin(), node.nodedata().end()), 0, snfWIRE, uZero, false); s.erase(); newNode.addRaw(s, snfPREFIX); auto blob = std::make_shared<Blob> (s.begin(), s.end()); getApp().getOPs().addFetchPack (newNode.getNodeHash(), blob); } } catch (...) { } }
// This can be optimized to avoid the << if needed SHAMapNode SHAMapNode::getChildNodeID (int m) const { assert ((m >= 0) && (m < 16)); uint256 child (mNodeID); child.begin ()[mDepth / 2] |= (mDepth & 1) ? m : (m << 4); return SHAMapNode (mDepth + 1, child, true); }
SHAMapAddNode SHAMap::addRootNode (Blob const& rootNode, SHANodeFormat format, SHAMapSyncFilter* filter) { ScopedLockType sl (mLock, __FILE__, __LINE__); // we already have a root node if (root->getNodeHash ().isNonZero ()) { WriteLog (lsTRACE, SHAMap) << "got root node, already have one"; return SHAMapAddNode::okay (); } assert (mSeq >= 1); SHAMapTreeNode::pointer node = boost::make_shared<SHAMapTreeNode> (SHAMapNode (), rootNode, mSeq - 1, format, uZero, false); if (!node) return SHAMapAddNode::invalid (); #ifdef BEAST_DEBUG node->dump (); #endif root = node; mTNByID[*root] = root; if (root->getNodeHash ().isZero ()) { root->setFullBelow (); clearSynching (); } else if (filter) { Serializer s; root->addRaw (s, snfPREFIX); filter->gotNode (false, *root, root->getNodeHash (), s.modData (), root->getType ()); } return SHAMapAddNode::useful (); }
// means "We got some data from an inbound ledger" void InboundLedgers::gotLedgerData (Job&, uint256 hash, boost::shared_ptr<protocol::TMLedgerData> packet_ptr, boost::weak_ptr<Peer> wPeer) { protocol::TMLedgerData& packet = *packet_ptr; Peer::pointer peer = wPeer.lock (); WriteLog (lsTRACE, InboundLedger) << "Got data (" << packet.nodes ().size () << ") for acquiring ledger: " << hash; InboundLedger::pointer ledger = find (hash); if (!ledger) { WriteLog (lsTRACE, InboundLedger) << "Got data for ledger we're not acquiring"; if (peer) peer->applyLoadCharge (LT_InvalidRequest); return; } ledger->noAwaitData (); if (!peer) return; if (packet.type () == protocol::liBASE) { if (packet.nodes_size () < 1) { WriteLog (lsWARNING, InboundLedger) << "Got empty base data"; peer->applyLoadCharge (LT_InvalidRequest); return; } if (!ledger->takeBase (packet.nodes (0).nodedata ())) { WriteLog (lsWARNING, InboundLedger) << "Got invalid base data"; peer->applyLoadCharge (LT_InvalidRequest); return; } SHAMapAddNode san = SHAMapAddNode::useful (); if ((packet.nodes ().size () > 1) && !ledger->takeAsRootNode (strCopy (packet.nodes (1).nodedata ()), san)) { WriteLog (lsWARNING, InboundLedger) << "Included ASbase invalid"; } if ((packet.nodes ().size () > 2) && !ledger->takeTxRootNode (strCopy (packet.nodes (2).nodedata ()), san)) { WriteLog (lsWARNING, InboundLedger) << "Included TXbase invalid"; } if (!san.isInvalid ()) { ledger->progress (); ledger->trigger (peer); } else WriteLog (lsDEBUG, InboundLedger) << "Peer sends invalid base data"; return; } if ((packet.type () == protocol::liTX_NODE) || (packet.type () == protocol::liAS_NODE)) { std::list<SHAMapNode> nodeIDs; std::list< Blob > nodeData; if (packet.nodes ().size () <= 0) { WriteLog (lsINFO, InboundLedger) << "Got response with no nodes"; peer->applyLoadCharge (LT_InvalidRequest); return; } for (int i = 0; i < packet.nodes ().size (); ++i) { const protocol::TMLedgerNode& node = packet.nodes (i); if (!node.has_nodeid () || !node.has_nodedata ()) { WriteLog (lsWARNING, InboundLedger) << "Got bad node"; peer->applyLoadCharge (LT_InvalidRequest); return; } nodeIDs.push_back (SHAMapNode (node.nodeid ().data (), node.nodeid ().size ())); nodeData.push_back (Blob (node.nodedata ().begin (), node.nodedata ().end ())); } SHAMapAddNode ret; if (packet.type () == protocol::liTX_NODE) ledger->takeTxNode (nodeIDs, nodeData, ret); else ledger->takeAsNode (nodeIDs, nodeData, ret); if (!ret.isInvalid ()) { ledger->progress (); ledger->trigger (peer); } else WriteLog (lsDEBUG, InboundLedger) << "Peer sends invalid node data"; return; } WriteLog (lsWARNING, InboundLedger) << "Not sure what ledger data we got"; peer->applyLoadCharge (LT_InvalidRequest); }
void runTest () { unsigned int seed; // VFALCO TODO Replace this with beast::Random RAND_pseudo_bytes (reinterpret_cast<unsigned char*> (&seed), sizeof (seed)); srand (seed); SHAMap source (smtFREE), destination (smtFREE); int items = 10000; for (int i = 0; i < items; ++i) source.addItem (*makeRandomAS (), false, false); beginTestCase ("add/remove"); unexpected (!confuseMap (source, 500), "ConfuseMap"); source.setImmutable (); std::vector<SHAMapNode> nodeIDs, gotNodeIDs; std::list< Blob > gotNodes; std::vector<uint256> hashes; std::vector<SHAMapNode>::iterator nodeIDIterator; std::list< Blob >::iterator rawNodeIterator; int passes = 0; int nodes = 0; destination.setSynching (); unexpected (!source.getNodeFat (SHAMapNode (), nodeIDs, gotNodes, (rand () % 2) == 0, (rand () % 2) == 0), "GetNodeFat"); unexpected (gotNodes.size () < 1, "NodeSize"); unexpected (!destination.addRootNode (*gotNodes.begin (), snfWIRE, NULL), "AddRootNode"); nodeIDs.clear (); gotNodes.clear (); #ifdef SMS_DEBUG int bytes = 0; #endif do { ++passes; hashes.clear (); // get the list of nodes we know we need destination.getMissingNodes (nodeIDs, hashes, 2048, NULL); if (nodeIDs.empty ()) break; // get as many nodes as possible based on this information for (nodeIDIterator = nodeIDs.begin (); nodeIDIterator != nodeIDs.end (); ++nodeIDIterator) { if (!source.getNodeFat (*nodeIDIterator, gotNodeIDs, gotNodes, (rand () % 2) == 0, (rand () % 2) == 0)) { WriteLog (lsFATAL, SHAMap) << "GetNodeFat fails"; fail ("GetNodeFat"); } else { pass (); } } assert (gotNodeIDs.size () == gotNodes.size ()); nodeIDs.clear (); hashes.clear (); if (gotNodeIDs.empty ()) { fail ("Got Node ID"); } else { pass (); } for (nodeIDIterator = gotNodeIDs.begin (), rawNodeIterator = gotNodes.begin (); nodeIDIterator != gotNodeIDs.end (); ++nodeIDIterator, ++rawNodeIterator) { ++nodes; #ifdef SMS_DEBUG bytes += rawNodeIterator->size (); #endif if (!destination.addKnownNode (*nodeIDIterator, *rawNodeIterator, NULL)) { WriteLog (lsTRACE, SHAMap) << "AddKnownNode fails"; fail ("AddKnownNode"); } else { pass (); } } gotNodeIDs.clear (); gotNodes.clear (); } while (true); destination.clearSynching (); #ifdef SMS_DEBUG WriteLog (lsINFO, SHAMap) << "SYNCHING COMPLETE " << items << " items, " << nodes << " nodes, " << bytes / 1024 << " KB"; #endif if (!source.deepCompare (destination)) { fail ("Deep Compare"); } else { pass (); } #ifdef SMS_DEBUG WriteLog (lsINFO, SHAMap) << "SHAMapSync test passed: " << items << " items, " << passes << " passes, " << nodes << " nodes"; #endif }