diff --git a/examples/js_client/hp-client-lib.js b/examples/js_client/hp-client-lib.js index 6d19c6bc..6b514371 100644 --- a/examples/js_client/hp-client-lib.js +++ b/examples/js_client/hp-client-lib.js @@ -7,7 +7,7 @@ if (isBrowser && window.HotPocket) return; - const supportedHpVersion = "0.0"; + const supportedHpVersion = "1.0.0"; const serverChallengeSize = 16; const outputValidationPassThreshold = 0.8; const connectionCheckIntervalMs = 1000; diff --git a/src/conf.hpp b/src/conf.hpp index d85184b3..23ad671f 100644 --- a/src/conf.hpp +++ b/src/conf.hpp @@ -196,7 +196,7 @@ namespace conf // Holds all the config values. struct hp_config { - std::string hp_version; // Version of Hot Pocket that generated the 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 5632a5a9..1a5d1deb 100644 --- a/src/ledger/ledger.cpp +++ b/src/ledger/ledger.cpp @@ -199,6 +199,13 @@ namespace ledger return -1; } + // 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) + { + LOG_ERROR << errno << ": Error creating and updating hp version table, shard: " << std::to_string(shard_seq_no); + return -1; + } + // Creating the ledger table. if (sqlite::create_ledger_table(*db) == -1) { @@ -216,6 +223,15 @@ namespace ledger return -1; } } + + // 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); @@ -224,7 +240,15 @@ namespace ledger LOG_ERROR << errno << ": Error creating prev_shard.hash file in shard " << std::to_string(shard_seq_no); return -1; } - if (write(fd, &prev_shard_hash, sizeof(util::h32)) == -1) + + 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[1].iov_base = &prev_shard_hash; + iov_vec[1].iov_len = sizeof(util::h32); + + if (writev(fd, iov_vec, 2) == -1) { LOG_ERROR << errno << ": Error writing to " << shard_hash_file_path << "."; close(fd); @@ -305,6 +329,14 @@ 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) { @@ -335,7 +367,15 @@ namespace ledger LOG_ERROR << errno << ": Error creating prev_shard.hash file in blob shard " << std::to_string(last_blob_shard_seq_no); return -1; } - if (write(fd, &prev_shard_hash, sizeof(util::h32)) == -1) + + 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[1].iov_base = &prev_shard_hash; + iov_vec[1].iov_len = sizeof(util::h32); + + if (writev(fd, iov_vec, 2) == -1) { LOG_ERROR << errno << ": Error writing to " << shard_hash_file_path << "."; close(fd); @@ -387,7 +427,14 @@ namespace ledger return -1; } - if (write(fd, builder.GetBufferPointer(), builder.GetSize()) == -1) + 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[1].iov_base = builder.GetBufferPointer(); + iov_vec[1].iov_len = builder.GetSize(); + + if (writev(fd, iov_vec, 2) == -1) { LOG_ERROR << errno << ": Error writing to ledger blob file. " << file_path; close(fd); @@ -481,9 +528,8 @@ namespace ledger } } uint8_t last_shard_seq_no_buf[8]; - if (read(fd, last_shard_seq_no_buf, 8) == -1) + if (pread(fd, last_shard_seq_no_buf, sizeof(last_shard_seq_no_buf), util::HP_VERSION_HEADER_SIZE) == -1) { - LOG_ERROR << errno << ": Error reading " << last_shard_seq_no_path; close(fd); return -1; @@ -513,7 +559,15 @@ 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); - // Write the prev_shard.hash to the new folder. + // 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) { @@ -522,7 +576,15 @@ namespace ledger } uint8_t seq_no_byte_str[8]; util::uint64_to_bytes(seq_no_byte_str, last_shard_seq_no); - if (write(fd, &seq_no_byte_str, 8) == -1) + + 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[1].iov_base = seq_no_byte_str; + iov_vec[1].iov_len = sizeof(seq_no_byte_str); + + if (writev(fd, iov_vec, 2) == -1) { LOG_ERROR << errno << ": Error updating the max_shard.seq_no file for shard " << std::to_string(last_shard_seq_no); close(fd); diff --git a/src/ledger/ledger_sync.cpp b/src/ledger/ledger_sync.cpp index 7d4ba900..6d9d0b92 100644 --- a/src/ledger/ledger_sync.cpp +++ b/src/ledger/ledger_sync.cpp @@ -17,7 +17,8 @@ namespace ledger } util::h32 prev_shard_hash_from_file; - const int res = read(fd, &prev_shard_hash_from_file, sizeof(util::h32)); + // 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); close(fd); if (res == -1) { diff --git a/src/ledger/sqlite.cpp b/src/ledger/sqlite.cpp index 3c508178..98ec291e 100644 --- a/src/ledger/sqlite.cpp +++ b/src/ledger/sqlite.cpp @@ -4,6 +4,8 @@ namespace ledger::sqlite { constexpr const char *LEDGER_TABLE = "ledger"; constexpr const char *LEDGER_COLUMNS = "seq_no, time, ledger_hash, prev_ledger_hash, data_hash, state_hash, patch_hash, user_hash, input_hash, output_hash"; + constexpr const char *HP_VERSION_TABLE = "hp"; + constexpr const char *HP_VERSION_COLUMN = "hp_version"; constexpr const char *COLUMN_DATA_TYPES[]{"INT", "TEXT"}; constexpr const char *CREATE_TABLE = "CREATE TABLE IF NOT EXISTS "; constexpr const char *INSERT_INTO = "INSERT INTO "; @@ -233,6 +235,29 @@ namespace ledger::sqlite return 0; } + /** + * Create and update the hp table from the hp version when creating a new shard. + * @param db Pointer to the db. + * @param version Hp version. + * @returns returns 0 on success, or -1 on error. + * + */ + int create_hp_version_table_and_update(sqlite3 *db, std::string_view version) + { + + const std::vector column_info{ + table_column_info(HP_VERSION_COLUMN, COLUMN_DATA_TYPE::TEXT)}; + + if (create_table(db, HP_VERSION_TABLE, column_info) == -1) + return -1; + + const std::string value_string = "\"" + std::string(version) + "\""; + if (insert_value(db, HP_VERSION_TABLE, HP_VERSION_COLUMN, value_string) == -1) + return -1; + + return 0; + } + /** * Inserts a ledger record. * @param db Pointer to the db. diff --git a/src/ledger/sqlite.hpp b/src/ledger/sqlite.hpp index 4fb73b59..ec546888 100644 --- a/src/ledger/sqlite.hpp +++ b/src/ledger/sqlite.hpp @@ -99,6 +99,8 @@ namespace ledger::sqlite // Ledger specific methdods. int create_ledger_table(sqlite3 *db); + int create_hp_version_table_and_update(sqlite3 *db, std::string_view version); + int insert_ledger_row(sqlite3 *db, const ledger &ledger); bool is_ledger_table_exist(sqlite3 *db); diff --git a/src/msg/json/usrmsg_json.cpp b/src/msg/json/usrmsg_json.cpp index 2ede5a30..cfff6e21 100644 --- a/src/msg/json/usrmsg_json.cpp +++ b/src/msg/json/usrmsg_json.cpp @@ -59,7 +59,7 @@ namespace msg::usrmsg::json msg += "{\""; msg += msg::usrmsg::FLD_HP_VERSION; msg += SEP_COLON; - msg += msg::usrmsg::USER_PROTOCOL_VERSION; + msg += conf::cfg.hp_version; msg += SEP_COMMA; msg += msg::usrmsg::FLD_TYPE; msg += SEP_COLON; diff --git a/src/msg/usrmsg_common.hpp b/src/msg/usrmsg_common.hpp index c33f200e..8f835a2c 100644 --- a/src/msg/usrmsg_common.hpp +++ b/src/msg/usrmsg_common.hpp @@ -7,7 +7,6 @@ namespace msg::usrmsg { // Length of user random challenge bytes. constexpr size_t CHALLENGE_LEN = 16; - constexpr const char *USER_PROTOCOL_VERSION = "0.0"; // Message field names constexpr const char *FLD_HP_VERSION = "hp_version"; diff --git a/src/util/util.cpp b/src/util/util.cpp index 683b60c4..af8d8e16 100644 --- a/src/util/util.cpp +++ b/src/util/util.cpp @@ -411,6 +411,33 @@ namespace util return fcntl(fd, F_SETLKW, &lock); } + /** + * Convert the given uint16_t number to bytes in big endian format. + * @param dest Byte array pointer. + * @param x Number to be converted. + */ + void uint16_to_bytes(uint8_t *dest, const uint16_t x) + { + dest[0] = (uint8_t)((x >> 8) & 0xff); + dest[1] = (uint8_t)((x >> 0) & 0xff); + } + + /** + * Read the uint16_t number from the given byte array which is in big endian format. + * @param data Byte array pointer. + * @return The uint16_t number in the given byte array. + */ + uint16_t uint16_from_bytes(const uint8_t *data) + { + return ((uint16_t)data[0] << 8) + + (uint16_t)data[1]; + } + + /** + * Convert the given uint32_t number to bytes in big endian format. + * @param dest Byte array pointer. + * @param x Number to be converted. + */ void uint32_to_bytes(uint8_t *dest, const uint32_t x) { dest[0] = (uint8_t)((x >> 24) & 0xff); @@ -419,6 +446,11 @@ namespace util dest[3] = (uint8_t)((x >> 0) & 0xff); } + /** + * Read the uint32_t number from the given byte array which is in big endian format. + * @param data Byte array pointer. + * @return The uint32_t number in the given byte array. + */ uint32_t uint32_from_bytes(const uint8_t *data) { return ((uint32_t)data[0] << 24) + @@ -427,6 +459,11 @@ namespace util ((uint32_t)data[3]); } + /** + * Convert the given uint64_t number to bytes in big endian format. + * @param dest Byte array pointer. + * @param x Number to be converted. + */ void uint64_to_bytes(uint8_t *dest, const uint64_t x) { dest[0] = (uint8_t)((x >> 56) & 0xff); @@ -439,6 +476,11 @@ namespace util dest[7] = (uint8_t)((x >> 0) & 0xff); } + /** + * Read the uint64_t number from the given byte array which is in big endian format. + * @param data Byte array pointer. + * @return The uint64_t number in the given byte array. + */ uint64_t uint64_from_bytes(const uint8_t *data) { return ((uint64_t)data[0] << 56) + @@ -451,4 +493,42 @@ 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 3bd195c2..6fbc8102 100644 --- a/src/util/util.hpp +++ b/src/util/util.hpp @@ -13,10 +13,13 @@ namespace util { // Hot Pocket version. Displayed on 'hotpocket version' and written to new configs. - constexpr const char *HP_VERSION = "0.1"; + 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 = "0.1"; + // 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. @@ -75,11 +78,20 @@ namespace util int release_lock(const int fd, struct flock &lock); + void uint16_to_bytes(uint8_t *dest, const uint16_t x); + + uint16_t uint16_from_bytes(const uint8_t *data); + void uint32_to_bytes(uint8_t *dest, const uint32_t x); + uint32_t uint32_from_bytes(const uint8_t *data); + void uint64_to_bytes(uint8_t *dest, const uint64_t x); + 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