Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rippled Server Software Upgrade Notification #3447

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Builds/CMake/RippledCore.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ install (
src/ripple/basics/IOUAmount.h
src/ripple/basics/LocalValue.h
src/ripple/basics/Log.h
src/ripple/basics/MathUtilities.h
src/ripple/basics/safe_cast.h
src/ripple/basics/Slice.h
src/ripple/basics/StringUtilities.h
Expand Down Expand Up @@ -698,6 +699,7 @@ target_sources (rippled PRIVATE
src/test/basics/FileUtilities_test.cpp
src/test/basics/IOUAmount_test.cpp
src/test/basics/KeyCache_test.cpp
src/test/basics/MathUtilities_test.cpp
src/test/basics/PerfLog_test.cpp
src/test/basics/RangeSet_test.cpp
src/test/basics/Slice_test.cpp
Expand Down Expand Up @@ -867,6 +869,7 @@ target_sources (rippled PRIVATE
test sources:
subdir: protocol
#]===============================]
src/test/protocol/BuildInfo_test.cpp
src/test/protocol/InnerObjectFormats_test.cpp
src/test/protocol/Issue_test.cpp
src/test/protocol/PublicKey_test.cpp
Expand Down
3 changes: 3 additions & 0 deletions src/ripple/app/ledger/LedgerMaster.h
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,9 @@ class LedgerMaster : public Stoppable, public AbstractFetchPackContainer
// without first wiping the database.
LedgerIndex const max_ledger_difference_{1000000};

// Time that the previous upgrade warning was issued.
TimeKeeper::time_point upgradeWarningPrevTime_{};

private:
struct Stats
{
Expand Down
69 changes: 69 additions & 0 deletions src/ripple/app/ledger/impl/LedgerMaster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <ripple/app/paths/PathRequests.h>
#include <ripple/app/tx/apply.h>
#include <ripple/basics/Log.h>
#include <ripple/basics/MathUtilities.h>
#include <ripple/basics/TaggedCache.h>
#include <ripple/basics/UptimeClock.h>
#include <ripple/basics/contract.h>
Expand All @@ -43,6 +44,7 @@
#include <ripple/nodestore/DatabaseShard.h>
#include <ripple/overlay/Overlay.h>
#include <ripple/overlay/Peer.h>
#include <ripple/protocol/BuildInfo.h>
#include <ripple/protocol/HashPrefix.h>
#include <ripple/protocol/digest.h>
#include <ripple/resource/Fees.h>
Expand Down Expand Up @@ -1037,6 +1039,73 @@ LedgerMaster::checkAccept(std::shared_ptr<Ledger const> const& ledger)
app_.getFeeTrack().setRemoteFee(fee);

tryAdvance();

if (ledger->seq() % 256 == 0)
{
// Check if the majority of validators run a higher version rippled
// software. If so print a warning.
//
// Once the HardenedValidations amendment is enabled, validators include
// their rippled software version in the validation messages of every
// (flag - 1) ledger. We wait for one ledger time before checking the
// version information to accumulate more validation messages.

auto currentTime = app_.timeKeeper().now();
bool needPrint = false;

// The variable upgradeWarningPrevTime_ will be set when and only when
// the warning is printed.
if (upgradeWarningPrevTime_ == TimeKeeper::time_point())
{
// Have not printed the warning before, check if need to print.
auto const vals = app_.getValidations().getTrustedForLedger(
ledger->info().parentHash);
std::size_t higherVersionCount = 0;
std::size_t rippledCount = 0;
for (auto const& v : vals)
{
if (v->isFieldPresent(sfServerVersion))
{
auto version = v->getFieldU64(sfServerVersion);
higherVersionCount +=
BuildInfo::isNewerVersion(version) ? 1 : 0;
rippledCount +=
BuildInfo::isRippledVersion(version) ? 1 : 0;
}
}
// We report only if (1) we have accumulated validation messages
// from 90% validators from the UNL, and (2) 60% of validators
// running the rippled implementation have higher version numbers.
constexpr std::size_t reportingPercent = 90;
constexpr std::size_t cutoffPercent = 60;
needPrint = calculatePercent(
vals.size(),
app_.validators().getQuorumKeys().second.size()) >=
reportingPercent &&
calculatePercent(higherVersionCount, rippledCount) >=
cutoffPercent;
}
// To throttle the warning messages, instead of printing a warning
// every flag ledger, we print every week.
else if (currentTime - upgradeWarningPrevTime_ >= weeks{1})
{
// Printed the warning before, and assuming most validators
// do not downgrade, we keep printing the warning
// until the local server is restarted.
needPrint = true;
}

if (needPrint)
{
upgradeWarningPrevTime_ = currentTime;
auto const upgradeMsg =
"Check for upgrade: "
"A majority of trusted validators are "
"running a newer version.";
std::cerr << upgradeMsg << std::endl;
JLOG(m_journal.error()) << upgradeMsg;
}
}
}

/** Report that the consensus process built a particular ledger */
Expand Down
51 changes: 51 additions & 0 deletions src/ripple/basics/MathUtilities.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2020 Ripple Labs Inc.

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================

#ifndef RIPPLE_BASICS_MATHUTILITIES_H_INCLUDED
#define RIPPLE_BASICS_MATHUTILITIES_H_INCLUDED

#include <algorithm>
#include <assert.h>
#include <cstddef>

namespace ripple {

/** Calculate one number divided by another number in percentage.
* The result is rounded up to the next integer, and capped in the range [0,100]
* E.g. calculatePercent(1, 100) = 1 because 1/100 = 0.010000
* calculatePercent(1, 99) = 2 because 1/99 = 0.010101
* calculatePercent(0, 100) = 0
* calculatePercent(100, 100) = 100
* calculatePercent(200, 100) = 100 because the result is capped to 100
*
* @param count dividend
* @param total divisor
* @return the percentage, in [0, 100]
*
* @note total cannot be zero.
* */
constexpr std::size_t
calculatePercent(std::size_t count, std::size_t total)
{
assert(total != 0);
return ((std::min(count, total) * 100) + total - 1) / total;
}
} // namespace ripple

#endif
30 changes: 29 additions & 1 deletion src/ripple/protocol/BuildInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ getVersionString();
std::string const&
getFullVersionString();

/** Returns the server version packed in a 64-bit integer.
/** Encode an arbitrary server software version in a 64-bit integer.

The general format is:

Expand All @@ -64,10 +64,38 @@ getFullVersionString();
10 if an RC
01 if a beta
N: 6-bit rc/beta number (1-63)

@param the version string
@return the encoded version in a 64-bit integer
*/
std::uint64_t
encodeSoftwareVersion(char const* const versionStr);

/** Returns this server's version packed in a 64-bit integer. */
std::uint64_t
getEncodedVersion();

/** Check if the encoded software version is a rippled software version.

@param version another node's encoded software version
@return true if the version is a rippled software version, false otherwise
*/
bool
isRippledVersion(std::uint64_t version);

/** Check if the version is newer than the local node's rippled software
version.

@param version another node's encoded software version
@return true if the version is newer than the local node's rippled software
version, false otherwise.

@note This function only understands version numbers that are generated by
rippled. Please see the encodeSoftwareVersion() function for detail.
*/
bool
isNewerVersion(std::uint64_t version);

} // namespace BuildInfo

} // namespace ripple
Expand Down
125 changes: 73 additions & 52 deletions src/ripple/protocol/impl/BuildInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,77 +77,98 @@ getFullVersionString()
return value;
}

static constexpr std::uint64_t implementationVersionIdentifier =
0x183B'0000'0000'0000LLU;
static constexpr std::uint64_t implementationVersionIdentifierMask =
0xFFFF'0000'0000'0000LLU;

std::uint64_t
getEncodedVersion()
encodeSoftwareVersion(char const* const versionStr)
{
static std::uint64_t const cookie = []() {
std::uint64_t c = 0x183B000000000000;
std::uint64_t c = implementationVersionIdentifier;

beast::SemanticVersion v;
beast::SemanticVersion v;

if (v.parse(versionString))
{
if (v.majorVersion >= 0 && v.majorVersion <= 255)
c |= static_cast<std::uint64_t>(v.majorVersion) << 40;
if (v.parse(std::string(versionStr)))
{
if (v.majorVersion >= 0 && v.majorVersion <= 255)
c |= static_cast<std::uint64_t>(v.majorVersion) << 40;

if (v.minorVersion >= 0 && v.minorVersion <= 255)
c |= static_cast<std::uint64_t>(v.minorVersion) << 32;

if (v.minorVersion >= 0 && v.minorVersion <= 255)
c |= static_cast<std::uint64_t>(v.minorVersion) << 32;
if (v.patchVersion >= 0 && v.patchVersion <= 255)
c |= static_cast<std::uint64_t>(v.patchVersion) << 24;

if (v.patchVersion >= 0 && v.patchVersion <= 255)
c |= static_cast<std::uint64_t>(v.patchVersion) << 24;
if (!v.isPreRelease())
c |= static_cast<std::uint64_t>(0xC00000);

if (!v.isPreRelease())
c |= static_cast<std::uint64_t>(0xC00000);
if (v.isPreRelease())
{
std::uint8_t x = 0;

if (v.isPreRelease())
for (auto id : v.preReleaseIdentifiers)
{
std::uint8_t x = 0;
auto parsePreRelease = [](std::string_view identifier,
std::string_view prefix,
std::uint8_t key,
std::uint8_t lok,
std::uint8_t hik) -> std::uint8_t {
std::uint8_t ret = 0;

if (prefix != identifier.substr(0, prefix.length()))
return 0;

if (!beast::lexicalCastChecked(
ret,
std::string(identifier.substr(prefix.length()))))
return 0;
pwang200 marked this conversation as resolved.
Show resolved Hide resolved

if (std::clamp(ret, lok, hik) != ret)
return 0;
pwang200 marked this conversation as resolved.
Show resolved Hide resolved

return ret + key;
};

x = parsePreRelease(id, "rc", 0x80, 0, 63);

if (x == 0)
x = parsePreRelease(id, "b", 0x40, 0, 63);

for (auto id : v.preReleaseIdentifiers)
if (x & 0xC0)
{
auto parsePreRelease =
[](std::string_view identifier,
std::string_view prefix,
std::uint8_t key,
std::uint8_t lok,
std::uint8_t hik) -> std::uint8_t {
std::uint8_t ret = 0;

if (prefix != identifier.substr(0, prefix.length()))
return 0;

if (!beast::lexicalCastChecked(
ret,
std::string(
identifier.substr(prefix.length()))))
return 0;

if (std::clamp(ret, lok, hik) != ret)
return 0;

return ret + key;
};

x = parsePreRelease(id, "rc", 0x80, 0, 63);

if (x == 0)
x = parsePreRelease(id, "b", 0x40, 0, 63);

if (x & 0xC0)
{
c |= static_cast<std::uint64_t>(x) << 16;
break;
}
c |= static_cast<std::uint64_t>(x) << 16;
break;
}
}
}
}

return c;
}();
return c;
}

std::uint64_t
getEncodedVersion()
{
static std::uint64_t const cookie = {encodeSoftwareVersion(versionString)};
return cookie;
}

bool
isRippledVersion(std::uint64_t version)
{
return (version & implementationVersionIdentifierMask) ==
implementationVersionIdentifier;
}

bool
isNewerVersion(std::uint64_t version)
{
if (isRippledVersion(version))
return version > getEncodedVersion();
return false;
pwang200 marked this conversation as resolved.
Show resolved Hide resolved
}
pwang200 marked this conversation as resolved.
Show resolved Hide resolved

} // namespace BuildInfo

} // namespace ripple
Loading