From 5959c4e3128233bb71cd41093ebb883bd77ad5b1 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Tue, 30 Jul 2013 17:29:23 -0700 Subject: [PATCH] Rewrite build version and protocol version interface --- Builds/VisualStudio2012/RippleD.vcxproj | 9 +- .../VisualStudio2012/RippleD.vcxproj.filters | 12 +- TODO.txt | 2 - .../ripple_app/basics/ripple_BuildInfo.cpp | 305 ++++++++++++++++++ modules/ripple_app/basics/ripple_BuildInfo.h | 145 +++++++++ .../ripple_app/basics/ripple_BuildVersion.h | 45 --- modules/ripple_app/basics/ripple_Version.h | 38 --- modules/ripple_app/misc/NetworkOPs.cpp | 4 +- modules/ripple_app/peers/ripple_Peer.cpp | 26 +- modules/ripple_app/ripple_app.cpp | 4 +- modules/ripple_app/rpc/rpc.cpp | 3 +- 11 files changed, 482 insertions(+), 111 deletions(-) create mode 100644 modules/ripple_app/basics/ripple_BuildInfo.cpp create mode 100644 modules/ripple_app/basics/ripple_BuildInfo.h delete mode 100644 modules/ripple_app/basics/ripple_BuildVersion.h delete mode 100644 modules/ripple_app/basics/ripple_Version.h diff --git a/Builds/VisualStudio2012/RippleD.vcxproj b/Builds/VisualStudio2012/RippleD.vcxproj index 1043e5e929..316da8440f 100644 --- a/Builds/VisualStudio2012/RippleD.vcxproj +++ b/Builds/VisualStudio2012/RippleD.vcxproj @@ -19,6 +19,12 @@ + + true + true + true + true + true true @@ -1318,9 +1324,8 @@ - + - diff --git a/Builds/VisualStudio2012/RippleD.vcxproj.filters b/Builds/VisualStudio2012/RippleD.vcxproj.filters index 60d2cee691..7ebed7d71f 100644 --- a/Builds/VisualStudio2012/RippleD.vcxproj.filters +++ b/Builds/VisualStudio2012/RippleD.vcxproj.filters @@ -870,6 +870,9 @@ [0] Subtrees\beast + + [1] Ripple\ripple_app\basics + @@ -1287,12 +1290,6 @@ [1] Ripple\ripple_client - - [1] Ripple\ripple_app\basics - - - [1] Ripple\ripple_app\basics - [1] Ripple\ripple_basics\utility @@ -1659,6 +1656,9 @@ [1] Ripple\ripple_core\validator + + [1] Ripple\ripple_app\basics + diff --git a/TODO.txt b/TODO.txt index 0a5bf27bc3..75efe6c797 100644 --- a/TODO.txt +++ b/TODO.txt @@ -122,8 +122,6 @@ David Features: use read/write locking semantics to update the values. Later, use Listeners to notify dependent code to resolve the dependency inversion. -- Merge ripple_Version.h and ripple_BuildVersion.h - - Rename LoadMonitor to LoadMeter, change LoadEvent to LoadMeter::ScopedSample - Rename LedgerMaster to Ledgers, create ILedgers interface. diff --git a/modules/ripple_app/basics/ripple_BuildInfo.cpp b/modules/ripple_app/basics/ripple_BuildInfo.cpp new file mode 100644 index 0000000000..c52bcad4d3 --- /dev/null +++ b/modules/ripple_app/basics/ripple_BuildInfo.cpp @@ -0,0 +1,305 @@ +//------------------------------------------------------------------------------ +/* + Copyright (c) 2011-2013, OpenCoin, Inc. +*/ +//============================================================================== + +String const& BuildInfo::getVersionString () +{ + static char const* const rawText = + + //-------------------------------------------------------------------------- + // + // The build version number (edit this for each release) + // + "0.010-rc1" + // + //-------------------------------------------------------------------------- + ; + + struct StaticInitializer + { + StaticInitializer () + { + // Sanity checking on the raw text + + Version v; + + if (! v.parse (rawText) || v.print () != rawText) + Throw (std::invalid_argument ("illegal server version format string")); + + versionString = rawText; + } + + String versionString; + }; + + static StaticInitializer value; + + return value.versionString; +} + +// The protocol version we speak and prefer. +// +BuildInfo::Protocol const& BuildInfo::getCurrentProtocol () +{ + static Protocol currentProtocol (1, 2); + + return currentProtocol; +} + +// The oldest protocol version we will accept. +// +BuildInfo::Protocol const& BuildInfo::getMinimumProtocol () +{ + static Protocol minimumProtocol (1, 2); + + return minimumProtocol; +} + + + +// Don't touch anything below this line +//------------------------------------------------------------------------------ + +char const* BuildInfo::getFullVersionString () +{ + struct StaticInitializer + { + StaticInitializer () + { + String s; + + s << "Ripple-" << getVersionString (); + + fullVersionString = s.toStdString (); + } + + std::string fullVersionString; + }; + + static StaticInitializer value; + + return value.fullVersionString.c_str (); +} + +//------------------------------------------------------------------------------ + +BuildInfo::Version::Version () + : vmajor (0) + , vminor (0) +{ +} + +bool BuildInfo::Version::parse (String const& s) +{ + // Many not have leading or trailing whitespace + if (s.trim () != s) + return false; + + int const indexOfDot = s.indexOfChar ('.'); + + // Must have a dot + if (indexOfDot == -1) + return false; + + String const majorString = s.substring (0, indexOfDot); + + // Must only contain digits + if (! majorString.containsOnly ("0123456789")) + return false; + + // Must match after conversion back and forth + if (String (majorString.getIntValue ()) != majorString) + return false; + + int const indexOfDash = s.indexOfChar ('-'); + + // A dash must come after the dot. + if (indexOfDash >= 0 && indexOfDash <= indexOfDot) + return false; + + String const minorString = (indexOfDash == -1) ? + s.substring (indexOfDot + 1) : s.substring (indexOfDot + 1, indexOfDash); + + // Must be length three + if (minorString.length () != 3) + return false; + + // Must only contain digits + if (! minorString.containsOnly ("0123456789")) + return false; + + String const suffixString = (indexOfDash == -1) ? + "" : s.substring (indexOfDash + 1); + + if (suffixString.length () > 0) + { + // Must be 4 characters or less + if (suffixString.length () > 4) + return false; + + // Must start with a letter + if (! String::charToString (suffixString [0]).containsOnly ("abcdefghijklmnopqrstuvwxyz")) + return false; + + // Must only contain letters and numbers + if (! String::charToString (suffixString [0]).containsOnly ("abcdefghijklmnopqrstuvwxyz01234567890")) + return false; + } + + vmajor = majorString.getIntValue (); + vminor = minorString.getIntValue (); + suffix = suffixString; + + return true; +} + +String BuildInfo::Version::print () const noexcept +{ + String s; + + s << String (vmajor) << "." << String (vminor).paddedLeft ('0', 3); + + if (suffix.isNotEmpty ()) + s << "-" << suffix; + + return s; +} + +//------------------------------------------------------------------------------ + +BuildInfo::Protocol::Protocol () + : vmajor (0) + , vminor (0) +{ +} + +BuildInfo::Protocol::Protocol (unsigned short major_, unsigned short minor_) + : vmajor (major_) + , vminor (minor_) +{ +} + +BuildInfo::Protocol::Protocol (PackedFormat packedVersion) +{ + vmajor = (packedVersion >> 16) & 0xffff; + vminor = (packedVersion & 0xffff); +} + +BuildInfo::Protocol::PackedFormat BuildInfo::Protocol::toPacked () const noexcept +{ + return ((vmajor << 16) & 0xffff0000) | (vminor & 0xffff); +} + +std::string BuildInfo::Protocol::toStdString () const noexcept +{ + String s; + + s << String (vmajor) << "." << "vminor"; + + return s.toStdString (); +} + +//------------------------------------------------------------------------------ + +class BuildInfoTests : public UnitTest +{ +public: + BuildInfoTests () : UnitTest ("BuildInfo", "ripple") + { + } + + void checkVersion (String const& s) + { + BuildInfo::Version v; + + expect (v.parse (s)); + + // Conversion back and forth should be identical + expect (v.print () == s); + } + + void testVersion () + { + beginTestCase ("version"); + + BuildInfo::Version v; + + checkVersion ("0.000"); + checkVersion ("1.002"); + checkVersion ("10.002"); + checkVersion ("99.999"); + checkVersion ("99.999-r"); + checkVersion ("99.999-r1"); + checkVersion ("99.999-r123"); + + unexpected (v.parse (" 1.2")); // Many not have leading or trailing whitespace + unexpected (v.parse ("1.2 ")); // Many not have leading or trailing whitespace + unexpected (v.parse (" 1.2 ")); // Many not have leading or trailing whitespace + unexpected (v.parse ("2")); // Must have a dot + unexpected (v.parse ("23")); // Must have a dot + unexpected (v.parse ("4-rc1")); // Must have a dot + unexpected (v.parse ("01.000")); // No leading zeroes + unexpected (v.parse ("4-4.r")); // A dash must come after the dot. + unexpected (v.parse ("1.2345")); // Must be length three + unexpected (v.parse ("1a.2")); // Must only contain digits + unexpected (v.parse ("1.2b")); // Must only contain digits + unexpected (v.parse ("1.2-rxxx1")); // Must be 4 characters or less + unexpected (v.parse ("1.2-")); // Must start with a letter + unexpected (v.parse ("1.2-3")); // Must start with a letter + unexpected (v.parse ("1.2-r!")); // Must only contain letters and numbers + } + + void checkProtcol (unsigned short vmajor, unsigned short vminor) + { + typedef BuildInfo::Protocol P; + + expect (P (P (vmajor, vminor).toPacked ()) == P (vmajor, vminor)); + } + + void testProtocol () + { + typedef BuildInfo::Protocol P; + + beginTestCase ("protocol"); + + expect (P (0, 0).toPacked () == 0); + expect (P (0, 1).toPacked () == 1); + expect (P (0, 65535).toPacked () == 65535); + + checkProtcol (0, 0); + checkProtcol (0, 1); + checkProtcol (0, 255); + checkProtcol (0, 65535); + checkProtcol (1, 0); + checkProtcol (1, 65535); + checkProtcol (65535, 65535); + } + + void testValues () + { + beginTestCase ("comparison"); + + typedef BuildInfo::Protocol P; + + expect (P(1,2) == P(1,2)); + expect (P(3,4) >= P(3,4)); + expect (P(5,6) <= P(5,6)); + expect (P(7,8) > P(6,7)); + expect (P(7,8) < P(8,9)); + expect (P(65535,0) < P(65535,65535)); + expect (P(65535,65535) >= P(65535,65535)); + + expect (BuildInfo::getCurrentProtocol () >= BuildInfo::getMinimumProtocol ()); + } + + void runTest () + { + testVersion (); + testProtocol (); + testValues (); + } +}; + +static BuildInfoTests buildInfoTests; diff --git a/modules/ripple_app/basics/ripple_BuildInfo.h b/modules/ripple_app/basics/ripple_BuildInfo.h new file mode 100644 index 0000000000..e27693660d --- /dev/null +++ b/modules/ripple_app/basics/ripple_BuildInfo.h @@ -0,0 +1,145 @@ +//------------------------------------------------------------------------------ +/* + Copyright (c) 2011-2013, OpenCoin, Inc. +*/ +//============================================================================== + +#ifndef RIPPLE_BUILDINFO_H_INCLUDED +#define RIPPLE_BUILDINFO_H_INCLUDED + +/** Versioning information for this build. */ +struct BuildInfo +{ + /** Server version. + + The server version has three parts: + + A non negative integer. + An integer between 0 and 999 inclusive. + An optional string. For example, "rc1" + + The version string is formatted thusly: + + '.' '.' ['-' ] + + The minor version number is always padded with leading zeroes + to bring the number of characters up to exactly three. For example, + the server version string "12.045-rc1" has major version 12, minor + version 45, and suffix "rc1". A suffix may only consist of lowercase + letters and digits, and must start with a letter. The suffix may + be up to 4 characters. The major version may not be prefixed with + extra leading zeroes. + + The suffix for a new official release is usually omitted. If hotfixes + are added to official releases they get a single leter suffix. + + Release candidates are marked with suffixes starting with "rc" and + followed by a number starting from 1 to indicate the first + release candidate, with subsequent release candidates incrementing + the number. A final release candidate which becomes an official + release loses the suffix. The next release candidate will have a + new major or minor version number, and start back at "rc1". + */ + static String const& getVersionString (); + + /** Full server version string. + + This includes the name of the server. It is used in the peer + protocol hello message and also the headers of some HTTP replies. + */ + static char const* getFullVersionString (); + + /** The server version's components. */ + struct Version + { + int vmajor; // 0+ + int vminor; // 0-999 + String suffix; // Can be empty + + //---- + + Version (); + + /** Convert a string to components. + @return `false` if the string is improperly formatted. + */ + bool parse (String const& s); + + /** Convert the components to a string. */ + String print () const noexcept; + }; + + //-------------------------------------------------------------------------- + + /** The wire protocol version. + + The version consists of two unsigned 16 bit integers representing + major and minor version numbers. All values are permissible. + */ + struct Protocol + { + unsigned short vmajor; + unsigned short vminor; + + //---- + + /** The serialized format of the protocol version. */ + typedef uint32 PackedFormat; + + Protocol (); + Protocol (unsigned short vmajor, unsigned short vminor); + explicit Protocol (PackedFormat packedVersion); + + PackedFormat toPacked () const noexcept; + + std::string toStdString () const noexcept; + + bool operator== (Protocol const& other) const noexcept { return toPacked () == other.toPacked (); } + bool operator!= (Protocol const& other) const noexcept { return toPacked () != other.toPacked (); } + bool operator>= (Protocol const& other) const noexcept { return toPacked () >= other.toPacked (); } + bool operator<= (Protocol const& other) const noexcept { return toPacked () <= other.toPacked (); } + bool operator> (Protocol const& other) const noexcept { return toPacked () > other.toPacked (); } + bool operator< (Protocol const& other) const noexcept { return toPacked () < other.toPacked (); } + }; + + /** The protocol version we speak and prefer. */ + static Protocol const& getCurrentProtocol (); + + /** The oldest protocol version we will accept. */ + static Protocol const& getMinimumProtocol (); + + //-------------------------------------------------------------------------- + // + // DEPRECATED STUFF + // + + /** Retrieve the build version number. + + This is typically incremented when an official version is publshed + with a list of changes. + + Format is: + + .. + */ + static char const* getBuildVersion () + { + return "0.0.1"; + } + + /** Retrieve the client API version number. + + The client API version is incremented whenever a new feature + or breaking change is made to the websocket / RPC interface. + + Format is: + + + */ + static char const* getClientVersion () + { + return "1"; + } +}; + +#endif diff --git a/modules/ripple_app/basics/ripple_BuildVersion.h b/modules/ripple_app/basics/ripple_BuildVersion.h deleted file mode 100644 index 6d5504c422..0000000000 --- a/modules/ripple_app/basics/ripple_BuildVersion.h +++ /dev/null @@ -1,45 +0,0 @@ -//------------------------------------------------------------------------------ -/* - Copyright (c) 2011-2013, OpenCoin, Inc. -*/ -//============================================================================== - -#ifndef RIPPLE_BUILDVERSION_RIPPLEHEADER -#define RIPPLE_BUILDVERSION_RIPPLEHEADER - -/** Versioning information for the build. -*/ - -class BuildVersion -{ -public: - /** Retrieve the build version number. - - This is typically incremented when an official version is publshed - with a list of changes. - - Format is: - - .. - */ - static char const* getBuildVersion () - { - return "0.0.1"; - } - - /** Retrieve the client API version number. - - The client API version is incremented whenever a new feature - or breaking change is made to the websocket / RPC interface. - - Format is: - - - */ - static char const* getClientVersion () - { - return "1"; - } -}; - -#endif diff --git a/modules/ripple_app/basics/ripple_Version.h b/modules/ripple_app/basics/ripple_Version.h deleted file mode 100644 index dae2dd0465..0000000000 --- a/modules/ripple_app/basics/ripple_Version.h +++ /dev/null @@ -1,38 +0,0 @@ -//------------------------------------------------------------------------------ -/* - Copyright (c) 2011-2013, OpenCoin, Inc. -*/ -//============================================================================== - -#ifndef RIPPLE_VERSION_H -#define RIPPLE_VERSION_H - -// -// Versions -// - -// VFALCO TODO Roll this together into ripple_BuildVersion -#define SERVER_VERSION_MAJOR 0 -#define SERVER_VERSION_MINOR 9 -#define SERVER_VERSION_SUB "-e" -#define SERVER_NAME "Ripple" - -#define SV_STRINGIZE(x) SV_STRINGIZE2(x) -#define SV_STRINGIZE2(x) #x -#define SERVER_VERSION \ - (SERVER_NAME "-" SV_STRINGIZE(SERVER_VERSION_MAJOR) "." SV_STRINGIZE(SERVER_VERSION_MINOR) SERVER_VERSION_SUB) - -// Version we prefer to speak: -#define PROTO_VERSION_MAJOR 1 -#define PROTO_VERSION_MINOR 2 - -// Version we will speak to: -#define MIN_PROTO_MAJOR 1 -#define MIN_PROTO_MINOR 2 - -#define MAKE_VERSION_INT(maj,min) ((maj << 16) | min) -#define GET_VERSION_MAJOR(ver) (ver >> 16) -#define GET_VERSION_MINOR(ver) (ver & 0xff) - -#endif -// vim:ts=4 diff --git a/modules/ripple_app/misc/NetworkOPs.cpp b/modules/ripple_app/misc/NetworkOPs.cpp index 08da9c9304..de64b67b3f 100644 --- a/modules/ripple_app/misc/NetworkOPs.cpp +++ b/modules/ripple_app/misc/NetworkOPs.cpp @@ -1391,8 +1391,8 @@ Json::Value NetworkOPs::getServerInfo (bool human, bool admin) { Json::Value info = Json::objectValue; - info ["build_version"] = BuildVersion::getBuildVersion (); - info ["client_version"] = BuildVersion::getClientVersion (); + info ["build_version"] = BuildInfo::getBuildVersion (); + info ["client_version"] = BuildInfo::getClientVersion (); if (getConfig ().TESTNET) info["testnet"] = getConfig ().TESTNET; diff --git a/modules/ripple_app/peers/ripple_Peer.cpp b/modules/ripple_app/peers/ripple_Peer.cpp index 46a4a233c4..e19dabb349 100644 --- a/modules/ripple_app/peers/ripple_Peer.cpp +++ b/modules/ripple_app/peers/ripple_Peer.cpp @@ -972,11 +972,11 @@ void PeerImp::recvHello (protocol::TMHello& packet) WriteLog (lsINFO, Peer) << "Recv(Hello): " << getIP () << " :Clock far off -" << ourTime - packet.nettime (); } } - else if (packet.protoversionmin () > MAKE_VERSION_INT (PROTO_VERSION_MAJOR, PROTO_VERSION_MINOR)) + else if (packet.protoversionmin () > BuildInfo::getCurrentProtocol().toPacked ()) { - WriteLog (lsINFO, Peer) << "Recv(Hello): Server requires protocol version " << - GET_VERSION_MAJOR (packet.protoversion ()) << "." << GET_VERSION_MINOR (packet.protoversion ()) - << " we run " << PROTO_VERSION_MAJOR << "." << PROTO_VERSION_MINOR; + WriteLog (lsINFO, Peer) << + "Recv(Hello): Server requires protocol version " << BuildInfo::Protocol (packet.protoversion()).toStdString () << + ", we run " << BuildInfo::getCurrentProtocol().toStdString (); } else if (!mNodePublic.setNodePublic (packet.nodepublic ())) { @@ -991,9 +991,8 @@ void PeerImp::recvHello (protocol::TMHello& packet) { // Successful connection. WriteLog (lsINFO, Peer) << "Recv(Hello): Connect: " << mNodePublic.humanNodePublic (); - CondLog (packet.protoversion () != MAKE_VERSION_INT (PROTO_VERSION_MAJOR, PROTO_VERSION_MINOR), lsINFO, Peer) - << "Peer speaks version " << - (packet.protoversion () >> 16) << "." << (packet.protoversion () & 0xFF); + CondLog (BuildInfo::Protocol (packet.protoversion()) != BuildInfo::getCurrentProtocol(), lsINFO, Peer) << + "Peer speaks version " << BuildInfo::Protocol (packet.protoversion()).toStdString (); mHello = packet; if (getApp().getUNL ().nodeInCluster (mNodePublic, mNodeName)) @@ -2239,9 +2238,9 @@ void PeerImp::sendHello () protocol::TMHello h; - h.set_protoversion (MAKE_VERSION_INT (PROTO_VERSION_MAJOR, PROTO_VERSION_MINOR)); - h.set_protoversionmin (MAKE_VERSION_INT (MIN_PROTO_MAJOR, MIN_PROTO_MINOR)); - h.set_fullversion (SERVER_VERSION); + h.set_protoversion (BuildInfo::getCurrentProtocol().toPacked ()); + h.set_protoversionmin (BuildInfo::getMinimumProtocol().toPacked ()); + h.set_fullversion (BuildInfo::getFullVersionString ()); h.set_nettime (getApp().getOPs ().getNetworkTimeNC ()); h.set_nodepublic (getApp().getLocalCredentials ().getNodePublic ().humanNodePublic ()); h.set_nodeproof (&vchSig[0], vchSig.size ()); @@ -2410,9 +2409,10 @@ Json::Value PeerImp::getJson () ret["version"] = mHello.fullversion (); if (mHello.has_protoversion () && - (mHello.protoversion () != MAKE_VERSION_INT (PROTO_VERSION_MAJOR, PROTO_VERSION_MINOR))) - ret["protocol"] = lexicalCastThrow (GET_VERSION_MAJOR (mHello.protoversion ())) + "." + - lexicalCastThrow (GET_VERSION_MINOR (mHello.protoversion ())); + (mHello.protoversion () != BuildInfo::getCurrentProtocol().toPacked ())) + { + ret["protocol"] = BuildInfo::Protocol (mHello.protoversion ()).toStdString (); + } if (!!mClosedLedgerHash) ret["ledger"] = mClosedLedgerHash.GetHex (); diff --git a/modules/ripple_app/ripple_app.cpp b/modules/ripple_app/ripple_app.cpp index 4b3631a3f8..c6afd1ee78 100644 --- a/modules/ripple_app/ripple_app.cpp +++ b/modules/ripple_app/ripple_app.cpp @@ -195,8 +195,7 @@ namespace ripple #include "contracts/ripple_Interpreter.h" #include "contracts/ripple_Operation.h" -#include "basics/ripple_Version.h" // VFALCO TODO Should this be private? -#include "basics/ripple_BuildVersion.h" // private +#include "basics/ripple_BuildInfo.h" // private #include "basics/ripple_RPCServerHandler.h" #include "rpc/RPCDoor.h" // needs RPCServer @@ -244,6 +243,7 @@ static const uint64 tenTo17m1 = tenTo17 - 1; #if ! defined (RIPPLE_MAIN_PART) || RIPPLE_MAIN_PART == 1 +#include "basics/ripple_BuildInfo.cpp" #include "basics/ripple_RPCServerHandler.cpp" #include "node/ripple_NodeObject.cpp" #include "node/ripple_NodeStore.cpp" diff --git a/modules/ripple_app/rpc/rpc.cpp b/modules/ripple_app/rpc/rpc.cpp index 568b537826..042e4e67d0 100644 --- a/modules/ripple_app/rpc/rpc.cpp +++ b/modules/ripple_app/rpc/rpc.cpp @@ -123,7 +123,8 @@ std::string HTTPReply (int nStatus, const std::string& strMsg) rfc1123Time ().c_str (), access.c_str (), strMsg.size () + 2, - SERVER_VERSION, + //SERVER_VERSION, + BuildInfo::getFullVersionString (), strMsg.c_str ()); }