From fe89aabe8f851e4b3621758bac35a9c816eed014 Mon Sep 17 00:00:00 2001
From: Georgiy Lebedev <g.lebedev@tarantool.org>
Date: Thu, 17 Nov 2022 13:51:37 +0300
Subject: [PATCH] box: export IPROTO constants and features to Lua
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Export IPROTO constants and features to Lua: for now, simply copy-paste all
constants from `src/box/iproto_{constants, features}.h` — it would be nice
to generate them in the future (#7103).

Closes #7894

@TarantoolBot document
Title: export IPROTO constants and features to Lua
or the API description and usage exmaples, see:
* [design document](https://www.notion.so/tarantool/box-iproto-override-44935a6ac7e04fb5a2c81ca713ed1bce#dcaf854d2a9f4743ae25661c16528523);
* tarantool/tarantool#7894.
---
 ...94-export-iproto-constants-and-features.md |   3 +
 src/box/CMakeLists.txt                        |   1 +
 src/box/iproto_constants.h                    |  18 +-
 src/box/iproto_features.h                     |   3 +
 src/box/lua/init.c                            |   2 +
 src/box/lua/iproto.c                          | 283 ++++++++++++++++++
 src/box/lua/iproto.h                          |  20 ++
 ...ort_iproto_constants_and_features_test.lua | 183 +++++++++++
 test/box/misc.result                          |   1 +
 9 files changed, 512 insertions(+), 2 deletions(-)
 create mode 100644 changelogs/unreleased/gh-7894-export-iproto-constants-and-features.md
 create mode 100644 src/box/lua/iproto.c
 create mode 100644 src/box/lua/iproto.h
 create mode 100644 test/box-luatest/gh_7894_export_iproto_constants_and_features_test.lua

diff --git a/changelogs/unreleased/gh-7894-export-iproto-constants-and-features.md b/changelogs/unreleased/gh-7894-export-iproto-constants-and-features.md
new file mode 100644
index 0000000000..860e026959
--- /dev/null
+++ b/changelogs/unreleased/gh-7894-export-iproto-constants-and-features.md
@@ -0,0 +1,3 @@
+## feature/box
+
+* Exported IPROTO constants and features to Lua (gh-7894).
diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index a6ae7cbf2a..1900e46b7f 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -248,6 +248,7 @@ set(box_sources
     lua/key_def.c
     lua/merger.c
     lua/watcher.c
+    lua/iproto.c
     ${bin_sources})
 
 if(ENABLE_AUDIT_LOG)
diff --git a/src/box/iproto_constants.h b/src/box/iproto_constants.h
index 3a9b61db58..685ef83ea9 100644
--- a/src/box/iproto_constants.h
+++ b/src/box/iproto_constants.h
@@ -49,7 +49,10 @@ enum {
 	XLOG_FIXHEADER_SIZE = 19
 };
 
-/** IPROTO_FLAGS bitfield constants. */
+/**
+ * IPROTO_FLAGS bitfield constants.
+ * `box.iproto.flag` needs to be updated correspondingly.
+ */
 enum {
 	/** Set for the last xrow in a transaction. */
 	IPROTO_FLAG_COMMIT = 0x01,
@@ -59,6 +62,9 @@ enum {
 	IPROTO_FLAG_WAIT_ACK = 0x04,
 };
 
+/**
+ * `box.iproto.key` needs to be updated correspondingly.
+ */
 enum iproto_key {
 	IPROTO_REQUEST_TYPE = 0x00,
 	IPROTO_SYNC = 0x01,
@@ -179,6 +185,7 @@ enum iproto_key {
  * Keys, stored in IPROTO_METADATA. They can not be received
  * in a request. Only sent as response, so no necessity in _strs
  * or _key_type arrays.
+ * `box.iproto.metadata_key` needs to be updated correspondingly.
  */
 enum iproto_metadata_key {
 	IPROTO_FIELD_NAME = 0,
@@ -189,6 +196,9 @@ enum iproto_metadata_key {
 	IPROTO_FIELD_SPAN = 5,
 };
 
+/**
+ * `box.iproto.ballot_key` needs to be updated correspondingly.
+ */
 enum iproto_ballot_key {
 	IPROTO_BALLOT_IS_RO_CFG = 0x01,
 	IPROTO_BALLOT_VCLOCK = 0x02,
@@ -208,7 +218,8 @@ iproto_key_bit(unsigned char key)
 extern const unsigned char iproto_key_type[IPROTO_KEY_MAX];
 
 /**
- * IPROTO command codes
+ * IPROTO command codes.
+ * `box.iproto.type` needs to be updated correspondingly.
  */
 enum iproto_type {
 	/** Acknowledgement that request or command is successful */
@@ -320,6 +331,9 @@ enum iproto_type {
 /** IPROTO type name by code */
 extern const char *iproto_type_strs[];
 
+/**
+ * `box.iproto.raft_key` needs to be updated correspondingly.
+ */
 enum iproto_raft_keys {
 	IPROTO_RAFT_TERM = 0,
 	IPROTO_RAFT_VOTE = 1,
diff --git a/src/box/iproto_features.h b/src/box/iproto_features.h
index 797cdad529..4cb65b3418 100644
--- a/src/box/iproto_features.h
+++ b/src/box/iproto_features.h
@@ -17,6 +17,8 @@ extern "C" {
 
 /**
  * IPROTO protocol feature ids returned by the IPROTO_ID command.
+ * `box.iproto.protocol_features` and `box.iproto.feature` need to be updated
+ * correspondingly.
  */
 enum iproto_feature_id {
 	/**
@@ -64,6 +66,7 @@ struct iproto_features {
 /**
  * Current IPROTO protocol version returned by the IPROTO_ID command.
  * It should be incremented every time a new feature is added or removed.
+ * `box.iproto.protocol_version` needs to be updated correspondingly.
  */
 enum {
 	IPROTO_CURRENT_VERSION = 4,
diff --git a/src/box/lua/init.c b/src/box/lua/init.c
index df9ab58a3a..2a484b848f 100644
--- a/src/box/lua/init.c
+++ b/src/box/lua/init.c
@@ -68,6 +68,7 @@
 #include "box/lua/key_def.h"
 #include "box/lua/merger.h"
 #include "box/lua/watcher.h"
+#include "box/lua/iproto.h"
 
 #include "mpstream/mpstream.h"
 
@@ -529,6 +530,7 @@ box_lua_init(struct lua_State *L)
 	box_lua_xlog_init(L);
 	box_lua_sql_init(L);
 	box_lua_watcher_init(L);
+	box_lua_iproto_init(L);
 #ifdef ENABLE_SPACE_UPGRADE
 	box_lua_space_upgrade_init(L);
 #endif
diff --git a/src/box/lua/iproto.c b/src/box/lua/iproto.c
new file mode 100644
index 0000000000..fca5dd03d0
--- /dev/null
+++ b/src/box/lua/iproto.c
@@ -0,0 +1,283 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2010-2022, Tarantool AUTHORS, please see AUTHORS file.
+ */
+
+#include "box/lua/iproto.h"
+
+#include "box/iproto_constants.h"
+#include "box/iproto_features.h"
+
+#include <lua.h>
+
+/**
+ * IPROTO constant from `src/box/iproto_{constants, features}.h`.
+ */
+struct iproto_constant {
+	/**
+	 * Constant literal, name of constant.
+	 */
+	const char *const name;
+	/**
+	 * Constant literal, value of constant.
+	 */
+	const lua_Integer val;
+};
+
+/**
+ * Pushes an array of IPROTO constants onto Lua stack.
+ */
+static void
+push_iproto_constant_subnamespace(struct lua_State *L, const char *subnamespace,
+				  const struct iproto_constant *constants,
+				  int constants_len)
+{
+	lua_createtable(L, 0, constants_len);
+	for (int i = 0; i < constants_len; ++i) {
+		lua_pushinteger(L, constants[i].val);
+		lua_setfield(L, -2, constants[i].name);
+	}
+	lua_setfield(L, -2, subnamespace);
+}
+
+/**
+ * Pushes IPROTO constants related to `IPROTO_FLAG` key onto Lua stack.
+ */
+static void
+push_iproto_flag_constants(struct lua_State *L)
+{
+	const struct iproto_constant flags[] = {
+		{"COMMIT", IPROTO_FLAG_COMMIT},
+		{"WAIT_SYNC", IPROTO_FLAG_WAIT_SYNC},
+		{"WAIT_ACK",  IPROTO_FLAG_WAIT_ACK},
+	};
+	push_iproto_constant_subnamespace(L, "flag", flags, lengthof(flags));
+}
+
+/**
+ * Pushes IPROTO constants from `iproto_key` enumeration onto Lua stack.
+ */
+static void
+push_iproto_key_enum(struct lua_State *L)
+{
+	const struct iproto_constant keys[] = {
+		{"REQUEST_TYPE", IPROTO_REQUEST_TYPE},
+		{"SYNC", IPROTO_SYNC},
+		{"REPLICA_ID", IPROTO_REPLICA_ID},
+		{"LSN", IPROTO_LSN},
+		{"TIMESTAMP", IPROTO_TIMESTAMP},
+		{"SCHEMA_VERSION", IPROTO_SCHEMA_VERSION},
+		{"SERVER_VERSION", IPROTO_SERVER_VERSION},
+		{"GROUP_ID", IPROTO_GROUP_ID},
+		{"TSN", IPROTO_TSN},
+		{"FLAGS", IPROTO_FLAGS},
+		{"STREAM_ID", IPROTO_STREAM_ID},
+		{"SPACE_ID", IPROTO_SPACE_ID},
+		{"INDEX_ID", IPROTO_INDEX_ID},
+		{"LIMIT", IPROTO_LIMIT},
+		{"OFFSET", IPROTO_OFFSET},
+		{"ITERATOR", IPROTO_ITERATOR},
+		{"INDEX_BASE", IPROTO_INDEX_BASE},
+		{"FETCH_POSITION", IPROTO_FETCH_POSITION},
+		{"KEY", IPROTO_KEY},
+		{"TUPLE", IPROTO_TUPLE},
+		{"FUNCTION_NAME", IPROTO_FUNCTION_NAME},
+		{"USER_NAME", IPROTO_USER_NAME},
+		{"INSTANCE_UUID", IPROTO_INSTANCE_UUID},
+		{"CLUSTER_UUID", IPROTO_CLUSTER_UUID},
+		{"VCLOCK", IPROTO_VCLOCK},
+		{"EXPR", IPROTO_EXPR},
+		{"OPS", IPROTO_OPS},
+		{"BALLOT", IPROTO_BALLOT},
+		{"TUPLE_META", IPROTO_TUPLE_META},
+		{"OPTIONS", IPROTO_OPTIONS},
+		{"OLD_TUPLE", IPROTO_OLD_TUPLE},
+		{"NEW_TUPLE", IPROTO_NEW_TUPLE},
+		{"IPROTO_AFTER_POSITION", IPROTO_AFTER_POSITION},
+		{"IPROTO_AFTER_TUPLE", IPROTO_AFTER_TUPLE},
+		{"DATA", IPROTO_DATA},
+		{"ERROR_24", IPROTO_ERROR_24},
+		{"METADATA", IPROTO_METADATA},
+		{"BIND_METADATA", IPROTO_BIND_METADATA},
+		{"BIND_COUNT", IPROTO_BIND_COUNT},
+		{"IPROTO_POSITION", IPROTO_POSITION},
+		{"SQL_TEXT", IPROTO_SQL_TEXT},
+		{"SQL_BIND", IPROTO_SQL_BIND},
+		{"SQL_INFO", IPROTO_SQL_INFO},
+		{"STMT_ID", IPROTO_STMT_ID},
+		{"REPLICA_ANON", IPROTO_REPLICA_ANON},
+		{"ID_FILTER", IPROTO_ID_FILTER},
+		{"ERROR", IPROTO_ERROR},
+		{"TERM", IPROTO_TERM},
+		{"VERSION", IPROTO_VERSION},
+		{"FEATURES", IPROTO_FEATURES},
+		{"TIMEOUT", IPROTO_TIMEOUT},
+		{"EVENT_KEY", IPROTO_EVENT_KEY},
+		{"EVENT_DATA", IPROTO_EVENT_DATA},
+		{"TXN_ISOLATION", IPROTO_TXN_ISOLATION},
+		{"VCLOCK_SYNC", IPROTO_VCLOCK_SYNC},
+	};
+	push_iproto_constant_subnamespace(L, "key", keys, lengthof(keys));
+}
+
+/**
+ * Pushes IPROTO constants from `iproto_metadata_key` enumeration onto Lua
+ * stack.
+ */
+static void
+push_iproto_metadata_key_enum(struct lua_State *L)
+{
+	const struct iproto_constant metadata_keys[] = {
+		{"NAME", IPROTO_FIELD_NAME},
+        	{"TYPE", IPROTO_FIELD_TYPE},
+        	{"COLL", IPROTO_FIELD_COLL},
+        	{"IS_NULLABLE", IPROTO_FIELD_IS_NULLABLE},
+        	{"IS_AUTOINCREMENT", IPROTO_FIELD_IS_AUTOINCREMENT},
+        	{"SPAN", IPROTO_FIELD_SPAN},
+	};
+	push_iproto_constant_subnamespace(L, "metadata_key", metadata_keys,
+					  lengthof(metadata_keys));
+}
+
+/**
+ * Pushes IPROTO constants from `iproto_ballot_key` enumeration onto Lua stack.
+ */
+static void
+push_iproto_ballot_key_enum(struct lua_State *L)
+{
+	const struct iproto_constant ballot_keys[] = {
+		{"IS_RO_CFG", IPROTO_BALLOT_IS_RO_CFG},
+        	{"VCLOCK", IPROTO_BALLOT_VCLOCK},
+        	{"GC_VCLOCK", IPROTO_BALLOT_GC_VCLOCK},
+        	{"IS_RO", IPROTO_BALLOT_IS_RO},
+        	{"IS_ANON", IPROTO_BALLOT_IS_ANON},
+        	{"IS_BOOTED", IPROTO_BALLOT_IS_BOOTED},
+        	{"CAN_LEAD", IPROTO_BALLOT_CAN_LEAD},
+	};
+	push_iproto_constant_subnamespace(L, "ballot_key", ballot_keys,
+					  lengthof(ballot_keys));
+}
+
+/**
+ * Pushes IPROTO constants from `iproto_type` enumeration onto Lua stack.
+ */
+static void
+push_iproto_type_enum(struct lua_State *L)
+{
+	const struct iproto_constant types[] = {
+		{"OK", IPROTO_OK},
+		{"SELECT", IPROTO_SELECT},
+		{"INSERT", IPROTO_INSERT},
+		{"REPLACE", IPROTO_REPLACE},
+		{"UPDATE", IPROTO_UPDATE},
+		{"DELETE", IPROTO_DELETE},
+		{"CALL_16", IPROTO_CALL_16},
+		{"AUTH", IPROTO_AUTH},
+		{"EVAL", IPROTO_EVAL},
+		{"UPSERT", IPROTO_UPSERT},
+		{"CALL", IPROTO_CALL},
+		{"EXECUTE", IPROTO_EXECUTE},
+		{"NOP", IPROTO_NOP},
+		{"PREPARE", IPROTO_PREPARE},
+		{"BEGIN", IPROTO_BEGIN},
+		{"COMMIT", IPROTO_COMMIT},
+		{"ROLLBACK", IPROTO_ROLLBACK},
+		{"RAFT", IPROTO_RAFT},
+		{"RAFT_PROMOTE", IPROTO_RAFT_PROMOTE},
+		{"RAFT_DEMOTE", IPROTO_RAFT_DEMOTE},
+		{"RAFT_CONFIRM", IPROTO_RAFT_CONFIRM},
+		{"RAFT_ROLLBACK", IPROTO_RAFT_ROLLBACK},
+		{"PING", IPROTO_PING},
+		{"JOIN", IPROTO_JOIN},
+		{"SUBSCRIBE", IPROTO_SUBSCRIBE},
+		{"VOTE_DEPRECATED", IPROTO_VOTE_DEPRECATED},
+		{"VOTE", IPROTO_VOTE},
+		{"FETCH_SNAPSHOT", IPROTO_FETCH_SNAPSHOT},
+		{"REGISTER", IPROTO_REGISTER},
+		{"JOIN_META", IPROTO_JOIN_META},
+		{"JOIN_SNAPSHOT", IPROTO_JOIN_SNAPSHOT},
+		{"ID", IPROTO_ID},
+		{"WATCH", IPROTO_WATCH},
+		{"UNWATCH", IPROTO_UNWATCH},
+		{"EVENT", IPROTO_EVENT},
+		{"CHUNK", IPROTO_CHUNK},
+		{"TYPE_ERROR", IPROTO_TYPE_ERROR},
+	};
+	push_iproto_constant_subnamespace(L, "type", types,
+					  lengthof(types));
+}
+
+/**
+ * Pushes IPROTO constants from `iproto_raft_keys` enumeration onto Lua stack.
+ */
+static void
+push_iproto_raft_keys_enum(struct lua_State *L)
+{
+	const struct iproto_constant raft_keys[] = {
+		{"TERM", IPROTO_RAFT_TERM},
+		{"VOTE", IPROTO_RAFT_VOTE},
+		{"STATE", IPROTO_RAFT_STATE},
+		{"VCLOCK", IPROTO_RAFT_VCLOCK},
+		{"LEADER_ID", IPROTO_RAFT_LEADER_ID},
+		{"IS_LEADER_SEEN", IPROTO_RAFT_IS_LEADER_SEEN},
+	};
+	push_iproto_constant_subnamespace(L, "raft_key", raft_keys,
+					  lengthof(raft_keys));
+}
+
+/**
+ * Pushes IPROTO constants onto Lua stack.
+ */
+static void
+push_iproto_constants(struct lua_State *L)
+{
+	push_iproto_flag_constants(L);
+	push_iproto_key_enum(L);
+	push_iproto_metadata_key_enum(L);
+	push_iproto_ballot_key_enum(L);
+	push_iproto_type_enum(L);
+	push_iproto_raft_keys_enum(L);
+}
+
+/**
+ * Pushes IPROTO protocol features onto Lua stack.
+ */
+static void
+push_iproto_protocol_features(struct lua_State *L)
+{
+	lua_pushinteger(L, IPROTO_CURRENT_VERSION);
+	lua_setfield(L, -2, "protocol_version");
+
+	const struct iproto_constant features[] = {
+		{"streams", IPROTO_FEATURE_STREAMS},
+        	{"transactions", IPROTO_FEATURE_TRANSACTIONS},
+        	{"error_extension", IPROTO_FEATURE_ERROR_EXTENSION},
+        	{"watchers", IPROTO_FEATURE_WATCHERS},
+        	{"pagination", IPROTO_FEATURE_PAGINATION},
+	};
+
+	lua_createtable(L, 0, lengthof(features));
+	for (size_t i = 0; i < lengthof(features); ++i) {
+		lua_pushboolean(L, true);
+		lua_setfield(L, -2, features[i].name);
+	}
+	lua_setfield(L, -2, "protocol_features");
+
+	push_iproto_constant_subnamespace(L, "feature", features,
+					  lengthof(features));
+}
+
+/**
+ * Initializes module for working with Tarantool's network subsystem.
+ */
+void
+box_lua_iproto_init(struct lua_State *L)
+{
+	lua_getfield(L, LUA_GLOBALSINDEX, "box");
+	lua_newtable(L);
+	push_iproto_constants(L);
+	push_iproto_protocol_features(L);
+	lua_setfield(L, -2, "iproto");
+	lua_pop(L, 1);
+}
diff --git a/src/box/lua/iproto.h b/src/box/lua/iproto.h
new file mode 100644
index 0000000000..14a55e618a
--- /dev/null
+++ b/src/box/lua/iproto.h
@@ -0,0 +1,20 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2010-2022, Tarantool AUTHORS, please see AUTHORS file.
+ */
+
+#pragma once
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+struct lua_State;
+
+void
+box_lua_iproto_init(struct lua_State *L);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif /* defined(__cplusplus) */
diff --git a/test/box-luatest/gh_7894_export_iproto_constants_and_features_test.lua b/test/box-luatest/gh_7894_export_iproto_constants_and_features_test.lua
new file mode 100644
index 0000000000..6cd1ed8ecc
--- /dev/null
+++ b/test/box-luatest/gh_7894_export_iproto_constants_and_features_test.lua
@@ -0,0 +1,183 @@
+local server = require('test.luatest_helpers.server')
+local t = require('luatest')
+
+local g = t.group()
+
+g.before_all(function(cg)
+    cg.server = server:new{
+        alias   = 'dflt',
+    }
+    cg.server:start()
+end)
+
+g.after_all(function(cg)
+    cg.server:drop()
+end)
+
+local reference_table = {
+    -- `IPROTO_FLAGS` bitfield constants enumeration.
+    flag = {
+        COMMIT = 0x01,
+        WAIT_SYNC = 0x02,
+        WAIT_ACK = 0x04,
+    },
+
+    -- `iproto_key` enumeration.
+    key = {
+        REQUEST_TYPE = 0x00,
+        SYNC = 0x01,
+        REPLICA_ID = 0x02,
+        LSN = 0x03,
+        TIMESTAMP = 0x04,
+        SCHEMA_VERSION = 0x05,
+        SERVER_VERSION = 0x06,
+        GROUP_ID = 0x07,
+        TSN = 0x08,
+        FLAGS = 0x09,
+        STREAM_ID = 0x0a,
+        SPACE_ID = 0x10,
+        INDEX_ID = 0x11,
+        LIMIT = 0x12,
+        OFFSET = 0x13,
+        ITERATOR = 0x14,
+        INDEX_BASE = 0x15,
+        FETCH_POSITION = 0x1f,
+        KEY = 0x20,
+        TUPLE = 0x21,
+        FUNCTION_NAME = 0x22,
+        USER_NAME = 0x23,
+        INSTANCE_UUID = 0x24,
+        CLUSTER_UUID = 0x25,
+        VCLOCK = 0x26,
+        EXPR = 0x27,
+        OPS = 0x28,
+        BALLOT = 0x29,
+        TUPLE_META = 0x2a,
+        OPTIONS = 0x2b,
+        OLD_TUPLE = 0x2c,
+        NEW_TUPLE = 0x2d,
+        IPROTO_AFTER_POSITION = 0x2e,
+        IPROTO_AFTER_TUPLE = 0x2f,
+        DATA = 0x30,
+        ERROR_24 = 0x31,
+        METADATA = 0x32,
+        BIND_METADATA = 0x33,
+        BIND_COUNT = 0x34,
+        IPROTO_POSITION = 0x35,
+        SQL_TEXT = 0x40,
+        SQL_BIND = 0x41,
+        SQL_INFO = 0x42,
+        STMT_ID = 0x43,
+        REPLICA_ANON = 0x50,
+        ID_FILTER = 0x51,
+        ERROR = 0x52,
+        TERM = 0x53,
+        VERSION = 0x54,
+        FEATURES = 0x55,
+        TIMEOUT = 0x56,
+        EVENT_KEY = 0x57,
+        EVENT_DATA = 0x58,
+        TXN_ISOLATION = 0x59,
+        VCLOCK_SYNC = 0x5a,
+    },
+
+    -- `iproto_metadata_key` enumeration.
+    metadata_key = {
+        NAME = 0,
+        TYPE = 1,
+        COLL = 2,
+        IS_NULLABLE = 3,
+        IS_AUTOINCREMENT = 4,
+        SPAN = 5,
+    },
+
+    -- `iproto_ballot_key` enumeration.
+    ballot_key = {
+        IS_RO_CFG = 0x01,
+        VCLOCK = 0x02,
+        GC_VCLOCK = 0x03,
+        IS_RO = 0x04,
+        IS_ANON = 0x05,
+        IS_BOOTED = 0x06,
+        CAN_LEAD = 0x07,
+    },
+
+    -- `iproto_type` enumeration.
+    type = {
+        OK = 0,
+        SELECT = 1,
+        INSERT = 2,
+        REPLACE = 3,
+        UPDATE = 4,
+        DELETE = 5,
+        CALL_16 = 6,
+        AUTH = 7,
+        EVAL = 8,
+        UPSERT = 9,
+        CALL = 10,
+        EXECUTE = 11,
+        NOP = 12,
+        PREPARE = 13,
+        BEGIN = 14,
+        COMMIT = 15,
+        ROLLBACK = 16,
+        RAFT = 30,
+        RAFT_PROMOTE = 31,
+        RAFT_DEMOTE = 32,
+        RAFT_CONFIRM = 40,
+        RAFT_ROLLBACK = 41,
+        PING = 64,
+        JOIN = 65,
+        SUBSCRIBE = 66,
+        VOTE_DEPRECATED = 67,
+        VOTE = 68,
+        FETCH_SNAPSHOT = 69,
+        REGISTER = 70,
+        JOIN_META = 71,
+        JOIN_SNAPSHOT = 72,
+        ID = 73,
+        WATCH = 74,
+        UNWATCH = 75,
+        EVENT = 76,
+        CHUNK = 128,
+        TYPE_ERROR = bit.lshift(1, 15),
+    },
+
+    -- `iproto_raft_keys` enumeration
+    raft_key = {
+        TERM = 0,
+        VOTE = 1,
+        STATE = 2,
+        VCLOCK = 3,
+        LEADER_ID = 4,
+        IS_LEADER_SEEN = 5,
+    },
+
+    -- `IPROTO_CURRENT_VERSION` constant
+    protocol_version = 4,
+
+    -- `feature_id` enumeration
+    protocol_features = {
+        streams = true,
+        transactions = true,
+        error_extension = true,
+        watchers = true,
+        pagination = true,
+    },
+    feature = {
+        streams = 0,
+        transactions = 1,
+        error_extension = 2,
+        watchers = 3,
+        pagination = 4,
+    },
+}
+
+-- Checks that IPROTO constants and features are exported correctly.
+g.test_iproto_constants_and_features_export = function(cg)
+    cg.server:exec(function(reference_table)
+        local t = require('luatest')
+
+        t.assert_equals(box.iproto, reference_table)
+    end, {reference_table})
+end
diff --git a/test/box/misc.result b/test/box/misc.result
index f477458cff..46a892afb3 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -85,6 +85,7 @@ t
   - index
   - info
   - internal
+  - iproto
   - is_in_txn
   - lib
   - on_commit
-- 
GitLab