diff --git a/CMakeLists.txt b/CMakeLists.txt index 1852d996..3d440341 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ add_executable(appbill #-------hpcore------- add_executable(hpcore + src/util/version.cpp src/util/util.cpp src/util/rollover_hashset.cpp src/util/ttl_set.cpp diff --git a/src/conf.cpp b/src/conf.cpp index c3fbe800..bc8f05cc 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -2,6 +2,7 @@ #include "conf.hpp" #include "crypto.hpp" #include "sc/sc.hpp" +#include "util/version.hpp" #include "util/util.hpp" #include "ledger/ledger_mount.hpp" #include "sc/contract_mount.hpp" @@ -150,8 +151,6 @@ namespace conf cfg.node.public_key_hex = util::to_hex(cfg.node.public_key); cfg.node.private_key_hex = util::to_hex(cfg.node.private_key); - cfg.hp_version = util::HP_VERSION; - cfg.node.role = ROLE::VALIDATOR; cfg.node.history = HISTORY::CUSTOM; cfg.node.history_config.max_primary_shards = 1; @@ -279,27 +278,28 @@ namespace conf try { // Check whether the hp version is specified. - cfg.hp_version = d["hp_version"].as(); - if (cfg.hp_version.empty()) + const std::string config_version = d["hp_version"].as(); + if (config_version.empty()) { std::cerr << "Config HP version missing.\n"; return -1; } // Check whether this config complies with the min version requirement. - int verresult = util::version_compare(cfg.hp_version, std::string(util::MIN_CONFIG_VERSION)); - if (verresult == -1) + const int ver_result = version::version_compare(config_version, std::string(version::MIN_CONFIG_VERSION)); + if (ver_result == -1) { - std::cerr << "Config version too old. Minimum " - << util::MIN_CONFIG_VERSION << " required. " - << cfg.hp_version << " found.\n"; + std::cerr << "Config version too old. Minimum " << version::MIN_CONFIG_VERSION << " required. " + << config_version << " found.\n"; return -1; } - else if (verresult == -2) + else if (ver_result == -2) { std::cerr << "Malformed version string.\n"; return -1; } + + // TODO: If our version and config version is different, we need to upgrade the contract dir. } catch (const std::exception &e) { @@ -523,7 +523,7 @@ namespace conf // Popualte json document with 'cfg' values. // ojson is used instead of json to preserve insertion order. jsoncons::ojson d; - d.insert_or_assign("hp_version", cfg.hp_version); + d.insert_or_assign("hp_version", version::HP_VERSION); // Node config. { diff --git a/src/conf.hpp b/src/conf.hpp index 23ad671f..3b99c93b 100644 --- a/src/conf.hpp +++ b/src/conf.hpp @@ -196,7 +196,6 @@ namespace conf // Holds all the config values. struct hp_config { - std::string hp_version; // Version of Hot Pocket that generated the config. (MAJOR.MINOR.PATCH) node_config node; contract_config contract; mesh_config mesh; diff --git a/src/ledger/ledger.cpp b/src/ledger/ledger.cpp index 9fef9fef..f592f9a4 100644 --- a/src/ledger/ledger.cpp +++ b/src/ledger/ledger.cpp @@ -2,6 +2,7 @@ #include "ledger.hpp" #include "../crypto.hpp" #include "../conf.hpp" +#include "../util/version.hpp" #include "../util/util.hpp" #include "../msg/fbuf/ledger_helpers.hpp" #include "../msg/fbuf/common_helpers.hpp" @@ -20,13 +21,13 @@ namespace ledger { ledger_context ctx; ledger_record genesis; - constexpr uint32_t LEDGER_FS_ID = 1; ledger::ledger_mount ledger_fs; // Global ledger file system instance. ledger::ledger_sync ledger_sync_worker; // Global ledger file system sync instance. ledger::ledger_serve ledger_server; // Ledger file server instance. std::shared_mutex primary_index_file_mutex; + constexpr uint32_t LEDGER_FS_ID = 1; constexpr int FILE_PERMS = 0644; /** @@ -225,7 +226,7 @@ namespace ledger } // Create and update the hp_version table with current hp version. - if (sqlite::create_hp_version_table_and_update(*db, conf::cfg.hp_version) == -1) + if (sqlite::create_hp_version_table_and_update(*db, version::LEDGER_VERSION) == -1) { LOG_ERROR << errno << ": Error creating and updating hp version table, shard: " << std::to_string(shard_seq_no); return -1; @@ -249,14 +250,6 @@ namespace ledger } } - // Creating the hp_version header. - uint8_t hp_version_header[util::HP_VERSION_HEADER_SIZE]; - if (util::create_hp_version_header(hp_version_header, conf::cfg.hp_version) == -1) - { - LOG_ERROR << "Error creating version header for prev_shard.hash in primary shard " << std::to_string(shard_seq_no); - return -1; - } - // Write the prev_shard.hash to the new folder. const std::string shard_hash_file_path = shard_path + PREV_SHARD_HASH_FILENAME; const int fd = open(shard_hash_file_path.data(), O_CREAT | O_RDWR, FILE_PERMS); @@ -267,8 +260,8 @@ namespace ledger } struct iovec iov_vec[2]; - iov_vec[0].iov_base = hp_version_header; - iov_vec[0].iov_len = util::HP_VERSION_HEADER_SIZE; + iov_vec[0].iov_base = version::LEDGER_VERSION_BYTES; + iov_vec[0].iov_len = version::VERSION_BYTES_LEN; iov_vec[1].iov_base = &prev_shard_hash; iov_vec[1].iov_len = sizeof(util::h32); @@ -377,8 +370,8 @@ namespace ledger } util::h32 prev_shard_hash_from_file; - // Start reading hash excluding hp_version header. - const int res = pread(fd, &prev_shard_hash_from_file, sizeof(util::h32), util::HP_VERSION_HEADER_SIZE); + // Start reading hash excluding version bytes. + const int res = pread(fd, &prev_shard_hash_from_file, sizeof(util::h32), version::VERSION_BYTES_LEN); close(fd); if (res == -1) { @@ -424,14 +417,6 @@ namespace ledger should_create_folder = true; } - // Creating the hp_version header. - uint8_t hp_version_header[util::HP_VERSION_HEADER_SIZE]; - if (util::create_hp_version_header(hp_version_header, conf::cfg.hp_version) == -1) - { - LOG_ERROR << "Error creating version header for prev_shard.hash in blob shard " << std::to_string(last_blob_shard_seq_no); - return -1; - } - // Create the required shard folder if not already existing. if (should_create_folder) { @@ -464,8 +449,8 @@ namespace ledger } struct iovec iov_vec[2]; - iov_vec[0].iov_base = hp_version_header; - iov_vec[0].iov_len = util::HP_VERSION_HEADER_SIZE; + iov_vec[0].iov_base = version::LEDGER_VERSION_BYTES; + iov_vec[0].iov_len = version::VERSION_BYTES_LEN; iov_vec[1].iov_base = &prev_shard_hash; iov_vec[1].iov_len = sizeof(util::h32); @@ -523,8 +508,8 @@ namespace ledger } struct iovec iov_vec[2]; - iov_vec[0].iov_base = hp_version_header; - iov_vec[0].iov_len = util::HP_VERSION_HEADER_SIZE; + iov_vec[0].iov_base = version::LEDGER_VERSION_BYTES; + iov_vec[0].iov_len = version::VERSION_BYTES_LEN; iov_vec[1].iov_base = builder.GetBufferPointer(); iov_vec[1].iov_len = builder.GetSize(); @@ -629,7 +614,7 @@ namespace ledger } } uint8_t last_shard_seq_no_buf[8]; - if (pread(fd, last_shard_seq_no_buf, sizeof(last_shard_seq_no_buf), util::HP_VERSION_HEADER_SIZE) == -1) + if (pread(fd, last_shard_seq_no_buf, sizeof(last_shard_seq_no_buf), version::VERSION_BYTES_LEN) == -1) { LOG_ERROR << errno << ": Error reading " << last_shard_seq_no_path; close(fd); @@ -660,14 +645,6 @@ namespace ledger const std::string last_shard_seq_no_vpath = shard_parent_dir + SHARD_SEQ_NO_FILENAME; const std::string last_shard_seq_no_path = ledger_fs.physical_path(hpfs::RW_SESSION_NAME, last_shard_seq_no_vpath); - // Creating the hp_version header. - uint8_t hp_version_header[util::HP_VERSION_HEADER_SIZE]; - if (util::create_hp_version_header(hp_version_header, conf::cfg.hp_version) == -1) - { - LOG_ERROR << "Error creating version header for max_shard.seq_no in " << shard_parent_dir; - return -1; - } - // Open max_shard.seq_no in given parent directory. const int fd = open(last_shard_seq_no_path.data(), O_CREAT | O_RDWR, FILE_PERMS); if (fd == -1) @@ -679,8 +656,8 @@ namespace ledger util::uint64_to_bytes(seq_no_byte_str, last_shard_seq_no); struct iovec iov_vec[2]; - iov_vec[0].iov_base = hp_version_header; - iov_vec[0].iov_len = util::HP_VERSION_HEADER_SIZE; + iov_vec[0].iov_base = version::LEDGER_VERSION_BYTES; + iov_vec[0].iov_len = version::VERSION_BYTES_LEN; iov_vec[1].iov_base = seq_no_byte_str; iov_vec[1].iov_len = sizeof(seq_no_byte_str); diff --git a/src/ledger/ledger.hpp b/src/ledger/ledger.hpp index 33e953ec..74dc4e64 100644 --- a/src/ledger/ledger.hpp +++ b/src/ledger/ledger.hpp @@ -9,7 +9,6 @@ namespace ledger { - struct ledger_context { private: diff --git a/src/ledger/ledger_query.cpp b/src/ledger/ledger_query.cpp index 64414cfc..a8e881d6 100644 --- a/src/ledger/ledger_query.cpp +++ b/src/ledger/ledger_query.cpp @@ -3,6 +3,7 @@ #include "ledger.hpp" #include "sqlite.hpp" #include "../msg/fbuf/ledger_helpers.hpp" +#include "../util/version.hpp" namespace ledger::query { @@ -86,7 +87,7 @@ namespace ledger::query if (fd == -1 && errno == ENOENT) continue; - if (fd != -1 && util::read_from_fd(fd, blob_msg, util::HP_VERSION_HEADER_SIZE) > 0) + if (fd != -1 && util::read_from_fd(fd, blob_msg, version::VERSION_BYTES_LEN) > 0) { ledger_blob raw_data; if (msg::fbuf::ledgermsg::create_ledger_blob_from_msg(raw_data, blob_msg, raw_inputs, raw_outputs) != -1) diff --git a/src/ledger/ledger_sync.cpp b/src/ledger/ledger_sync.cpp index 9c12c6bd..123b485a 100644 --- a/src/ledger/ledger_sync.cpp +++ b/src/ledger/ledger_sync.cpp @@ -1,6 +1,7 @@ -#include "./ledger_sync.hpp" +#include "ledger_sync.hpp" #include "ledger.hpp" +#include "../util/version.hpp" namespace ledger { @@ -17,8 +18,8 @@ namespace ledger } util::h32 prev_shard_hash_from_file; - // Start reading hash excluding hp_version header. - const int res = pread(fd, &prev_shard_hash_from_file, sizeof(util::h32), util::HP_VERSION_HEADER_SIZE); + // Start reading hash excluding version bytes. + const int res = pread(fd, &prev_shard_hash_from_file, sizeof(util::h32), version::VERSION_BYTES_LEN); close(fd); if (res == -1) { diff --git a/src/main.cpp b/src/main.cpp index 1cd9ef1e..3e7f3fcb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,6 +3,7 @@ **/ #include "pchheader.hpp" +#include "util/version.hpp" #include "util/util.hpp" #include "conf.hpp" #include "crypto.hpp" @@ -142,6 +143,9 @@ int main(int argc, char **argv) pthread_sigmask(SIG_BLOCK, &mask, NULL); } + if (version::init() == -1) + return -1; + // Extract the CLI args // This call will populate conf::ctx if (parse_cmd(argc, argv) != 0) @@ -150,7 +154,7 @@ int main(int argc, char **argv) if (conf::ctx.command == "version") { // Print the version - std::cout << util::HP_VERSION << std::endl; + std::cout << "HotPocket " << version::HP_VERSION << " (ledger version " << version::LEDGER_VERSION << ")" << std::endl; } else { @@ -189,7 +193,7 @@ int main(int argc, char **argv) hplog::init(); - LOG_INFO << "Hot Pocket " << util::HP_VERSION; + LOG_INFO << "Hot Pocket " << version::HP_VERSION; LOG_INFO << "Role: " << (conf::cfg.node.role == conf::ROLE::OBSERVER ? "Observer" : "Validator"); LOG_INFO << "Public key: " << conf::cfg.node.public_key_hex; LOG_INFO << "Contract: " << conf::cfg.contract.id << " (" << conf::cfg.contract.version << ")"; diff --git a/src/msg/bson/usrmsg_bson.cpp b/src/msg/bson/usrmsg_bson.cpp index b41fc9dd..a387e57b 100644 --- a/src/msg/bson/usrmsg_bson.cpp +++ b/src/msg/bson/usrmsg_bson.cpp @@ -1,6 +1,7 @@ #include "../../conf.hpp" #include "../../p2p/p2p.hpp" #include "../../pchheader.hpp" +#include "../../util/version.hpp" #include "../../util/util.hpp" #include "../../hplog.hpp" #include "../../ledger/ledger_query.hpp" @@ -28,7 +29,7 @@ namespace msg::usrmsg::bson encoder.key(msg::usrmsg::FLD_TYPE); encoder.string_value(msg::usrmsg::MSGTYPE_STAT_RESPONSE); encoder.key(msg::usrmsg::FLD_HP_VERSION); - encoder.string_value(conf::cfg.hp_version); + encoder.string_value(version::HP_VERSION); encoder.key(msg::usrmsg::FLD_LCL_SEQ); encoder.int64_value(lcl_seq_no); encoder.key(msg::usrmsg::FLD_LCL_HASH); diff --git a/src/msg/fbuf/p2pmsg.fbs b/src/msg/fbuf/p2pmsg.fbs index ca40e5e8..81fd4d8c 100644 --- a/src/msg/fbuf/p2pmsg.fbs +++ b/src/msg/fbuf/p2pmsg.fbs @@ -18,7 +18,7 @@ union P2PMsgContent { } table P2PMsg { - hp_version:string; + hp_version:[ubyte]; created_on:uint64; content:P2PMsgContent; } diff --git a/src/msg/fbuf/p2pmsg_conversion.cpp b/src/msg/fbuf/p2pmsg_conversion.cpp index f1a6503f..d75b43c5 100644 --- a/src/msg/fbuf/p2pmsg_conversion.cpp +++ b/src/msg/fbuf/p2pmsg_conversion.cpp @@ -1,3 +1,4 @@ +#include "../../util/version.hpp" #include "../../hpfs/hpfs_mount.hpp" #include "../../unl.hpp" #include "../../crypto.hpp" @@ -308,8 +309,9 @@ namespace msg::fbuf::p2pmsg void create_p2p_msg(flatbuffers::FlatBufferBuilder &builder, const msg::fbuf::p2pmsg::P2PMsgContent content_type, const flatbuffers::Offset content) { + std::string_view version((char *)version::HP_VERSION_BYTES, version::VERSION_BYTES_LEN); const auto p2pmsg = CreateP2PMsg(builder, - sv_to_flatbuf_str(builder, conf::cfg.hp_version), + sv_to_flatbuf_bytes(builder, version), util::get_epoch_milliseconds(), content_type, content); diff --git a/src/msg/fbuf/p2pmsg_generated.h b/src/msg/fbuf/p2pmsg_generated.h index 7227342a..571db1d6 100644 --- a/src/msg/fbuf/p2pmsg_generated.h +++ b/src/msg/fbuf/p2pmsg_generated.h @@ -247,11 +247,11 @@ struct P2PMsg FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VT_CONTENT_TYPE = 8, VT_CONTENT = 10 }; - const flatbuffers::String *hp_version() const { - return GetPointer(VT_HP_VERSION); + const flatbuffers::Vector *hp_version() const { + return GetPointer *>(VT_HP_VERSION); } - flatbuffers::String *mutable_hp_version() { - return GetPointer(VT_HP_VERSION); + flatbuffers::Vector *mutable_hp_version() { + return GetPointer *>(VT_HP_VERSION); } uint64_t created_on() const { return GetField(VT_CREATED_ON, 0); @@ -305,7 +305,7 @@ struct P2PMsg FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_HP_VERSION) && - verifier.VerifyString(hp_version()) && + verifier.VerifyVector(hp_version()) && VerifyField(verifier, VT_CREATED_ON) && VerifyField(verifier, VT_CONTENT_TYPE) && VerifyOffset(verifier, VT_CONTENT) && @@ -362,7 +362,7 @@ struct P2PMsgBuilder { typedef P2PMsg Table; flatbuffers::FlatBufferBuilder &fbb_; flatbuffers::uoffset_t start_; - void add_hp_version(flatbuffers::Offset hp_version) { + void add_hp_version(flatbuffers::Offset> hp_version) { fbb_.AddOffset(P2PMsg::VT_HP_VERSION, hp_version); } void add_created_on(uint64_t created_on) { @@ -388,7 +388,7 @@ struct P2PMsgBuilder { inline flatbuffers::Offset CreateP2PMsg( flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset hp_version = 0, + flatbuffers::Offset> hp_version = 0, uint64_t created_on = 0, msg::fbuf::p2pmsg::P2PMsgContent content_type = msg::fbuf::p2pmsg::P2PMsgContent_NONE, flatbuffers::Offset content = 0) { @@ -402,11 +402,11 @@ inline flatbuffers::Offset CreateP2PMsg( inline flatbuffers::Offset CreateP2PMsgDirect( flatbuffers::FlatBufferBuilder &_fbb, - const char *hp_version = nullptr, + const std::vector *hp_version = nullptr, uint64_t created_on = 0, msg::fbuf::p2pmsg::P2PMsgContent content_type = msg::fbuf::p2pmsg::P2PMsgContent_NONE, flatbuffers::Offset content = 0) { - auto hp_version__ = hp_version ? _fbb.CreateString(hp_version) : 0; + auto hp_version__ = hp_version ? _fbb.CreateVector(*hp_version) : 0; return msg::fbuf::p2pmsg::CreateP2PMsg( _fbb, hp_version__, diff --git a/src/msg/json/usrmsg_json.cpp b/src/msg/json/usrmsg_json.cpp index 296652c2..31e512c4 100644 --- a/src/msg/json/usrmsg_json.cpp +++ b/src/msg/json/usrmsg_json.cpp @@ -1,4 +1,5 @@ #include "../../pchheader.hpp" +#include "../../util/version.hpp" #include "../../util/util.hpp" #include "../../util/merkle_hash_tree.hpp" #include "../../unl.hpp" @@ -60,7 +61,7 @@ namespace msg::usrmsg::json msg += "{\""; msg += msg::usrmsg::FLD_HP_VERSION; msg += SEP_COLON; - msg += conf::cfg.hp_version; + msg += version::HP_VERSION; msg += SEP_COMMA; msg += msg::usrmsg::FLD_TYPE; msg += SEP_COLON; @@ -151,7 +152,7 @@ namespace msg::usrmsg::json msg += SEP_COMMA; msg += msg::usrmsg::FLD_HP_VERSION; msg += SEP_COLON; - msg += conf::cfg.hp_version; + msg += version::HP_VERSION; msg += SEP_COMMA; msg += msg::usrmsg::FLD_LCL_SEQ; msg += SEP_COLON_NOQUOTE; diff --git a/src/sc/sc.cpp b/src/sc/sc.cpp index c48f59ad..3529f4de 100644 --- a/src/sc/sc.cpp +++ b/src/sc/sc.cpp @@ -7,6 +7,7 @@ #include "../msg/controlmsg_common.hpp" #include "../msg/controlmsg_parser.hpp" #include "../unl.hpp" +#include "../util/version.hpp" #include "contract_serve.hpp" #include "sc.hpp" @@ -325,7 +326,7 @@ namespace sc // json string manually. std::ostringstream os; - os << "{\"hp_version\":\"" << util::HP_VERSION + os << "{\"hp_version\":\"" << version::HP_VERSION << "\",\"contract_id\":\"" << conf::cfg.contract.id << "\",\"pubkey\":\"" << conf::cfg.node.public_key_hex << "\",\"timestamp\":" << ctx.args.time diff --git a/src/util/util.cpp b/src/util/util.cpp index d99276b6..ae1f9b85 100644 --- a/src/util/util.cpp +++ b/src/util/util.cpp @@ -55,42 +55,6 @@ namespace util std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); } - /** - * Compare two version strings in the format of "1.12.3". - * v1 < v2 -> returns -1 - * v1 == v2 -> returns 0 - * v1 > v2 -> returns +1 - * Error -> returns -2 - * - * Remark on string_view: In other places of the code-base we utilize string_view - * to pass immutable string references around. However in this function we keep the 'const string&' - * syntax because istringstream doesn't support string_view. It's not worth optmising - * this code as it's not being used in high-scale processing. - */ - int version_compare(const std::string &x, const std::string &y) - { - std::istringstream ix(x), iy(y); - while (ix.good() || iy.good()) - { - int cx = 0, cy = 0; - ix >> cx; - iy >> cy; - - if ((!ix.eof() && !ix.good()) || (!iy.eof() && !iy.good())) - return -2; - - if (cx > cy) - return 1; - if (cx < cy) - return -1; - - ix.ignore(); - iy.ignore(); - } - - return 0; - } - // Provide a safe std::string overload for realpath const std::string realpath(const std::string &path) { @@ -494,42 +458,4 @@ namespace util ((uint64_t)data[7]); } - /** - * Create 16 byte hp version header. First 6 bytes contains the hp version and the - * next 10 bytes are reserved for future use. - * @param header Header byte array to be populated with header data. - * @param hp_version Current hp version string. - * @return Returns -1 on error and 0 on success. - */ - int create_hp_version_header(uint8_t *header, std::string_view hp_version) - { - const std::string delimeter = "."; - size_t start = 0; - size_t end = hp_version.find(delimeter); - - if (end == std::string::npos) - return -1; - - const uint16_t major = atoi(hp_version.substr(start, end - start).data()); - - start = end + delimeter.length(); - end = hp_version.find(delimeter, start); - - if (end == std::string::npos) - return -1; - - const uint16_t minor = atoi(hp_version.substr(start, end - start).data()); - start = end + delimeter.length(); - end = hp_version.find(delimeter, start); - - const uint16_t patch = atoi(hp_version.substr(start).data()); - uint16_to_bytes(&header[0], major); - uint16_to_bytes(&header[2], minor); - uint16_to_bytes(&header[4], patch); - // Make remaining bytes to zero for future use. - memset(&header[6], 0, 10); - - return 0; - } - } // namespace util diff --git a/src/util/util.hpp b/src/util/util.hpp index 889d32a2..3ee4d9d5 100644 --- a/src/util/util.hpp +++ b/src/util/util.hpp @@ -12,15 +12,6 @@ namespace util { - // Hot Pocket version. Displayed on 'hotpocket version' and written to new configs. - constexpr const char *HP_VERSION = "1.0.0"; - - // Minimum compatible config version (this will be used to validate configs). - constexpr const char *MIN_CONFIG_VERSION = "1.0.0"; - - // HP version header size. - constexpr const int HP_VERSION_HEADER_SIZE = 16; - /** * The messaging protocol used in a web socket channel. */ @@ -38,8 +29,6 @@ namespace util void sleep(const uint64_t milliseconds); - int version_compare(const std::string &x, const std::string &y); - const std::string realpath(const std::string &path); void mask_signal(); @@ -90,8 +79,6 @@ namespace util uint64_t uint64_from_bytes(const uint8_t *data); - int create_hp_version_header(uint8_t *header, std::string_view hp_version); - } // namespace util #endif diff --git a/src/util/version.cpp b/src/util/version.cpp new file mode 100644 index 00000000..35e1e9b2 --- /dev/null +++ b/src/util/version.cpp @@ -0,0 +1,100 @@ +#include "version.hpp" +#include "util.hpp" + +namespace version +{ + // Binary representations of the versions. (populated during version init) + uint8_t HP_VERSION_BYTES[VERSION_BYTES_LEN]; + uint8_t LEDGER_VERSION_BYTES[VERSION_BYTES_LEN]; + + int init() + { + // Generate version bytes. + if (set_version_bytes(HP_VERSION_BYTES, HP_VERSION) == -1 || + set_version_bytes(LEDGER_VERSION_BYTES, LEDGER_VERSION) == -1) + return -1; + + return 0; + } + + /** + * Create 8 byte binary version from version string. First 6 bytes contains the 3 version components and the + * next 2 bytes are reserved for future use. + * @param bytes Byte buffer to be populated with binary version data. + * @param version Version string. + * @return Returns -1 on error and 0 on success. + */ + int set_version_bytes(uint8_t *bytes, std::string_view version) + { + memset(bytes, 0, VERSION_BYTES_LEN); + + const std::string delimeter = "."; + size_t start = 0; + size_t end = version.find(delimeter); + + if (end == std::string::npos) + { + LOG_ERROR << "Invalid version " << version; + return -1; + } + + const uint16_t major = atoi(version.substr(start, end - start).data()); + + start = end + delimeter.length(); + end = version.find(delimeter, start); + + if (end == std::string::npos) + { + LOG_ERROR << "Invalid version " << version; + return -1; + } + + const uint16_t minor = atoi(version.substr(start, end - start).data()); + start = end + delimeter.length(); + end = version.find(delimeter, start); + + const uint16_t patch = atoi(version.substr(start).data()); + + util::uint16_to_bytes(&bytes[0], major); + util::uint16_to_bytes(&bytes[2], minor); + util::uint16_to_bytes(&bytes[4], patch); + + return 0; + } + + /** + * Compare two version strings in the format of "1.12.3". + * v1 < v2 -> returns -1 + * v1 == v2 -> returns 0 + * v1 > v2 -> returns +1 + * Error -> returns -2 + * + * Remark on string_view: In other places of the code-base we utilize string_view + * to pass immutable string references around. However in this function we keep the 'const string&' + * syntax because istringstream doesn't support string_view. It's not worth optimising + * this code as it's not being used in high-scale processing. + */ + int version_compare(const std::string &x, const std::string &y) + { + std::istringstream ix(x), iy(y); + while (ix.good() || iy.good()) + { + int cx = 0, cy = 0; + ix >> cx; + iy >> cy; + + if ((!ix.eof() && !ix.good()) || (!iy.eof() && !iy.good())) + return -2; + + if (cx > cy) + return 1; + if (cx < cy) + return -1; + + ix.ignore(); + iy.ignore(); + } + + return 0; + } +} \ No newline at end of file diff --git a/src/util/version.hpp b/src/util/version.hpp new file mode 100644 index 00000000..3fbd8c06 --- /dev/null +++ b/src/util/version.hpp @@ -0,0 +1,33 @@ +#ifndef _HP_UTIL_VERSION_ +#define _HP_UTIL_VERSION_ + +#include "../pchheader.hpp" + +namespace version +{ + // Hot Pocket version. Written to new configs and p2p/user messages. + constexpr const char *HP_VERSION = "1.0.0"; + + // Minimum compatible config version (this will be used to validate configs). + constexpr const char *MIN_CONFIG_VERSION = "1.0.0"; + + // Ledger file storage version. All nodes in a cluster MUST use the same ledger version. + constexpr const char *LEDGER_VERSION = "1.0.0"; + + // Version header size in bytes when serialized in binary format. (applies to hp version as well as ledger version) + // 2 bytes each for 3 version components. 2 bytes reserved. + constexpr const size_t VERSION_BYTES_LEN = 8; + + // Binary representations of the versions. (populated during version init) + extern uint8_t HP_VERSION_BYTES[VERSION_BYTES_LEN]; + extern uint8_t LEDGER_VERSION_BYTES[VERSION_BYTES_LEN]; + + int init(); + + int version_compare(const std::string &x, const std::string &y); + + int set_version_bytes(uint8_t *bytes, std::string_view version); + +} + +#endif \ No newline at end of file