diff --git a/src/device-state-access.h b/src/device-state-access.h index 8f89cbe2e..51d5633a9 100644 --- a/src/device-state-access.h +++ b/src/device-state-access.h @@ -14,8 +14,8 @@ // with this program (see COPYING). If not, see . // -#ifndef DEVICE_STATE_ACCESS -#define DEVICE_STATE_ACCESS +#ifndef DEVICE_STATE_ACCESS_H +#define DEVICE_STATE_ACCESS_H /// \file /// \brief Virtual state access implementation @@ -158,4 +158,4 @@ class device_state_access : public i_device_state_access { } // namespace cartesi -#endif +#endif // DEVICE_STATE_ACCESS_H diff --git a/src/i-device-state-access.h b/src/i-device-state-access.h index 1065c7825..075d2bc46 100644 --- a/src/i-device-state-access.h +++ b/src/i-device-state-access.h @@ -14,8 +14,8 @@ // with this program (see COPYING). If not, see . // -#ifndef I_DEVICE_STATE_ACCESS -#define I_DEVICE_STATE_ACCESS +#ifndef I_DEVICE_STATE_ACCESS_H +#define I_DEVICE_STATE_ACCESS_H #include @@ -231,4 +231,4 @@ class i_device_state_access { } // namespace cartesi -#endif +#endif // I_DEVICE_STATE_ACCESS_H diff --git a/src/i-virtual-machine.h b/src/i-virtual-machine.h index db4cd7abc..e3202ac03 100644 --- a/src/i-virtual-machine.h +++ b/src/i-virtual-machine.h @@ -14,8 +14,8 @@ // with this program (see COPYING). If not, see . // -#ifndef I_VIRTUAL_MACHINE -#define I_VIRTUAL_MACHINE +#ifndef I_VIRTUAL_MACHINE_H +#define I_VIRTUAL_MACHINE_H #include #include @@ -290,4 +290,4 @@ class i_virtual_machine { } // namespace cartesi -#endif +#endif // I_VIRTUAL_MACHINE_H diff --git a/src/jsonrpc-fork-result.h b/src/jsonrpc-fork-result.h index d42ec64a1..26a5ba73c 100644 --- a/src/jsonrpc-fork-result.h +++ b/src/jsonrpc-fork-result.h @@ -14,8 +14,8 @@ // with this program (see COPYING). If not, see . // -#ifndef JSONRPC_FORK_RESULT -#define JSONRPC_FORK_RESULT +#ifndef JSONRPC_FORK_RESULT_H +#define JSONRPC_FORK_RESULT_H namespace cartesi { @@ -26,4 +26,4 @@ struct fork_result final { } // namespace cartesi -#endif // JSONRPC_FORK_RESULT +#endif // JSONRPC_FORK_RESULT_H diff --git a/src/jsonrpc-machine-c-api.h b/src/jsonrpc-machine-c-api.h index abfbf43ef..9ae8312c3 100644 --- a/src/jsonrpc-machine-c-api.h +++ b/src/jsonrpc-machine-c-api.h @@ -122,12 +122,6 @@ CM_API cm_error cm_jsonrpc_get_server_version(const cm_machine *m, const char ** /// This function makes it the leader of its own program group. CM_API cm_error cm_jsonrpc_emancipate_server(cm_machine *m); -/// \brief Asks server to delay next request by a given amount of time. -/// \param m Pointer to a valid JSONRPC remote machine object. -/// \param ms Number of milliseconds to delay next request. -/// \returns 0 for success, non zero code for error. -CM_API cm_error cm_jsonrpc_delay_next_request(cm_machine *m, uint64_t ms); - // ----------------------------------------------------------------------------- // Client API functions // ----------------------------------------------------------------------------- @@ -167,6 +161,16 @@ CM_API cm_error cm_jsonrpc_get_cleanup_call(cm_machine *m, cm_jsonrpc_cleanup_ca /// \returns 0 for success, non zero code for error. CM_API cm_error cm_jsonrpc_get_server_address(cm_machine *m, const char **address); +// ----------------------------------------------------------------------------- +// Debugging and testing +// ----------------------------------------------------------------------------- + +/// \brief Asks server to delay next request by a given amount of time. +/// \param m Pointer to a valid JSONRPC remote machine object. +/// \param ms Number of milliseconds to delay next request. +/// \returns 0 for success, non zero code for error. +CM_API cm_error cm_jsonrpc_delay_next_request(cm_machine *m, uint64_t ms); + #ifdef __cplusplus } #endif diff --git a/src/jsonrpc-remote-machine.cpp b/src/jsonrpc-remote-machine.cpp index 4977a6105..f97a2d76c 100644 --- a/src/jsonrpc-remote-machine.cpp +++ b/src/jsonrpc-remote-machine.cpp @@ -62,6 +62,7 @@ #include "interpret.h" #include "json-util.h" #include "jsonrpc-discover.h" +#include "jsonrpc-version.h" #include "machine-config.h" #include "machine-merkle-tree.h" #include "machine-runtime-config.h" @@ -106,17 +107,6 @@ std::ostream &operator<<(std::ostream &out, log_prefix prefix) { using namespace std::string_literals; using json = nlohmann::json; -/// \brief Server semantic version major -static constexpr uint32_t server_version_major = 0; -/// \brief Server semantic version minor -static constexpr uint32_t server_version_minor = 5; -/// \brief Server semantic version patch -static constexpr uint32_t server_version_patch = 0; -/// \brief Server semantic version pre_release -static constexpr const char *server_version_pre_release = ""; -/// \brief Server semantic version build -static constexpr const char *server_version_build = ""; - /// \brief Installs a signal handler template static void install_signal_handler(int signum, HANDLER handler) { @@ -723,11 +713,11 @@ static json jsonrpc_get_version_handler(const json &j, const std::shared_ptr. +// + +#ifndef JSONRPC_VERSION_H +#define JSONRPC_VERSION_H + +#include + +namespace cartesi { + +constexpr uint32_t JSONRPC_VERSION_MAJOR = 0; +constexpr uint32_t JSONRPC_VERSION_MINOR = 5; +constexpr uint32_t JSONRPC_VERSION_PATCH = 0; +constexpr const char *JSONRPC_VERSION_PRE_RELEASE = ""; +constexpr const char *JSONRPC_VERSION_BUILD = ""; + +} // namespace cartesi + +#endif // JSONRPC_VERSION_H diff --git a/src/jsonrpc-virtual-machine.cpp b/src/jsonrpc-virtual-machine.cpp index aa4b01b7e..66122c30e 100644 --- a/src/jsonrpc-virtual-machine.cpp +++ b/src/jsonrpc-virtual-machine.cpp @@ -47,6 +47,7 @@ #include "interpret.h" #include "json-util.h" #include "json.hpp" +#include "jsonrpc-version.h" #include "machine-config.h" #include "machine-memory-range-descr.h" #include "machine-merkle-tree.h" @@ -330,9 +331,23 @@ const std::string &jsonrpc_virtual_machine::get_server_address() const { return m_address; } +static inline std::string semver_to_string(uint32_t major, uint32_t minor) { + return std::to_string(major) + "." + std::to_string(minor); +} + +void jsonrpc_virtual_machine::check_server_version() const { + const auto server_version = get_server_version(); + if (server_version.major != JSONRPC_VERSION_MAJOR || server_version.minor != JSONRPC_VERSION_MINOR) { + throw std::runtime_error{"expected server version "s + + semver_to_string(JSONRPC_VERSION_MAJOR, JSONRPC_VERSION_MINOR) + " (got "s + + semver_to_string(server_version.major, server_version.minor) + ")"s}; + } +} + jsonrpc_virtual_machine::jsonrpc_virtual_machine(std::string address) : m_address(std::move(address)) { // Install handler to ignore SIGPIPE lest we crash when a server closes a connection os_disable_sigpipe(); + check_server_version(); } static boost::asio::ip::tcp::endpoint address_to_endpoint(const std::string &address) { @@ -450,6 +465,7 @@ jsonrpc_virtual_machine::jsonrpc_virtual_machine(const std::string &address, for spawned.address = m_address; // Install handler to ignore SIGPIPE lest we crash when a server closes a connection os_disable_sigpipe(); + check_server_version(); } catch (...) { if (restore_sigprocmask) { sigprocmask(SIG_SETMASK, &omask, nullptr); diff --git a/src/jsonrpc-virtual-machine.h b/src/jsonrpc-virtual-machine.h index ddfbd2295..8afd3ff45 100644 --- a/src/jsonrpc-virtual-machine.h +++ b/src/jsonrpc-virtual-machine.h @@ -14,8 +14,8 @@ // with this program (see COPYING). If not, see . // -#ifndef JSONRPC_VIRTUAL_MACHINE -#define JSONRPC_VIRTUAL_MACHINE +#ifndef JSONRPC_VIRTUAL_MACHINE_H +#define JSONRPC_VIRTUAL_MACHINE_H #include #include @@ -136,6 +136,8 @@ class jsonrpc_virtual_machine final : public i_virtual_machine { const hash_type &root_hash_before, const access_log &log, const hash_type &root_hash_after) const override; bool do_is_jsonrpc_virtual_machine() const override; + void check_server_version() const; + mutable boost::asio::io_context m_ioc{1}; // The io_context is required for all I/O mutable boost::beast::tcp_stream m_stream{m_ioc}; // TCP stream for keep alive connections cleanup_call m_call{cleanup_call::nothing}; @@ -145,4 +147,4 @@ class jsonrpc_virtual_machine final : public i_virtual_machine { } // namespace cartesi -#endif +#endif // JSONRPC_VIRTUAL_MACHINE_H diff --git a/src/virtual-machine.h b/src/virtual-machine.h index d7c6e82fa..b1e36ed5a 100644 --- a/src/virtual-machine.h +++ b/src/virtual-machine.h @@ -14,8 +14,8 @@ // with this program (see COPYING). If not, see . // -#ifndef VIRTUAL_MACHINE -#define VIRTUAL_MACHINE +#ifndef VIRTUAL_MACHINE_H +#define VIRTUAL_MACHINE_H #include #include @@ -92,4 +92,4 @@ class virtual_machine : public i_virtual_machine { } // namespace cartesi -#endif +#endif // VIRTUAL_MACHINE_H diff --git a/tests/lua/machine-test.lua b/tests/lua/machine-test.lua index 0426fe790..1dbe35756 100755 --- a/tests/lua/machine-test.lua +++ b/tests/lua/machine-test.lua @@ -388,8 +388,8 @@ do_test("register values should match", function(machine) end) if machine_type ~= "local" then - print("\n\n check remote get_machine") - do_test("get_machine should get reference to working machine", function(machine) + print("\n\n checking remote-machine-specific functionality") + do_test("connect should get give access to working machine", function(machine) local machine_2 = jsonrpc.connect_server(machine:get_server_address()) assert(machine:get_root_hash() == machine_2:get_root_hash()) machine_2:destroy() @@ -400,6 +400,52 @@ if machine_type ~= "local" then assert(err:match("no machine")) end) + do_test("timeout mechanism should be respected", function(machine) + -- default timeout is -1 (meaning wait indefinitely) + local old_tm = machine:get_timeout() + assert(old_tm == -1) + -- set timeout to 100ms + machine:set_timeout(100) + -- make sure it stuck + assert(machine:get_timeout() == 100) + -- ask server to delay response by 1000ms + machine:delay_next_request(1000) + -- next call should fail with timeout + local ret, err = pcall(function() + machine:get_root_hash() + end) + assert(ret == false) + assert(err:match("jsonrpc error: timeout")) + machine:set_timeout(old_tm) + end) + + do_test("cleanup call should be respected", function(machine) + -- all machines returned by build_machine are configured to shutdown server + assert(machine:get_cleanup_call() == jsonrpc.SHUTDOWN) + local address + do + -- fork server, get address of new server, make sure fork holds a + -- machine, and set cleanup to destroy it on close + local m2 = machine:fork_server() + address = m2:get_server_address() + assert(not m2:is_empty()) + m2:set_cleanup_call(jsonrpc.DESTROY) + end + do + -- connect to server again, make sure it does not have a machine + -- anymore, and set cleanup to shut it down on close + local m2 = jsonrpc.connect_server(address) + assert(m2:is_empty()) + m2:set_cleanup_call(jsonrpc.SHUTDOWN) + end + -- now there should be no server at that address + local ret, err = pcall(function() + jsonrpc.connect_server(address) + end) + assert(ret == false) + assert(err:match("jsonrpc error: post error contacting " .. address)) + end) + do_test("jsonrpc connection error 49 after rapid successive requests ", function(machine) -- On a Mac, this loop will break with EADDRNOTAVAIL(49) -- Setting up the SO_LINGER to 0 fixed this issue