diff --git a/src/xrpld/app/misc/ValidatorList.h b/src/xrpld/app/misc/ValidatorList.h index 45b24e86090..c49076c790e 100644 --- a/src/xrpld/app/misc/ValidatorList.h +++ b/src/xrpld/app/misc/ValidatorList.h @@ -344,7 +344,7 @@ class ValidatorList static std::vector parseBlobs(protocol::TMValidatorListCollection const& body); - static void + static std::optional sendValidatorList( Peer& peer, std::uint64_t peerSequence, @@ -356,6 +356,18 @@ class ValidatorList HashRouter& hashRouter, beast::Journal j); + std::tuple< + std::string, + std::uint32_t, + std::map, + uint256> + sendLatestValidatorLists( + Peer& peer, + std::uint64_t peerSequence, + PublicKey const& publisherKey, + HashRouter& hashRouter, + beast::Journal j) const; + [[nodiscard]] static std::pair buildValidatorListMessages( std::size_t messageVersion, @@ -796,7 +808,7 @@ class ValidatorList HashRouter& hashRouter, beast::Journal j); - static void + static std::optional sendValidatorList( Peer& peer, std::uint64_t peerSequence, diff --git a/src/xrpld/app/misc/detail/ValidatorList.cpp b/src/xrpld/app/misc/detail/ValidatorList.cpp index 5c1c5e80a1c..3277ef022bb 100644 --- a/src/xrpld/app/misc/detail/ValidatorList.cpp +++ b/src/xrpld/app/misc/detail/ValidatorList.cpp @@ -717,8 +717,62 @@ ValidatorList::buildValidatorListMessages( return {0, 0}; } +std::tuple< + std::string, + std::uint32_t, + std::map, + uint256> +ValidatorList::sendLatestValidatorLists( + Peer& peer, + std::uint64_t peerSequence, + PublicKey const& publisherKey, + HashRouter& hashRouter, + beast::Journal j) const +{ + std::vector messages; + std::map blobInfos; + + if (publisherLists_.count(publisherKey) == 0) + return {}; + ValidatorList::PublisherListCollection const& lists = + publisherLists_.at(publisherKey); + + auto const maxSequence = lists.current.sequence; + ASSERT( + lists.current.sequence == maxSequence || + lists.remaining.count(maxSequence) == 1, + "ripple::ValidatorList::sendLatestValidatorLists : valid sequence"); + + if (peerSequence < maxSequence) + { + buildBlobInfos(blobInfos, lists); + sendValidatorList( + peer, + peerSequence, + publisherKey, + maxSequence, + lists.rawVersion, + lists.rawManifest, + blobInfos, + messages, + hashRouter, + j); + + // Suppress the messages so they'll be ignored next time. + uint256 lasthash; + for (auto const& m : messages) + { + lasthash = m.hash; + hashRouter.addSuppressionPeer(lasthash, peer.id()); + } + return std::make_tuple( + lists.rawManifest, lists.rawVersion, blobInfos, lasthash); + } + return {}; +} + // static -void +std::optional ValidatorList::sendValidatorList( Peer& peer, std::uint64_t peerSequence, @@ -736,7 +790,7 @@ ValidatorList::sendValidatorList( : peer.supportsFeature(ProtocolFeature::ValidatorListPropagation) ? 1 : 0; if (!messageVersion) - return; + return {}; auto const [newPeerSequence, numVLs] = buildValidatorListMessages( messageVersion, peerSequence, @@ -772,12 +826,15 @@ ValidatorList::sendValidatorList( if (sent) { if (messageVersion > 1) + { JLOG(j.debug()) << "Sent " << messages.size() << " validator list collection(s) containing " << numVLs << " validator list(s) for " << strHex(publisherKey) << " with sequence range " << peerSequence << ", " << newPeerSequence << " to " << peer.fingerprint(); + return "ValidatorListCollection"; + } else { XRPL_ASSERT( @@ -788,13 +845,15 @@ ValidatorList::sendValidatorList( << "Sent validator list for " << strHex(publisherKey) << " with sequence " << newPeerSequence << " to " << peer.fingerprint(); + return "ValidatorList"; } } } + return {}; } // static -void +std::optional ValidatorList::sendValidatorList( Peer& peer, std::uint64_t peerSequence, @@ -807,7 +866,7 @@ ValidatorList::sendValidatorList( beast::Journal j) { std::vector messages; - sendValidatorList( + return sendValidatorList( peer, peerSequence, publisherKey, @@ -878,7 +937,7 @@ ValidatorList::broadcastBlobs( std::map blobInfos; XRPL_ASSERT( - lists.current.sequence == maxSequence || + lists.current.sequence <= maxSequence || lists.remaining.count(maxSequence) == 1, "xrpl::ValidatorList::broadcastBlobs : valid sequence"); // Can't use overlay.foreach here because we need to modify diff --git a/src/xrpld/overlay/detail/PeerImp.cpp b/src/xrpld/overlay/detail/PeerImp.cpp index 53237ed3ae8..277988a0f09 100644 --- a/src/xrpld/overlay/detail/PeerImp.cpp +++ b/src/xrpld/overlay/detail/PeerImp.cpp @@ -886,6 +886,46 @@ PeerImp::domain() const // Protocol logic +void +logVLBlob(beast::Journal j, ValidatorBlobInfo const& blob, std::size_t count) +{ + auto const stream = j.trace(); + JLOG(stream) << "Blob " << count << " Signature: " << blob.signature; + JLOG(stream) << "Blob " << count << " blob: " << base64_decode(blob.blob); + JLOG(stream) << "Blob " << count << " manifest: " + << (blob.manifest ? base64_decode(*blob.manifest) : "NONE"); +} + +void +logVLBlob( + beast::Journal j, + std::pair const& blob, + std::size_t count) +{ + logVLBlob(j, blob.second, count); +} + +template +void +logVL( + beast::Journal j, + std::string const& manifest, + std::uint32_t version, + TBlobs const& blobs, + uint256 const& hash) +{ + auto const stream = j.trace(); + JLOG(stream) << "Manifest: " << manifest; + JLOG(stream) << "Version: " << version; + JLOG(stream) << "Hash: " << hash; + std::size_t count = 1; + for (auto const& blob : blobs) + { + logVLBlob(j, blob, count); + ++count; + } +} + void PeerImp::doProtocolStart() { @@ -2157,6 +2197,8 @@ PeerImp::onValidatorListMessage( return; } + logVL(p_journal_, manifest, version, blobs, hash); + auto const applyResult = app_.validators().applyListsAndBroadcast( manifest, version, @@ -2198,7 +2240,8 @@ PeerImp::onValidatorListMessage( "xrpl::PeerImp::onValidatorListMessage : lower sequence"); } #endif - publisherListSequences_[pubKey] = applyResult.sequence; + if (publisherListSequences_[pubKey] < applyResult.sequence) + publisherListSequences_[pubKey] = applyResult.sequence; } break; case ListDisposition::same_sequence: @@ -2217,8 +2260,37 @@ PeerImp::onValidatorListMessage( } #endif // !NDEBUG + [[fallthrough]]; + case ListDisposition::stale: { + auto const [pubKey, currentPeerSeq] = [&]() { + std::lock_guard sl(recentLock_); + ASSERT( + applyResult.sequence && applyResult.publisherKey, + "ripple::PeerImp::onValidatorListMessage : (stale) nonzero " + "sequence"); + auto const& pubKey = *applyResult.publisherKey; + auto const& current = publisherListSequences_[pubKey]; + ASSERT( + current <= applyResult.sequence, + "ripple::PeerImp::onValidatorListMessage : (stale) valid " + "sequence"); + return std::make_pair( + pubKey, current ? current : applyResult.sequence); + }(); + if (currentPeerSeq <= applyResult.sequence) + { + auto const [sentmanifest, sentversion, sentblobs, senthash] = + app_.validators().sendLatestValidatorLists( + *this, + currentPeerSeq, + pubKey, + app_.getHashRouter(), + p_journal_); + logVL( + p_journal_, sentmanifest, sentversion, sentblobs, senthash); + } + } break; - case ListDisposition::stale: case ListDisposition::untrusted: case ListDisposition::invalid: case ListDisposition::unsupported_version: