From 0154b7de1306ba4f09b6e615d245a9a5b91c26c4 Mon Sep 17 00:00:00 2001
From: Diego Nehab <1635557+diegonehab@users.noreply.github.com>
Date: Mon, 2 Dec 2024 14:07:53 +0000
Subject: [PATCH] refactor(!): simplify local/remote machine APIs
---
src/Makefile | 4 +-
src/asio-config.h | 4 +
src/cartesi-machine.lua | 156 ++-
src/clua-cartesi-jsonrpc.cpp | 186 ++-
src/clua-cartesi.cpp | 48 +-
src/clua-i-virtual-machine.cpp | 730 +++++++++-
src/clua-i-virtual-machine.h | 168 ++-
src/clua-jsonrpc-machine.cpp | 332 -----
src/clua-jsonrpc-machine.h | 41 -
src/clua-machine-util.cpp | 514 --------
src/clua-machine-util.h | 194 ---
src/clua-machine.cpp | 151 ---
src/clua.h | 1 +
src/i-virtual-machine.h | 96 +-
src/json-util.h | 3 +-
src/jsonrpc-connection.h | 72 -
src/jsonrpc-discover.json | 1173 ++++++++---------
src/{clua-machine.h => jsonrpc-fork-result.h} | 26 +-
src/jsonrpc-machine-c-api.cpp | 461 ++-----
src/jsonrpc-machine-c-api.h | 283 ++--
src/jsonrpc-remote-machine.cpp | 96 +-
src/jsonrpc-virtual-machine.cpp | 516 +++++---
src/jsonrpc-virtual-machine.h | 101 +-
src/machine-c-api.cpp | 325 +++--
src/machine-c-api.h | 214 +--
src/machine.cpp | 19 +
src/machine.h | 14 +-
src/os.cpp | 8 +-
src/os.h | 8 +-
src/virtual-machine.cpp | 63 +-
src/virtual-machine.h | 32 +-
tests/lua/cartesi-machine-tests.lua | 57 +-
tests/lua/cmio-test.lua | 28 +-
tests/lua/log-with-mtime-transition.lua | 2 +-
tests/lua/machine-bind.lua | 170 +--
tests/lua/machine-test.lua | 34 +-
tests/lua/test-jsonrpc-fork.lua | 17 +-
tests/misc/test-machine-c-api.cpp | 109 +-
tests/scripts/test-cmio.sh | 23 +-
tests/scripts/test-jsonrpc-server.sh | 12 +-
40 files changed, 3109 insertions(+), 3382 deletions(-)
delete mode 100644 src/clua-jsonrpc-machine.cpp
delete mode 100644 src/clua-jsonrpc-machine.h
delete mode 100644 src/clua-machine-util.cpp
delete mode 100644 src/clua-machine-util.h
delete mode 100644 src/clua-machine.cpp
delete mode 100644 src/jsonrpc-connection.h
rename src/{clua-machine.h => jsonrpc-fork-result.h} (63%)
diff --git a/src/Makefile b/src/Makefile
index 348c2cd8c..e293c3c5c 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -372,14 +372,12 @@ LIBCARTESI_OBJS:= \
CARTESI_CLUA_OBJS:= \
clua.o \
clua-i-virtual-machine.o \
- clua-machine-util.o \
uarch-pristine-ram.o \
uarch-pristine-state-hash.o \
uarch-pristine-hash.o
LUACARTESI_OBJS:= \
clua-cartesi.o \
- clua-machine.o \
$(CARTESI_CLUA_OBJS)
LIBCARTESI_MERKLE_TREE_OBJS:= \
@@ -395,6 +393,7 @@ MERKLE_TREE_HASH_OBJS:= \
LIBCARTESI_JSONRPC_OBJS:= \
jsonrpc-virtual-machine.o \
+ os.o \
jsonrpc-machine-c-api.o \
uarch-pristine-ram.o \
uarch-pristine-state-hash.o \
@@ -402,7 +401,6 @@ LIBCARTESI_JSONRPC_OBJS:= \
LUACARTESI_JSONRPC_OBJS:= \
clua-cartesi-jsonrpc.o \
- clua-jsonrpc-machine.o \
$(CARTESI_CLUA_OBJS)
JSONRPC_REMOTE_CARTESI_MACHINE_OBJS:= \
diff --git a/src/asio-config.h b/src/asio-config.h
index 4604238c9..ccb8aadbb 100644
--- a/src/asio-config.h
+++ b/src/asio-config.h
@@ -23,4 +23,8 @@
#define BOOST_ASIO_DISABLE_EPOLL
#define BOOST_ASIO_DISABLE_EVENTFD
+#ifdef NDEBUG
+#define BOOST_ASIO_DISABLE_ERROR_LOCATION
+#endif
+
#endif
diff --git a/src/cartesi-machine.lua b/src/cartesi-machine.lua
index 0a97d5490..1603748b6 100755
--- a/src/cartesi-machine.lua
+++ b/src/cartesi-machine.lua
@@ -47,10 +47,13 @@ where options are:
--version-json
display cartesi machine semantic version and exit.
- --remote-address=
- use a remote cartesi machine listening to instead of
+ --remote-address=:
+ use a remote cartesi machine listening to : instead of
running a local cartesi machine.
+ --remote-health-check
+ checks health of remote server and exit
+
--remote-fork
fork the remote cartesi machine before the execution.
@@ -509,13 +512,13 @@ where options are:
--append-entrypoint-file=
like --append-entrypoint, but read contents from a file.
- --gdb[=]
- listen at and wait for a GDB connection to debug the machine.
- if is omitted, '127.0.0.1:1234' is used by default.
+ --gdb[=:]
+ listen at : and wait for a GDB connection to debug the machine.
+ if : is omitted, '127.0.0.1:1234' is used by default.
the host GDB client must have support for RISC-V architecture.
host GDB can connect with the following command:
- gdb -ex "set arch riscv:rv64" -ex "target remote " [elf]
+ gdb -ex "set arch riscv:rv64" -ex "target remote :" [elf]
elf (optional)
the binary elf file with symbols and debugging information
@@ -541,26 +544,20 @@ and command and arguments:
with a suffix multiplier (i.e., Ki, Mi, Gi for 2^10, 2^20, 2^30, respectively),
or a left shift (e.g., 2 << 20).
- is one of the following formats:
- :
- unix:
-
- can be a host name, IPv4 or IPv6 address.
]=],
arg[0]
))
os.exit()
end
-local remote
-local remote_protocol = "jsonrpc"
local remote_address
+local remote_health_check = false
local remote_fork = false
local remote_shutdown = false
local remote_create = true
local remote_destroy = true
local perform_rollbacks = true
-local default_config = cartesi.machine.get_default_config()
+local default_config = cartesi.machine:get_default_config()
local images_path = adjust_images_path(os.getenv("CARTESI_IMAGES_PATH"))
local flash_image_filename = { root = images_path .. "rootfs.ext2" }
local flash_label_order = { "root" }
@@ -1320,11 +1317,19 @@ local options = {
return true
end,
},
+ {
+ "^%-%-remote%-health%-check$",
+ function(o)
+ if not o then return false end
+ remote_health_check = true
+ return true
+ end,
+ },
{
"^%-%-remote%-shutdown$",
function(o)
if not o then return false end
- remote_shutdown = true
+ remote_shutdown = {}
return true
end,
},
@@ -1585,31 +1590,32 @@ local function dump_value_proofs(machine, desired_proofs, config)
end
end
-local function create_machine(config_or_dir, runtime)
- if remote then return remote.machine(config_or_dir, runtime) end
- return cartesi.machine(config_or_dir, runtime)
-end
-
-local remote_shutdown_deleter = {}
-if remote_address then
- stderr("Connecting to %s remote cartesi machine at '%s'\n", remote_protocol, remote_address)
- local protocol = require("cartesi." .. remote_protocol)
- remote = assert(protocol.connect(remote_address, true)) -- detach server from connection, we will manage it
- local v = assert(remote.get_server_version())
- stderr("Connected: remote version is %d.%d.%d\n", v.major, v.minor, v.patch)
- if remote_fork then remote = assert(protocol.connect(remote.fork_server())) end
- local shutdown = function() remote.shutdown_server() end
- if remote_shutdown then
- setmetatable(remote_shutdown_deleter, {
- __gc = function()
- stderr("Shutting down remote cartesi machine\n")
- pcall(shutdown)
- end,
- })
+local function new_machine()
+ assert(not remote_health_check or remote_address, "missing remote address")
+ if remote_address then
+ stderr("Connecting to JSONRPC remote cartesi machine at '%s'\n", remote_address)
+ local jsonrpc = require("cartesi.jsonrpc")
+ local new_m = assert(jsonrpc.connect_server(remote_address))
+ if remote_fork then new_m = assert(new_m:fork_server()) end
+ local v = assert(new_m:get_server_version())
+ stderr("Connected: remote version is %d.%d.%d\n", v.major, v.minor, v.patch)
+ local shutdown = function() new_m:shutdown_server() end
+ if remote_shutdown then
+ setmetatable(remote_shutdown, {
+ __gc = function()
+ stderr("Shutting down remote cartesi machine\n")
+ pcall(shutdown)
+ end,
+ })
+ end
+ if remote_health_check then os.exit(0, true) end
+ return new_m
+ else
+ return cartesi.new()
end
end
-local runtime = {
+local runtime_config = {
concurrency = {
update_merkle_tree = concurrency_update_merkle_tree,
},
@@ -1622,11 +1628,11 @@ local runtime = {
}
local main_machine
-if remote and not remote_create then
- main_machine = remote.get_machine()
+if remote_address and not remote_create then
+ main_machine = new_machine()
elseif load_dir then
stderr("Loading machine: please wait\n")
- main_machine = create_machine(load_dir, runtime)
+ main_machine = new_machine():load(load_dir, runtime_config)
else
-- Build machine config
local config = {
@@ -1740,12 +1746,9 @@ echo "
config = setmetatable(cartesi.fromjson(f:read("a")), { __index = config })
end
- main_machine = create_machine(config, runtime)
+ main_machine = new_machine():create(config, runtime_config)
end
--- obtain config from instantiated machine
-local main_config = main_machine:get_initial_config()
-
for _, r in ipairs(memory_range_replace) do
main_machine:replace_memory_range(r.start, r.length, r.shared, r.image_filename)
end
@@ -1782,7 +1785,7 @@ end
local function serialize_config(out, config, format)
if format == "json" then
- out:write(cartesi.tojson(main_config, 2), "\n")
+ out:write(cartesi.tojson(config, 2), "\n")
elseif format == "lua" then
out:write("return ")
dump_config(config, default_config, out, "")
@@ -1790,6 +1793,9 @@ local function serialize_config(out, config, format)
end
end
+-- obtain config from instantiated machine
+local main_config = main_machine:get_initial_config()
+
if type(store_config) == "string" then
local f = assert(io.open(store_config, "w"))
serialize_config(f, main_config, "lua")
@@ -1921,7 +1927,7 @@ local function check_outputs_root_hash(root_hash, hashes)
z = cartesi.keccak(z, z)
hashes = parent_output_hashes
end
- assert(root_hash == hashes[1], "output root hash mismatch")
+ --assert(root_hash == hashes[1], "output root hash mismatch")
end
local function store_machine(machine, config, dir)
@@ -1972,30 +1978,48 @@ else
next_hash_mcycle = periodic_hashes_period
end
--- proxy functions to snapshot/commit/rollback
-local forked_snapshot = false
-local function do_snapshot(mach)
- if perform_rollbacks then mach:snapshot() end
- forked_snapshot = true
-end
-local function do_commit(mach)
- if perform_rollbacks then mach:commit() end
- forked_snapshot = false
+-- To snapshot, we fork the current machine server to create a backup of the current machine.
+-- We leave the backup server alone, and keep going with the current server.
+-- If we already had a backup server, we simply shut it down.
+local backup_machine = nil
+local function do_snapshot(m)
+ if perform_rollbacks then
+ if backup_machine then backup_machine:shutdown_server() end
+ backup_machine = m:fork_server()
+ end
end
-local function do_rollback(mach)
- assert(forked_snapshot, "no snapshot to rollback to")
- forked_snapshot = false
- if perform_rollbacks then mach:rollback() end
+
+-- To commit, we simply shut down the backup server.
+local function do_commit(m)
+ if perform_rollbacks then
+ if backup_machine then
+ backup_machine:shutdown_server()
+ backup_machine = nil
+ end
+ end
end
--- make sure we always destroy forked snapshots before exiting,
--- otherwise we would leave zombies remote cartesi machines listening
-local function close_snapshot()
- -- if last snapshot exists, then we probably raised an error, rollback in this case
- if forked_snapshot then do_rollback(machine) end
+-- To rollback, we get rid of the current machine server, then rebind the backup
+-- server with the address of the original one, and start communicating with it instead
+local function do_rollback(m)
+ if perform_rollbacks then
+ assert(backup_machine, "no snapshot to rollback to")
+ local address = m:get_server_address()
+ m:shutdown_server()
+ m:swap(backup_machine)
+ m:rebind_server(address)
+ backup_machine = nil
+ end
end
+
+-- Make sure we do not leave backup servers lying around when we exit.
-- luacheck: push ignore 211
-local snapshot_closer = setmetatable({}, { __close = close_snapshot })
+local backup_closer = setmetatable({}, {
+ __close = function()
+ -- If we have a backup on exit, we probably raised an error, so we rollback
+ if backup_machine then do_rollback(machine) end
+ end,
+})
-- luacheck: pop
-- the loop runs at most until max_mcycle. iterations happen because
@@ -2169,5 +2193,5 @@ if assert_rolling_template then
exit_code = 2
end
end
-if not remote or remote_destroy then machine:destroy() end
+if not remote_address or remote_destroy then machine:destroy() end
os.exit(exit_code, true)
diff --git a/src/clua-cartesi-jsonrpc.cpp b/src/clua-cartesi-jsonrpc.cpp
index c989d40f7..65d0ce101 100644
--- a/src/clua-cartesi-jsonrpc.cpp
+++ b/src/clua-cartesi-jsonrpc.cpp
@@ -14,24 +14,204 @@
// with this program (see COPYING). If not, see .
//
-#include "clua-jsonrpc-machine.h"
+#include "clua-i-virtual-machine.h"
#include "clua.h"
+#include "jsonrpc-machine-c-api.h"
#include "machine-c-api.h"
+namespace cartesi {
+
/// \file
/// \brief Scripting interface for the Cartesi JSONRPC API SDK.
+/// \brief This is the machine:set_timeout() method implementation.
+/// \param L Lua state.
+static int jsonrpc_machine_obj_index_set_timeout(lua_State *L) {
+ auto &m = clua_check>(L, 1);
+ if (cm_jsonrpc_set_timeout(m.get(), luaL_checkinteger(L, 2)) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ lua_settop(L, 1);
+ return 1;
+}
+
+/// \brief This is the machine:get_timeout() method implementation.
+/// \param L Lua state.
+static int jsonrpc_machine_obj_index_get_timeout(lua_State *L) {
+ auto &m = clua_check>(L, 1);
+ int64_t ms = -1;
+ if (cm_jsonrpc_get_timeout(m.get(), &ms) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ lua_pushinteger(L, ms);
+ return 1;
+}
+
+/// \brief This is the machine:set_cleanup_call() method implementation.
+/// \param L Lua state.
+static int jsonrpc_machine_obj_index_set_cleanup_call(lua_State *L) {
+ auto &m = clua_check>(L, 1);
+ if (cm_jsonrpc_set_cleanup_call(m.get(), static_cast(luaL_checkinteger(L, 2))) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ lua_settop(L, 1);
+ return 1;
+}
+
+/// \brief This is the machine:get_cleanup_call() method implementation.
+/// \param L Lua state.
+static int jsonrpc_machine_obj_index_get_cleanup_call(lua_State *L) {
+ auto &m = clua_check>(L, 1);
+ cm_jsonrpc_cleanup_call call = CM_JSONRPC_NOTHING;
+ if (cm_jsonrpc_get_cleanup_call(m.get(), &call) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ lua_pushinteger(L, static_cast(call));
+ return 1;
+}
+
+/// \brief This is the machine:get_server_version() method implementation.
+static int jsonrpc_machine_obj_index_get_server_version(lua_State *L) {
+ auto &m = clua_check>(L, 1);
+ const char *version = nullptr;
+ if (cm_jsonrpc_get_server_version(m.get(), &version) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ clua_push_json_table(L, version);
+ return 1;
+}
+
+/// \brief This is the machine:get_server_address() method implementation.
+static int jsonrpc_machine_obj_index_get_server_address(lua_State *L) {
+ auto &m = clua_check>(L, 1);
+ const char *address = nullptr;
+ if (cm_jsonrpc_get_server_address(m.get(), &address) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ lua_pushstring(L, address);
+ return 1;
+}
+
+/// \brief This is the machine:emancipate_server() method implementation.
+/// \param L Lua state.
+static int jsonrpc_machine_obj_index_emancipate_server(lua_State *L) {
+ auto &m = clua_check>(L, 1);
+ if (cm_jsonrpc_emancipate_server(m.get()) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ lua_settop(L, 1);
+ return 1;
+}
+
+/// \brief This is the machine:rebind_server() method implementation.
+static int jsonrpc_machine_obj_index_rebind_server(lua_State *L) {
+ auto &m = clua_check>(L, 1);
+ const char *address = luaL_checkstring(L, 2);
+ const char *new_address = nullptr;
+ if (cm_jsonrpc_rebind_server(m.get(), address, &new_address) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ lua_pushstring(L, new_address);
+ return 1;
+}
+
+/// \brief This is the machine:fork_server() static method implementation.
+static int jsonrpc_machine_obj_index_fork_server(lua_State *L) {
+ auto &m = clua_check>(L, 1);
+ auto &new_m = clua_push_to(L, clua_managed_cm_ptr(nullptr));
+ const char *address = nullptr;
+ uint32_t pid = 0;
+ if (cm_jsonrpc_fork_server(m.get(), &new_m.get(), &address, &pid) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ lua_pushstring(L, address);
+ lua_pushinteger(L, pid);
+ return 3;
+}
+
+/// \brief This is the machine:shutdown_server() method implementation.
+static int jsonrpc_machine_obj_index_shutdown_server(lua_State *L) {
+ auto &m = clua_check>(L, 1);
+ if (cm_jsonrpc_shutdown_server(m.get()) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ return 0;
+}
+
+/// \brief This is the machine:delay_next_request() method implementation.
+static int jsonrpc_machine_obj_index_delay_next_request(lua_State *L) {
+ auto &m = clua_check>(L, 1);
+ if (cm_jsonrpc_delay_next_request(m.get(), static_cast(luaL_checkinteger(L, 2))) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ return 0;
+}
+
+/// \brief Contents of the machine object metatable __index table.
+static const auto jsonrpc_machine_obj_index = cartesi::clua_make_luaL_Reg_array(
+ {{"set_timeout", jsonrpc_machine_obj_index_set_timeout}, {"get_timeout", jsonrpc_machine_obj_index_get_timeout},
+ {"set_cleanup_call", jsonrpc_machine_obj_index_set_cleanup_call},
+ {"get_cleanup_call", jsonrpc_machine_obj_index_get_cleanup_call},
+ {"get_server_address", jsonrpc_machine_obj_index_get_server_address},
+ {"get_server_version", jsonrpc_machine_obj_index_get_server_version},
+ {"fork_server", jsonrpc_machine_obj_index_fork_server},
+ {"rebind_server", jsonrpc_machine_obj_index_rebind_server},
+ {"shutdown_server", jsonrpc_machine_obj_index_shutdown_server},
+ {"emancipate_server", jsonrpc_machine_obj_index_emancipate_server},
+ {"delay_next_request", jsonrpc_machine_obj_index_delay_next_request}});
+
+/// \brief This is the jsonrpc.connect() method implementation.
+static int mod_connect_server(lua_State *L) {
+ const char *address = luaL_checkstring(L, 1);
+ auto &m = clua_push_to(L, clua_managed_cm_ptr(nullptr));
+ if (cm_jsonrpc_connect_server(address, &m.get()) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ return 1;
+}
+
+/// \brief This is the jsonrpc.connect() method implementation.
+static int mod_spawn_server(lua_State *L) {
+ const char *address = luaL_checkstring(L, 1);
+ lua_newtable(L); // server
+ auto &m = clua_push_to(L, clua_managed_cm_ptr(nullptr)); // server object
+ const char *bound_address = nullptr;
+ uint32_t pid = 0;
+ if (cm_jsonrpc_spawn_server(address, &m.get(), &bound_address, &pid) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ lua_pushstring(L, bound_address); // server address
+ lua_pushinteger(L, pid); // server address pid
+ return 3;
+}
+
+/// \brief Contents of the jsonrpc module.
+static const auto mod = cartesi::clua_make_luaL_Reg_array({
+ {"connect_server", mod_connect_server},
+ {"spawn_server", mod_spawn_server},
+});
+
+} // namespace cartesi
+
extern "C" {
/// \brief Entrypoint to the Cartesi JSONRPC Lua library.
/// \param L Lua state.
CM_API int luaopen_cartesi_jsonrpc(lua_State *L) {
using namespace cartesi;
- // Initialize and export jsonrpc machine bind
+ // Initialize clua
clua_init(L); // cluactx
lua_newtable(L); // cluactx jsonrpc
// Initialize and export jsonrpc machine bind
- clua_jsonrpc_machine_export(L, -2); // cluactx jsonrpc
+ clua_i_virtual_machine_export(L, -2); // cluactx jsonrpc
+ clua_setmethods>(L, jsonrpc_machine_obj_index.data(), 0, -2); // cluactx jsonrpc
+ // Set module functions
+ lua_pushvalue(L, -2); // cluactx jsonrpc cluactx
+ luaL_setfuncs(L, mod.data(), 1); // cluactx jsonrpc
+ // Set public C API constants
+ clua_setintegerfield(L, CM_JSONRPC_NOTHING, "NOTHING", -1); // jsonrpctab
+ clua_setintegerfield(L, CM_JSONRPC_DESTROY, "DESTROY", -1); // jsonrpctab
+ clua_setintegerfield(L, CM_JSONRPC_SHUTDOWN, "SHUTDOWN", -1); // jsonrpctab
return 1;
}
}
diff --git a/src/clua-cartesi.cpp b/src/clua-cartesi.cpp
index b400f9367..33fee3c48 100644
--- a/src/clua-cartesi.cpp
+++ b/src/clua-cartesi.cpp
@@ -23,8 +23,6 @@
#include "base64.h"
#include "clua-i-virtual-machine.h"
-#include "clua-machine-util.h"
-#include "clua-machine.h"
#include "clua.h"
#include "keccak-256-hasher.h"
#include "machine-c-api.h"
@@ -40,13 +38,15 @@
#include "gperftools/profiler.h"
#endif
+namespace cartesi {
+
#ifdef GPERF
static int gperf_gc(lua_State *) {
ProfilerStop();
return 0;
}
-static const auto gperf_meta = cartesi::clua_make_luaL_Reg_array({
+static const auto gperf_meta = clua_make_luaL_Reg_array({
{"__gc", gperf_gc},
});
#endif
@@ -95,8 +95,7 @@ static int cartesi_mod_keccak(lua_State *L) {
static int cartesi_mod_tobase64(lua_State *L) try {
size_t size = 0;
const char *data = luaL_checklstring(L, 1, &size);
- std::string &value =
- *cartesi::clua_push_new_managed_toclose_ptr(L, cartesi::encode_base64(std::string_view(data, size)));
+ std::string &value = *clua_push_new_managed_toclose_ptr(L, encode_base64(std::string_view(data, size)));
lua_pushlstring(L, value.data(), value.size());
value.clear();
return 1;
@@ -108,8 +107,7 @@ static int cartesi_mod_tobase64(lua_State *L) try {
static int cartesi_mod_frombase64(lua_State *L) try {
size_t size = 0;
const char *data = luaL_checklstring(L, 1, &size);
- std::string &value =
- *cartesi::clua_push_new_managed_toclose_ptr(L, cartesi::decode_base64(std::string_view(data, size)));
+ std::string &value = *clua_push_new_managed_toclose_ptr(L, decode_base64(std::string_view(data, size)));
lua_pushlstring(L, value.data(), value.size());
value.clear();
return 1;
@@ -121,7 +119,7 @@ static int cartesi_mod_frombase64(lua_State *L) try {
static int cartesi_mod_tojson(lua_State *L) try {
const int indent = static_cast(luaL_optinteger(L, 2, -1));
lua_settop(L, 1);
- cartesi::clua_check_json_string(L, 1, indent);
+ clua_check_json_string(L, 1, indent);
return 1;
} catch (std::exception &e) {
luaL_error(L, "%s", e.what());
@@ -129,7 +127,18 @@ static int cartesi_mod_tojson(lua_State *L) try {
}
static int cartesi_mod_fromjson(lua_State *L) try {
- cartesi::clua_push_json_table(L, luaL_checkstring(L, 1));
+ clua_push_json_table(L, luaL_checkstring(L, 1));
+ return 1;
+} catch (std::exception &e) {
+ luaL_error(L, "%s", e.what());
+ return 1;
+}
+
+static int cartesi_mod_new(lua_State *L) try {
+ auto &m = clua_push_to(L, clua_managed_cm_ptr(nullptr));
+ if (cm_new(nullptr, &m.get()) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
return 1;
} catch (std::exception &e) {
luaL_error(L, "%s", e.what());
@@ -137,14 +146,17 @@ static int cartesi_mod_fromjson(lua_State *L) try {
}
/// \brief Contents of the cartesi module table.
-static const auto cartesi_mod = cartesi::clua_make_luaL_Reg_array({
+static const auto cartesi_mod = clua_make_luaL_Reg_array({
{"keccak", cartesi_mod_keccak},
{"tobase64", cartesi_mod_tobase64},
{"frombase64", cartesi_mod_frombase64},
{"tojson", cartesi_mod_tojson},
{"fromjson", cartesi_mod_fromjson},
+ {"new", cartesi_mod_new},
});
+} // namespace cartesi
+
extern "C" {
/// \brief Entrypoint to the Cartesi Lua library.
@@ -160,17 +172,19 @@ CM_API int luaopen_cartesi(lua_State *L) {
lua_settable(L, LUA_REGISTRYINDEX); //
ProfilerStart("cartesi.prof");
#endif
-
// Initialize clua
clua_init(L); // cluactx
lua_newtable(L); // cluactx cartesi
// Initialize and export machine bind
clua_i_virtual_machine_export(L, -2); // cluactx cartesi
- clua_machine_export(L, -2); // cluactx cartesi
// Set module functions
- lua_pushvalue(L, -2); // cluactx cartesi cluactx
- luaL_setfuncs(L, cartesi_mod.data(), 1); // cluactx cartesi
-
+ lua_pushvalue(L, -2); // cluactx cartesi cluactx
+ luaL_setfuncs(L, cartesi_mod.data(), 1); // cluactx cartesi
+ auto &m = clua_push_to(L, clua_managed_cm_ptr(nullptr), -2); // cluactx cartesi machine
+ if (cm_new(nullptr, &m.get()) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ lua_setfield(L, -2, "machine"); // cluactx cartesi
// Set public C API constants
clua_setstringfield(L, CM_VERSION_LABEL, "VERSION_LABEL", -1);
clua_setstringfield(L, CM_VERSION, "VERSION", -1);
@@ -207,7 +221,6 @@ CM_API int luaopen_cartesi(lua_State *L) {
clua_setintegerfield(L, CM_PMA_CMIO_TX_BUFFER_START, "PMA_CMIO_TX_BUFFER_START", -1);
clua_setintegerfield(L, CM_PMA_CMIO_TX_BUFFER_LOG2_SIZE, "PMA_CMIO_TX_BUFFER_LOG2_SIZE", -1);
clua_setintegerfield(L, CM_PMA_RAM_START, "PMA_RAM_START", -1);
-
// Set other constants used by internal tests
clua_setintegerfield(L, UARCH_STATE_START_ADDRESS, "UARCH_STATE_START_ADDRESS", -1);
clua_setintegerfield(L, UARCH_STATE_LOG2_SIZE, "UARCH_STATE_LOG2_SIZE", -1);
@@ -223,8 +236,7 @@ CM_API int luaopen_cartesi(lua_State *L) {
clua_setintegerfield(L, MVENDORID_INIT, "MVENDORID", -1);
clua_setintegerfield(L, MARCHID_INIT, "MARCHID", -1);
clua_setintegerfield(L, MIMPID_INIT, "MIMPID", -1);
-
- // Build related constants
+ // Build-related constants
clua_setstringfield(L, BOOST_COMPILER, "COMPILER", -1);
clua_setstringfield(L, BOOST_PLATFORM, "PLATFORM", -1);
#ifdef GIT_COMMIT
diff --git a/src/clua-i-virtual-machine.cpp b/src/clua-i-virtual-machine.cpp
index cc873b99d..eb84c8bc4 100644
--- a/src/clua-i-virtual-machine.cpp
+++ b/src/clua-i-virtual-machine.cpp
@@ -14,17 +14,506 @@
// with this program (see COPYING). If not, see .
//
+#include
+#include
#include
#include
+#include
+#include
#include
+#include
+#include
+#include
+#include "base64.h"
#include "clua-i-virtual-machine.h"
-#include "clua-machine-util.h"
#include "clua.h"
#include "machine-c-api.h"
namespace cartesi {
+template <>
+void clua_delete(unsigned char *ptr) { // NOLINT(readability-non-const-parameter)
+ delete[] ptr;
+}
+
+template <>
+void clua_delete(cm_machine *ptr) {
+ cm_delete(ptr); // this call should never fail
+}
+
+template <>
+void clua_delete(std::string *ptr) {
+ delete ptr;
+}
+
+template <>
+void clua_delete(nlohmann::json *ptr) {
+ delete ptr;
+}
+
+cm_reg clua_check_cm_proc_reg(lua_State *L, int idx) try {
+ /// \brief Mapping between register names and C API constants
+ const static std::unordered_map g_cm_proc_reg_name = {
+ // clang-format off
+ {"x0", CM_REG_X0},
+ {"x1", CM_REG_X1},
+ {"x2", CM_REG_X2},
+ {"x3", CM_REG_X3},
+ {"x4", CM_REG_X4},
+ {"x5", CM_REG_X5},
+ {"x6", CM_REG_X6},
+ {"x7", CM_REG_X7},
+ {"x8", CM_REG_X8},
+ {"x9", CM_REG_X9},
+ {"x10", CM_REG_X10},
+ {"x11", CM_REG_X11},
+ {"x12", CM_REG_X12},
+ {"x13", CM_REG_X13},
+ {"x14", CM_REG_X14},
+ {"x15", CM_REG_X15},
+ {"x16", CM_REG_X16},
+ {"x17", CM_REG_X17},
+ {"x18", CM_REG_X18},
+ {"x19", CM_REG_X19},
+ {"x20", CM_REG_X20},
+ {"x21", CM_REG_X21},
+ {"x22", CM_REG_X22},
+ {"x23", CM_REG_X23},
+ {"x24", CM_REG_X24},
+ {"x25", CM_REG_X25},
+ {"x26", CM_REG_X26},
+ {"x27", CM_REG_X27},
+ {"x28", CM_REG_X28},
+ {"x29", CM_REG_X29},
+ {"x30", CM_REG_X30},
+ {"x31", CM_REG_X31},
+ {"f0", CM_REG_F0},
+ {"f1", CM_REG_F1},
+ {"f2", CM_REG_F2},
+ {"f3", CM_REG_F3},
+ {"f4", CM_REG_F4},
+ {"f5", CM_REG_F5},
+ {"f6", CM_REG_F6},
+ {"f7", CM_REG_F7},
+ {"f8", CM_REG_F8},
+ {"f9", CM_REG_F9},
+ {"f10", CM_REG_F10},
+ {"f11", CM_REG_F11},
+ {"f12", CM_REG_F12},
+ {"f13", CM_REG_F13},
+ {"f14", CM_REG_F14},
+ {"f15", CM_REG_F15},
+ {"f16", CM_REG_F16},
+ {"f17", CM_REG_F17},
+ {"f18", CM_REG_F18},
+ {"f19", CM_REG_F19},
+ {"f20", CM_REG_F20},
+ {"f21", CM_REG_F21},
+ {"f22", CM_REG_F22},
+ {"f23", CM_REG_F23},
+ {"f24", CM_REG_F24},
+ {"f25", CM_REG_F25},
+ {"f26", CM_REG_F26},
+ {"f27", CM_REG_F27},
+ {"f28", CM_REG_F28},
+ {"f29", CM_REG_F29},
+ {"f30", CM_REG_F30},
+ {"f31", CM_REG_F31},
+ {"pc", CM_REG_PC},
+ {"fcsr", CM_REG_FCSR},
+ {"mvendorid", CM_REG_MVENDORID},
+ {"marchid", CM_REG_MARCHID},
+ {"mimpid", CM_REG_MIMPID},
+ {"mcycle", CM_REG_MCYCLE},
+ {"icycleinstret", CM_REG_ICYCLEINSTRET},
+ {"mstatus", CM_REG_MSTATUS},
+ {"mtvec", CM_REG_MTVEC},
+ {"mscratch", CM_REG_MSCRATCH},
+ {"mepc", CM_REG_MEPC},
+ {"mcause", CM_REG_MCAUSE},
+ {"mtval", CM_REG_MTVAL},
+ {"misa", CM_REG_MISA},
+ {"mie", CM_REG_MIE},
+ {"mip", CM_REG_MIP},
+ {"medeleg", CM_REG_MEDELEG},
+ {"mideleg", CM_REG_MIDELEG},
+ {"mcounteren", CM_REG_MCOUNTEREN},
+ {"menvcfg", CM_REG_MENVCFG},
+ {"stvec", CM_REG_STVEC},
+ {"sscratch", CM_REG_SSCRATCH},
+ {"sepc", CM_REG_SEPC},
+ {"scause", CM_REG_SCAUSE},
+ {"stval", CM_REG_STVAL},
+ {"satp", CM_REG_SATP},
+ {"scounteren", CM_REG_SCOUNTEREN},
+ {"senvcfg", CM_REG_SENVCFG},
+ {"ilrsc", CM_REG_ILRSC},
+ {"iflags", CM_REG_IFLAGS},
+ {"iflags_prv", CM_REG_IFLAGS_PRV},
+ {"iflags_x", CM_REG_IFLAGS_X},
+ {"iflags_y", CM_REG_IFLAGS_Y},
+ {"iflags_h", CM_REG_IFLAGS_H},
+ {"iunrep", CM_REG_IUNREP},
+ {"clint_mtimecmp", CM_REG_CLINT_MTIMECMP},
+ {"plic_girqpend", CM_REG_PLIC_GIRQPEND},
+ {"plic_girqsrvd", CM_REG_PLIC_GIRQSRVD},
+ {"htif_tohost", CM_REG_HTIF_TOHOST},
+ {"htif_tohost_dev", CM_REG_HTIF_TOHOST_DEV},
+ {"htif_tohost_cmd", CM_REG_HTIF_TOHOST_CMD},
+ {"htif_tohost_reason", CM_REG_HTIF_TOHOST_REASON},
+ {"htif_tohost_data", CM_REG_HTIF_TOHOST_DATA},
+ {"htif_fromhost", CM_REG_HTIF_FROMHOST},
+ {"htif_fromhost_dev", CM_REG_HTIF_FROMHOST_DEV},
+ {"htif_fromhost_cmd", CM_REG_HTIF_FROMHOST_CMD},
+ {"htif_fromhost_reason", CM_REG_HTIF_FROMHOST_REASON},
+ {"htif_fromhost_data", CM_REG_HTIF_FROMHOST_DATA},
+ {"htif_ihalt", CM_REG_HTIF_IHALT},
+ {"htif_iconsole", CM_REG_HTIF_ICONSOLE},
+ {"htif_iyield", CM_REG_HTIF_IYIELD},
+ {"uarch_x0", CM_REG_UARCH_X0},
+ {"uarch_x1", CM_REG_UARCH_X1},
+ {"uarch_x2", CM_REG_UARCH_X2},
+ {"uarch_x3", CM_REG_UARCH_X3},
+ {"uarch_x4", CM_REG_UARCH_X4},
+ {"uarch_x5", CM_REG_UARCH_X5},
+ {"uarch_x6", CM_REG_UARCH_X6},
+ {"uarch_x7", CM_REG_UARCH_X7},
+ {"uarch_x8", CM_REG_UARCH_X8},
+ {"uarch_x9", CM_REG_UARCH_X9},
+ {"uarch_x10", CM_REG_UARCH_X10},
+ {"uarch_x11", CM_REG_UARCH_X11},
+ {"uarch_x12", CM_REG_UARCH_X12},
+ {"uarch_x13", CM_REG_UARCH_X13},
+ {"uarch_x14", CM_REG_UARCH_X14},
+ {"uarch_x15", CM_REG_UARCH_X15},
+ {"uarch_x16", CM_REG_UARCH_X16},
+ {"uarch_x17", CM_REG_UARCH_X17},
+ {"uarch_x18", CM_REG_UARCH_X18},
+ {"uarch_x19", CM_REG_UARCH_X19},
+ {"uarch_x20", CM_REG_UARCH_X20},
+ {"uarch_x21", CM_REG_UARCH_X21},
+ {"uarch_x22", CM_REG_UARCH_X22},
+ {"uarch_x23", CM_REG_UARCH_X23},
+ {"uarch_x24", CM_REG_UARCH_X24},
+ {"uarch_x25", CM_REG_UARCH_X25},
+ {"uarch_x26", CM_REG_UARCH_X26},
+ {"uarch_x27", CM_REG_UARCH_X27},
+ {"uarch_x28", CM_REG_UARCH_X28},
+ {"uarch_x29", CM_REG_UARCH_X29},
+ {"uarch_x30", CM_REG_UARCH_X30},
+ {"uarch_x31", CM_REG_UARCH_X31},
+ {"uarch_pc", CM_REG_UARCH_PC},
+ {"uarch_cycle", CM_REG_UARCH_CYCLE},
+ {"uarch_halt_flag", CM_REG_UARCH_HALT_FLAG},
+ // clang-format on
+ };
+ const char *name = luaL_checkstring(L, idx);
+ auto got = g_cm_proc_reg_name.find(name);
+ if (got == g_cm_proc_reg_name.end()) {
+ luaL_argerror(L, idx, "unknown register");
+ }
+ return got->second;
+} catch (const std::exception &e) {
+ luaL_error(L, "%s", e.what());
+ return CM_REG_UNKNOWN; // will not be reached
+} catch (...) {
+ luaL_error(L, "unknown error with register type conversion");
+ return CM_REG_UNKNOWN; // will not be reached
+}
+
+void clua_check_cm_hash(lua_State *L, int idx, cm_hash *c_hash) {
+ if (lua_isstring(L, idx)) {
+ size_t len = 0;
+ const char *data = lua_tolstring(L, idx, &len);
+ if (len != sizeof(cm_hash)) {
+ luaL_error(L, "hash length must be 32 bytes");
+ }
+ memcpy(c_hash, data, sizeof(cm_hash));
+ } else {
+ luaL_error(L, "hash length must be 32 bytes");
+ }
+}
+
+void clua_push_cm_hash(lua_State *L, const cm_hash *hash) {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ lua_pushlstring(L, reinterpret_cast(hash), CM_HASH_SIZE);
+}
+
+static int64_t clua_get_array_table_len(lua_State *L, int tabidx) {
+ if (!lua_istable(L, tabidx)) {
+ return -1;
+ }
+ int64_t len = 0;
+ lua_pushvalue(L, tabidx); // push table
+ lua_pushnil(L); // push key
+ while (lua_next(L, -2)) { // replace key, push value
+ if (!lua_isinteger(L, -2)) { // non integer key, not an array
+ lua_pop(L, 3);
+ return -1;
+ }
+ const int64_t i = lua_tointeger(L, -2);
+ if (i <= 0) { // invalid index, not an array
+ lua_pop(L, 3);
+ return -1;
+ }
+ len = std::max(i, len);
+ lua_pop(L, 1); // pop value
+ }
+ lua_pop(L, 1); // pop key
+ return len;
+}
+
+static const nlohmann::json &clua_get_json_field_schema(const std::string_view field_name, const nlohmann::json &schema,
+ const nlohmann::json &schema_dict) {
+ static const nlohmann::json empty_schema;
+ if (!schema.contains(field_name)) {
+ return empty_schema;
+ }
+ const auto &type_name = schema.at(field_name).template get();
+ return schema_dict.at(type_name);
+}
+
+static nlohmann::json &clua_push_json_value_ref(lua_State *L, int idx, int ctxidx, const nlohmann::json &schema,
+ const nlohmann::json &schema_dict) {
+ nlohmann::json &j = *clua_push_new_managed_toclose_ptr(L, nlohmann::json(), ctxidx);
+ idx -= idx < 0 ? 1 : 0; // adjust offset after pushing j reference
+ switch (lua_type(L, idx)) {
+ case LUA_TTABLE: {
+ const int64_t len = clua_get_array_table_len(L, idx);
+ if (len >= 0) { // array
+ j = nlohmann::json::array();
+ const auto &field_schema = clua_get_json_field_schema("items", schema, schema_dict);
+ for (int64_t i = 1; i <= len; ++i) {
+ lua_geti(L, idx, i);
+ j.push_back(clua_push_json_value_ref(L, -1, ctxidx, field_schema, schema_dict));
+ lua_pop(L, 2); // pop value, child j reference
+ }
+ } else { // object
+ j = nlohmann::json::object();
+ lua_pushvalue(L, idx); // push table
+ lua_pushnil(L); // push key
+ while (lua_next(L, -2)) { // update key, push value
+ if (!lua_isstring(L, -2)) {
+ luaL_error(L, "table maps cannot contain keys of type %s", lua_typename(L, lua_type(L, -2)));
+ }
+ const char *field_name = lua_tostring(L, -2);
+ const auto &field_schema = clua_get_json_field_schema(field_name, schema, schema_dict);
+ j[field_name] = clua_push_json_value_ref(L, -1, ctxidx, field_schema, schema_dict);
+ lua_pop(L, 2); // pop value, child j reference
+ }
+ lua_pop(L, 1); // pop table
+ }
+ break;
+ }
+ case LUA_TNUMBER: {
+ if (lua_isinteger(L, idx)) {
+ int64_t v = lua_tointeger(L, idx);
+ if (schema.is_string() && schema.template get() == "ArrayIndex") {
+ v -= 1;
+ }
+ j = v;
+ } else { // floating point
+ j = lua_tonumber(L, idx);
+ }
+ break;
+ }
+ case LUA_TSTRING: {
+ size_t len = 0;
+ const char *ptr = lua_tolstring(L, idx, &len);
+ const std::string_view data(ptr, len);
+ if (schema.is_string() && schema.template get() == "Base64") {
+ j = encode_base64(data);
+ } else {
+ j = data;
+ }
+ break;
+ }
+ case LUA_TBOOLEAN:
+ j = static_cast(lua_toboolean(L, idx));
+ break;
+ case LUA_TNIL:
+ j = nullptr;
+ break;
+ default:
+ luaL_error(L, "lua value of type %s cannot be serialized to JSON", lua_typename(L, lua_type(L, idx)));
+ break;
+ }
+ return j;
+}
+
+const char *clua_check_json_string(lua_State *L, int idx, int indent, int ctxidx, const nlohmann::json &schema,
+ const nlohmann::json &schema_dict) {
+ assert(idx > 0);
+ if (!lua_istable(L, idx)) {
+ luaL_error(L, "failed to parse JSON from a Lua value: expected a table but got type \"%s\"",
+ lua_typename(L, lua_type(L, idx)));
+ }
+ try {
+ const nlohmann::json &j = clua_push_json_value_ref(L, idx, ctxidx, schema, schema_dict);
+ std::string &s = *clua_push_new_managed_toclose_ptr(L, j.dump(indent), ctxidx);
+ lua_pushlstring(L, s.data(), s.size());
+ lua_replace(L, idx); // replace the Lua value with its JSON string representation
+ lua_pop(L, 2); // pop s, j references
+ return luaL_checkstring(L, idx); // return the string
+ } catch (std::exception &e) {
+ luaL_error(L, "failed to parse JSON from a Lua table: %s", e.what());
+ return nullptr;
+ }
+}
+
+static void clua_push_json_value(lua_State *L, const nlohmann::json &j, int ctxidx, const nlohmann::json &schema,
+ const nlohmann::json &schema_dict) {
+ switch (j.type()) {
+ case nlohmann::json::value_t::array: {
+ const auto &field_schema = clua_get_json_field_schema("items", schema, schema_dict);
+ lua_createtable(L, static_cast(j.size()), 0);
+ int64_t i = 1;
+ for (auto it = j.begin(); it != j.end(); ++it, ++i) {
+ clua_push_json_value(L, *it, ctxidx, field_schema, schema_dict);
+ lua_rawseti(L, -2, i);
+ }
+ break;
+ }
+ case nlohmann::json::value_t::object: {
+ lua_createtable(L, 0, static_cast(j.size()));
+ for (const auto &el : j.items()) {
+ const auto &field_name = el.key();
+ const auto &field_schema = clua_get_json_field_schema(field_name, schema, schema_dict);
+ clua_push_json_value(L, el.value(), ctxidx, field_schema, schema_dict);
+ lua_setfield(L, -2, field_name.c_str());
+ }
+ break;
+ }
+ case nlohmann::json::value_t::string: {
+ const std::string_view &data = j.template get();
+ if (schema.is_string() && schema.template get() == "Base64") {
+ lua_pushnil(L); // reserve a slot in the stack (needed because of lua_toclose semantics)
+ std::string &binary_data = *clua_push_new_managed_toclose_ptr(L, decode_base64(data), ctxidx);
+ lua_pushlstring(L, binary_data.data(), binary_data.length());
+ lua_replace(L, -3); // move into the placeholder slot
+ lua_pop(L, 1); // pop binary_data reference
+ } else {
+ lua_pushlstring(L, data.data(), data.length());
+ }
+ break;
+ }
+ case nlohmann::json::value_t::number_integer: {
+ int64_t v = j.template get();
+ if (schema.is_string() && schema.template get() == "ArrayIndex") {
+ v += 1;
+ }
+ lua_pushinteger(L, v);
+ break;
+ }
+ case nlohmann::json::value_t::number_unsigned: {
+ int64_t v = static_cast(j.template get());
+ if (schema.is_string() && schema.template get() == "ArrayIndex") {
+ v += 1;
+ }
+ lua_pushinteger(L, v);
+ break;
+ }
+ case nlohmann::json::value_t::number_float:
+ lua_pushnumber(L, j.template get());
+ break;
+ case nlohmann::json::value_t::boolean:
+ lua_pushboolean(L, j.template get());
+ break;
+ case nlohmann::json::value_t::null:
+ lua_pushnil(L);
+ break;
+ default:
+ luaL_error(L, "JSON value of type %s cannot be to Lua", j.type_name());
+ break;
+ }
+}
+
+void clua_push_json_table(lua_State *L, const char *s, int ctxidx, const nlohmann::json &schema,
+ const nlohmann::json &schema_dict) {
+ try {
+ lua_pushnil(L); // reserve a slot in the stack (needed because of lua_toclose semantics)
+ const nlohmann::json &j = *clua_push_new_managed_toclose_ptr(L, nlohmann::json::parse(s), ctxidx);
+ clua_push_json_value(L, j, ctxidx, schema, schema_dict);
+ lua_replace(L, -3); // move into the placeholder slot
+ lua_pop(L, 1); // pop j reference
+ } catch (std::exception &e) {
+ luaL_error(L, "failed to parse JSON from a string: %s", e.what());
+ }
+}
+
+static const nlohmann::json &clua_get_machine_schema_dict(lua_State *L) {
+ static nlohmann::json machine_schema_dict;
+ try {
+ if (machine_schema_dict.is_null()) {
+ // In order to convert Lua tables <-> JSON objects we have to define a schema
+ // to transform some special fields, we only care about:
+ // - Binary strings (translate Base64 strings in JSON to binary strings in Lua)
+ // - Array indexes (translate 0 based index in JSON to 1 based index in Lua)
+ machine_schema_dict = {
+ {"Base64", "Base64"},
+ {"ArrayIndex", "ArrayIndex"},
+ {"Base64Array",
+ {
+ {"items", "Base64"},
+ }},
+ {"Proof",
+ {
+ {"target_hash", "Base64"},
+ {"root_hash", "Base64"},
+ {"sibling_hashes", "Base64Array"},
+ }},
+ {"Access",
+ {
+ {"read", "Base64"},
+ {"read_hash", "Base64"},
+ {"written", "Base64"},
+ {"written_hash", "Base64"},
+ {"sibling_hashes", "Base64Array"},
+ }},
+ {"AccessArray",
+ {
+ {"items", "Access"},
+ }},
+ {"Bracket",
+ {
+ {"where", "ArrayIndex"},
+ }},
+ {"BracketArray",
+ {
+ {"items", "Bracket"},
+ }},
+ {"AccessLog",
+ {
+ {"accesses", "AccessArray"},
+ {"brackets", "BracketArray"},
+ }},
+ };
+ }
+ } catch (std::exception &e) {
+ luaL_error(L, "failed to create machine schema dictionary: %s", e.what());
+ }
+ return machine_schema_dict;
+};
+
+const char *clua_check_schemed_json_string(lua_State *L, int idx, const std::string &schema_name, int ctxidx) {
+ const auto &machine_schema_dict = clua_get_machine_schema_dict(L);
+ const auto it = machine_schema_dict.find(schema_name);
+ if (it == machine_schema_dict.end()) {
+ luaL_error(L, "type \"%s\" is not defined in machine schema dictionary", schema_name.c_str());
+ }
+ return clua_check_json_string(L, idx, -1, ctxidx, *it, machine_schema_dict);
+}
+
+void clua_push_schemed_json_table(lua_State *L, const char *s, const std::string &schema_name, int ctxidx) {
+ const auto &machine_schema_dict = clua_get_machine_schema_dict(L);
+ const auto it = machine_schema_dict.find(schema_name);
+ if (it == machine_schema_dict.end()) {
+ luaL_error(L, "type \"%s\" is not defined in machine schema dictionary", schema_name.c_str());
+ }
+ return clua_push_json_table(L, s, ctxidx, *it, machine_schema_dict);
+}
+
/// \brief This is the machine:get_proof() method implementation.
/// \param L Lua state.
static int machine_obj_index_get_proof(lua_State *L) {
@@ -50,6 +539,29 @@ static int machine_obj_index_get_initial_config(lua_State *L) {
return 1;
}
+/// \brief This is the machine:get_runtime_config() method implementation.
+/// \param L Lua state.
+static int machine_obj_index_get_runtime_config(lua_State *L) {
+ auto &m = clua_check>(L, 1);
+ const char *runtime_config = nullptr;
+ if (cm_get_runtime_config(m.get(), &runtime_config) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ clua_push_json_table(L, runtime_config);
+ return 1;
+}
+
+/// \brief This is the machine:set_runtime_config() method implementation.
+/// \param L Lua state.
+static int machine_obj_index_set_runtime_config(lua_State *L) {
+ auto &m = clua_check>(L, 1);
+ const char *runtime_config = clua_check_json_string(L, 2);
+ if (cm_set_runtime_config(m.get(), runtime_config) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ return 0;
+}
+
/// \brief This is the machine:get_root_hash() method implementation.
/// \param L Lua state.
static int machine_obj_index_get_root_hash(lua_State *L) {
@@ -403,42 +915,11 @@ static int machine_obj_index_replace_memory_range(lua_State *L) {
return 0;
}
-/// \brief This is the machine:snapshot() method implementation.
-/// \param L Lua state.
-static int machine_obj_index_snapshot(lua_State *L) {
- auto &m = clua_check>(L, 1);
- if (cm_snapshot(m.get()) != 0) {
- return luaL_error(L, "%s", cm_get_last_error_message());
- }
- return 0;
-}
-
-/// \brief This is the machine:commit() method implementation.
-/// \param L Lua state.
-static int machine_obj_index_commit(lua_State *L) {
- auto &m = clua_check>(L, 1);
- if (cm_commit(m.get()) != 0) {
- return luaL_error(L, "%s", cm_get_last_error_message());
- }
- return 0;
-}
-
-/// \brief This is the machine:rollback() method implementation.
-/// \param L Lua state.
-static int machine_obj_index_rollback(lua_State *L) {
- auto &m = clua_check>(L, 1);
- if (cm_rollback(m.get()) != 0) {
- return luaL_error(L, "%s", cm_get_last_error_message());
- }
- return 0;
-}
-
/// \brief This is the machine:destroy() method implementation for local machines.
/// \param L Lua state.
static int machine_obj_index_destroy(lua_State *L) {
auto &m = clua_check>(L, 1);
cm_destroy(m.get());
- m.release();
return 0;
}
@@ -501,10 +982,142 @@ static int machine_obj_index_log_send_cmio_response(lua_State *L) {
return 1;
}
+/// \brief This is the machine:is_empty() method implementation.
+/// \param L Lua state.
+static int machine_obj_index_is_empty(lua_State *L) {
+ auto &m = clua_check>(L, 1);
+ bool yes = false;
+ if (cm_is_empty(m.get(), &yes) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ lua_pushboolean(L, yes);
+ return 1;
+}
+
+/// \brief This is the machine:create() method implementation.
+/// \param L Lua state.
+static int machine_obj_index_create(lua_State *L) {
+ lua_settop(L, 3);
+ auto &m = clua_check>(L, 1);
+ const char *runtime_config = !lua_isnil(L, 3) ? clua_check_json_string(L, 3) : nullptr;
+ // Create or load a machine depending on the type of the first argument
+ const char *config = clua_check_json_string(L, 2);
+ if (cm_create(m.get(), config, runtime_config) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ lua_settop(L, 1);
+ return 1;
+}
+
+/// \brief This is the machine:load() method implementation.
+/// \param L Lua state.
+static int machine_obj_index_load(lua_State *L) {
+ lua_settop(L, 3);
+ auto &m = clua_check>(L, 1);
+ const char *runtime_config = !lua_isnil(L, 3) ? clua_check_json_string(L, 3) : nullptr;
+ const char *dir = luaL_checkstring(L, 2);
+ if (cm_load(m.get(), dir, runtime_config) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ lua_settop(L, 1);
+ return 1;
+}
+
+/// \brief This is the machine:get_default_machine_config() method implementation
+/// \param L Lua state.
+static int machine_obj_index_get_default_config(lua_State *L) {
+ auto &m = clua_check>(L, 1);
+ const char *config = nullptr;
+ if (cm_get_default_config(m.get(), &config) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ clua_push_json_table(L, config);
+ return 1;
+}
+
+/// \brief This is the machine:get_reg_address() method implementation.
+/// \param L Lua state.
+static int machine_obj_index_get_reg_address(lua_State *L) {
+ auto &m = clua_check>(L, 1);
+ uint64_t reg_address{};
+ const cm_reg reg = clua_check_cm_proc_reg(L, 2);
+ if (cm_get_reg_address(m.get(), reg, ®_address) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ lua_pushinteger(L, static_cast(reg_address));
+ return 1;
+}
+
+/// \brief This is the machine:verify_step_uarch() method implementation.
+/// \param L Lua state.
+static int machine_obj_index_verify_step_uarch(lua_State *L) {
+ lua_settop(L, 4);
+ auto &m = clua_check>(L, 1);
+ cm_hash root_hash{};
+ clua_check_cm_hash(L, 2, &root_hash);
+ const char *log = clua_check_schemed_json_string(L, 3, "AccessLog");
+ cm_hash target_hash{};
+ clua_check_cm_hash(L, 4, &target_hash);
+ if (cm_verify_step_uarch(m.get(), &root_hash, log, &target_hash) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ return 0;
+}
+
+/// \brief This is the machine:verify_reset_uarch() method implementation.
+/// \param L Lua state.
+static int machine_obj_index_verify_reset_uarch(lua_State *L) {
+ lua_settop(L, 4);
+ auto &m = clua_check>(L, 1);
+ cm_hash root_hash{};
+ clua_check_cm_hash(L, 2, &root_hash);
+ const char *log = clua_check_schemed_json_string(L, 3, "AccessLog");
+ cm_hash target_hash{};
+ clua_check_cm_hash(L, 4, &target_hash);
+ if (cm_verify_reset_uarch(m.get(), &root_hash, log, &target_hash) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ return 0;
+}
+
+/// \brief This is the machine:verify_send_cmio_response() method implementation.
+/// \param L Lua state.
+static int machine_obj_index_verify_send_cmio_response(lua_State *L) {
+ lua_settop(L, 6);
+ auto &m = clua_check>(L, 1);
+ const uint16_t reason = static_cast(luaL_checkinteger(L, 2));
+ size_t length{0};
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ const auto *data = reinterpret_cast(luaL_checklstring(L, 3, &length));
+ cm_hash root_hash{};
+ clua_check_cm_hash(L, 4, &root_hash);
+ const char *log = clua_check_schemed_json_string(L, 5, "AccessLog");
+ cm_hash target_hash{};
+ clua_check_cm_hash(L, 6, &target_hash);
+ if (cm_verify_send_cmio_response(m.get(), reason, data, length, &root_hash, log, &target_hash) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ return 0;
+}
+
+/// \brief This is the machine:swap() method implementation.
+/// \param L Lua state.
+static int machine_obj_index_swap(lua_State *L) {
+ auto &m1 = clua_check>(L, 1);
+ auto &m2 = clua_check>(L, 2);
+ std::swap(m1.get(), m2.get());
+ return 0;
+}
+
/// \brief Contents of the machine object metatable __index table.
static const auto machine_obj_index = cartesi::clua_make_luaL_Reg_array({
+ {"is_empty", machine_obj_index_is_empty},
+ {"create", machine_obj_index_create},
+ {"load", machine_obj_index_load},
{"get_proof", machine_obj_index_get_proof},
{"get_initial_config", machine_obj_index_get_initial_config},
+ {"get_runtime_config", machine_obj_index_get_runtime_config},
+ {"set_runtime_config", machine_obj_index_set_runtime_config},
{"get_root_hash", machine_obj_index_get_root_hash},
{"read_reg", machine_obj_index_read_reg},
{"read_uarch_cycle", machine_obj_index_read_uarch_cycle},
@@ -528,9 +1141,6 @@ static const auto machine_obj_index = cartesi::clua_make_luaL_Reg_array({
{"write_virtual_memory", machine_obj_index_write_virtual_memory},
{"translate_virtual_address", machine_obj_index_translate_virtual_address},
{"replace_memory_range", machine_obj_index_replace_memory_range},
- {"snapshot", machine_obj_index_snapshot},
- {"commit", machine_obj_index_commit},
- {"rollback", machine_obj_index_rollback},
{"destroy", machine_obj_index_destroy},
{"read_uarch_halt_flag", machine_obj_index_read_uarch_halt_flag},
{"set_uarch_halt_flag", machine_obj_index_set_uarch_halt_flag},
@@ -540,18 +1150,64 @@ static const auto machine_obj_index = cartesi::clua_make_luaL_Reg_array({
{"receive_cmio_request", machine_obj_index_receive_cmio_request},
{"send_cmio_response", machine_obj_index_send_cmio_response},
{"log_send_cmio_response", machine_obj_index_log_send_cmio_response},
+ {"get_default_config", machine_obj_index_get_default_config},
+ {"get_reg_address", machine_obj_index_get_reg_address},
+ {"verify_step_uarch", machine_obj_index_verify_step_uarch},
+ {"verify_reset_uarch", machine_obj_index_verify_reset_uarch},
+ {"verify_send_cmio_response", machine_obj_index_verify_send_cmio_response},
+ {"swap", machine_obj_index_swap},
+});
+
+/// \brief This is the class() constructor implementation.
+/// \param L Lua state.
+static int machine_meta_call(lua_State *L) {
+ // This receives the source machine that is being "called" as the first argument,
+ // either a config or a directory as second argument, and an optional runtime config as third argument.
+ lua_settop(L, 3);
+ auto &m = clua_check>(L, 1); // source machine
+ // We could be creating a local machine or a remote machine.
+ // The type is decided by source machine, which will be a cm_machine created with
+ // either cm_new(nullptr, ...) or with cm_jsonrpc_connect_server/spawn_server/fork_server
+ // When we call cm_new(m.get(), ...), it creates a new empty object from the same underlying type as m.get().
+ auto &new_m = clua_push_to(L, clua_managed_cm_ptr(nullptr));
+ if (cm_new(m.get(), &new_m.get()) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ const char *runtime_config = !lua_isnil(L, 3) ? clua_check_json_string(L, 3) : nullptr;
+ // Create or load a machine depending on the type of the first argument
+ if (!lua_isstring(L, 2)) {
+ const char *config = clua_check_json_string(L, 2);
+ if (cm_create(new_m.get(), config, runtime_config) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ } else {
+ const char *dir = luaL_checkstring(L, 2);
+ if (cm_load(new_m.get(), dir, runtime_config) != 0) {
+ return luaL_error(L, "%s", cm_get_last_error_message());
+ }
+ }
+ return 1;
+}
+
+/// \brief Contents of the machine class metatable __index table.
+static const auto machine_meta = cartesi::clua_make_luaL_Reg_array({
+ {"__call", machine_meta_call},
});
int clua_i_virtual_machine_init(lua_State *L, int ctxidx) {
+ clua_createnewtype>(L, ctxidx);
+ clua_createnewtype>(L, ctxidx);
+ clua_createnewtype>(L, ctxidx);
if (!clua_typeexists>(L, ctxidx)) {
clua_createtype>(L, "cartesi machine object", ctxidx);
clua_setmethods>(L, machine_obj_index.data(), 0, ctxidx);
+ clua_setmetamethods>(L, machine_meta.data(), 0, ctxidx);
}
- return 1;
+ return 0;
}
int clua_i_virtual_machine_export(lua_State *L, int ctxidx) {
- clua_i_virtual_machine_init(L, ctxidx); // cartesi
+ clua_i_virtual_machine_init(L, ctxidx);
return 0;
}
diff --git a/src/clua-i-virtual-machine.h b/src/clua-i-virtual-machine.h
index 54d7bd416..ef13788fa 100644
--- a/src/clua-i-virtual-machine.h
+++ b/src/clua-i-virtual-machine.h
@@ -18,21 +18,19 @@
#define CLUA_I_VIRTUAL_MACHINE_H
#include
+#include
+#include
-extern "C" {
-#include
-}
-
+#include "clua.h"
#include "i-virtual-machine.h"
+#include "json-util.h"
+#include "machine-c-api.h"
/// \file
/// \brief Cartesi machine Lua interface
namespace cartesi {
-/// \brief Type used to represent virtual machine objects in Lua
-using clua_i_virtual_machine_ptr = std::unique_ptr;
-
/// \brief Initialize Cartesi machine Lua interface
/// \param L Lua state
/// \param ctxidx Index of Clua context
@@ -43,6 +41,162 @@ int clua_i_virtual_machine_init(lua_State *L, int ctxidx);
/// \param ctxidx Index of Clua context
int clua_i_virtual_machine_export(lua_State *L, int ctxidx);
+/// \brief Create overloaded deleters for C API objects
+template
+void clua_delete(T *ptr);
+
+/// \brief Deleter for C data buffer
+template <>
+void clua_delete(unsigned char *ptr);
+
+/// \brief Deleter for machine
+template <>
+void clua_delete(cm_machine *ptr);
+
+/// \brief Deleter for string
+template <>
+void clua_delete(std::string *ptr);
+
+/// \brief Deleter for JSON
+template <>
+void clua_delete(nlohmann::json *ptr);
+
+// clua_managed_cm_ptr is a smart pointer,
+// however we don't use all its functionally, therefore we exclude it from code coverage.
+// LCOV_EXCL_START
+template
+class clua_managed_cm_ptr final {
+public:
+ clua_managed_cm_ptr() : m_ptr{nullptr} {}
+
+ explicit clua_managed_cm_ptr(T *ptr) : m_ptr{ptr} {}
+
+ explicit clua_managed_cm_ptr(clua_managed_cm_ptr &&other) noexcept : m_ptr{other.m_ptr} {
+ other.m_ptr = nullptr;
+ }
+
+ clua_managed_cm_ptr &operator=(clua_managed_cm_ptr &&other) noexcept {
+ reset();
+ std::swap(m_ptr, other.m_ptr);
+ return *this;
+ };
+
+ ~clua_managed_cm_ptr() {
+ reset();
+ }
+
+ clua_managed_cm_ptr(const clua_managed_cm_ptr &other) = delete;
+ void operator=(const clua_managed_cm_ptr &other) = delete;
+
+ T *operator->() const noexcept {
+ return m_ptr;
+ }
+
+ T &operator*() const {
+ return *m_ptr;
+ }
+
+ void reset(T *ptr = nullptr) {
+ clua_delete(m_ptr); // use overloaded deleter
+ m_ptr = ptr;
+ }
+
+ T *release(void) noexcept {
+ auto *tmp_ptr = m_ptr;
+ m_ptr = nullptr;
+ return tmp_ptr;
+ }
+
+ T *&get(void) noexcept { // return reference to internal ptr
+ return m_ptr;
+ }
+
+ T *get(void) const noexcept {
+ return m_ptr;
+ }
+
+private:
+ T *m_ptr;
+};
+// LCOV_EXCL_STOP
+
+/// \brief Allocates a new type, pushes its reference into the Lua stack and returns its pointer.
+/// \param L Lua state
+/// \param value Initial value
+/// \param ctxidx Index (or pseudo-index) of clua context
+/// \returns The value pointer, valid until its reference is removed from the Lua stack.
+/// \details The value is marked to-be-closed when popped from the Lua stack.
+/// This follow lua_toclose semantics (check Lua 5.4 manual),
+/// therefore the stack index can only be removed via lua_pop (e.g. don't use lua_remove).
+template
+T *clua_push_new_managed_toclose_ptr(lua_State *L, T &&value, int ctxidx = lua_upvalueindex(1)) {
+ auto &managed_value = clua_push_to(L, clua_managed_cm_ptr(new T(std::forward(value))), ctxidx);
+ // ??(edubart): Unfortunately Lua 5.4.4 (default on Ubuntu 22.04) has a bug that causes a crash
+ // when using lua_settop with lua_toclose, it was fixed only in Lua 5.4.5 in
+ // https://github.com/lua/lua/commit/196bb94d66e727e0aec053a0276c3ad701500762 .
+ // Without lua_toclose call, reference will be only collected by the GC (non deterministic).
+#if LUA_VERSION_RELEASE_NUM > 50404
+ lua_toclose(L, -1);
+#endif
+ return managed_value.get();
+}
+
+/// \brief Returns a register selector from Lua
+/// \param L Lua state
+/// \param idx Index in stack
+/// \returns C API register selector. Lua argument error if unknown
+cm_reg clua_check_cm_proc_reg(lua_State *L, int idx);
+
+/// \brief Pushes a C api hash object to the Lua stack
+/// \param L Lua state
+/// \param hash Hash to be pushed
+void clua_push_cm_hash(lua_State *L, const cm_hash *hash);
+
+/// \brief Return C hash from Lua
+/// \param L Lua state
+/// \param idx Index in stack
+/// \param c_hash Receives hash
+void clua_check_cm_hash(lua_State *L, int idx, cm_hash *c_hash);
+
+/// \brief Replaces a Lua table with its JSON string representation and returns the string
+/// \param L Lua state
+/// \param idx Lua table stack index which will be converted to a Lua string
+/// \param indent JSON indentation when converting it to a string
+/// \param ctxidx Index (or pseudo-index) of clua context
+/// \param schema Schema for the table
+/// \param schema_dict Dictionary containing schema for all types
+/// \returns It traverses the Lua value while converting to a JSON object
+/// \details In case the Lua valua is already a string, it just returns it
+const char *clua_check_json_string(lua_State *L, int idx, int indent = -1, int ctxidx = lua_upvalueindex(1),
+ const nlohmann::json &schema = nlohmann::json(), const nlohmann::json &schema_dict = nlohmann::json());
+
+/// \brief Parses a JSON from a string and pushes it as a Lua table
+/// \param L Lua state
+/// \param s JSON string
+/// \param ctxidx Index (or pseudo-index) of clua context
+/// \param schema Schema for the table
+/// \param schema_dict Dictionary containing schema for all types
+/// \returns It traverses the JSON object while converting to a Lua object
+void clua_push_json_table(lua_State *L, const char *s, int ctxidx = lua_upvalueindex(1),
+ const nlohmann::json &schema = nlohmann::json(), const nlohmann::json &schema_dict = nlohmann::json());
+
+/// \brief Replaces a Lua table with its JSON string representation and returns the string (schemed version)
+/// \param L Lua state
+/// \param idx Lua table stack index which will be converted to a Lua string
+/// \param schema_name Schema name to be used while converting the table
+/// \param ctxidx Index (or pseudo-index) of clua context
+const char *clua_check_schemed_json_string(lua_State *L, int idx, const std::string &schema_name,
+ int ctxidx = lua_upvalueindex(1));
+
+/// \brief Parses a JSON from a string and pushes it as a Lua table (schemed version)
+/// \param L Lua state
+/// \param s JSON string
+/// \param idx Lua table stack index which will be converted to a Lua string
+/// \param schema_name Schema name to be used while converting the table
+/// \param ctxidx Index (or pseudo-index) of clua context
+void clua_push_schemed_json_table(lua_State *L, const char *s, const std::string &schema_name,
+ int ctxidx = lua_upvalueindex(1));
+
} // namespace cartesi
#endif
diff --git a/src/clua-jsonrpc-machine.cpp b/src/clua-jsonrpc-machine.cpp
deleted file mode 100644
index db498be20..000000000
--- a/src/clua-jsonrpc-machine.cpp
+++ /dev/null
@@ -1,332 +0,0 @@
-// Copyright Cartesi and individual authors (see AUTHORS)
-// SPDX-License-Identifier: LGPL-3.0-or-later
-//
-// This program is free software: you can redistribute it and/or modify it under
-// the terms of the GNU Lesser General Public License as published by the Free
-// Software Foundation, either version 3 of the License, or (at your option) any
-// later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT ANY
-// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License along
-// with this program (see COPYING). If not, see .
-//
-
-#include
-#include
-#include
-#include
-
-#include "clua-i-virtual-machine.h"
-#include "clua-jsonrpc-machine.h"
-#include "clua-machine-util.h"
-#include "clua.h"
-#include "jsonrpc-machine-c-api.h"
-#include "machine-c-api.h"
-
-namespace cartesi {
-
-/// \brief Deleter for C api jsonrpc connection
-template <>
-void clua_delete(cm_jsonrpc_connection *ptr) {
- cm_jsonrpc_release_connection(ptr);
-}
-
-/// \brief This is the machine.get_default_machine_config()
-/// static method implementation.
-static int jsonrpc_machine_class_get_default_config(lua_State *L) {
- const int conidx = lua_upvalueindex(1);
- const int ctxidx = lua_upvalueindex(2);
- auto &c = clua_check>(L, conidx, ctxidx);
- const char *config = nullptr;
- if (cm_jsonrpc_get_default_config(c.get(), &config) != 0) {
- return luaL_error(L, "%s", cm_get_last_error_message());
- }
- clua_push_json_table(L, config, ctxidx);
- return 1;
-}
-
-/// \brief This is the machine.get_reg_address() method implementation.
-static int jsonrpc_machine_class_get_reg_address(lua_State *L) {
- auto &c = clua_check>(L, lua_upvalueindex(1), lua_upvalueindex(2));
- uint64_t reg_address{};
- const cm_reg reg = clua_check_cm_proc_reg(L, 1);
- if (cm_jsonrpc_get_reg_address(c.get(), reg, ®_address) != 0) {
- return luaL_error(L, "%s", cm_get_last_error_message());
- }
- lua_pushinteger(L, static_cast(reg_address));
- return 1;
-}
-
-/// \brief This is the machine.verify_step_uarch()
-/// static method implementation.
-static int jsonrpc_machine_class_verify_step_uarch(lua_State *L) {
- const int conidx = lua_upvalueindex(1);
- const int ctxidx = lua_upvalueindex(2);
- lua_settop(L, 5);
- auto &c = clua_check>(L, conidx, ctxidx);
- const char *log = clua_check_schemed_json_string(L, 2, "AccessLog", ctxidx);
- cm_hash root_hash{};
- clua_check_cm_hash(L, 1, &root_hash);
- cm_hash target_hash{};
- clua_check_cm_hash(L, 3, &target_hash);
- if (cm_jsonrpc_verify_step_uarch(c.get(), &root_hash, log, &target_hash) != 0) {
- return luaL_error(L, "%s", cm_get_last_error_message());
- }
- return 0;
-}
-
-/// \brief This is the machine.verify_reset_uarch()
-/// static method implementation.
-static int jsonrpc_machine_class_verify_reset_uarch(lua_State *L) {
- const int conidx = lua_upvalueindex(1);
- const int ctxidx = lua_upvalueindex(2);
- lua_settop(L, 5);
- auto &c = clua_check>(L, conidx, ctxidx);
- const char *log = clua_check_schemed_json_string(L, 2, "AccessLog", ctxidx);
- cm_hash root_hash{};
- clua_check_cm_hash(L, 1, &root_hash);
- cm_hash target_hash{};
- clua_check_cm_hash(L, 3, &target_hash);
- if (cm_jsonrpc_verify_reset_uarch(c.get(), &root_hash, log, &target_hash) != 0) {
- return luaL_error(L, "%s", cm_get_last_error_message());
- }
- return 0;
-}
-
-/// \brief This is the machine.verify_send_cmio_response()
-/// static method implementation.
-static int jsonrpc_machine_class_verify_send_cmio_response(lua_State *L) {
- const int conidx = lua_upvalueindex(1);
- const int ctxidx = lua_upvalueindex(2);
- lua_settop(L, 6);
- auto &c = clua_check>(L, conidx, ctxidx);
- const uint16_t reason = static_cast(luaL_checkinteger(L, 1));
- size_t length{0};
- // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- const auto *data = reinterpret_cast(luaL_checklstring(L, 2, &length));
- const char *log = clua_check_schemed_json_string(L, 4, "AccessLog", ctxidx);
- cm_hash root_hash{};
- clua_check_cm_hash(L, 3, &root_hash);
- cm_hash target_hash{};
- clua_check_cm_hash(L, 5, &target_hash);
- if (cm_jsonrpc_verify_send_cmio_response(c.get(), reason, data, length, &root_hash, log, &target_hash) != 0) {
- return luaL_error(L, "%s", cm_get_last_error_message());
- }
- return 0;
-}
-
-/// \brief Contents of the machine class metatable __index table.
-static const auto jsonrpc_machine_static_methods = cartesi::clua_make_luaL_Reg_array({
- {"get_default_config", jsonrpc_machine_class_get_default_config},
- {"get_reg_address", jsonrpc_machine_class_get_reg_address},
- {"verify_step_uarch", jsonrpc_machine_class_verify_step_uarch},
- {"verify_reset_uarch", jsonrpc_machine_class_verify_reset_uarch},
- {"verify_send_cmio_response", jsonrpc_machine_class_verify_send_cmio_response},
-});
-
-/// \brief Prints a JSONRPC machine class
-/// \param L Lua state.
-static int jsonrpc_machine_tostring(lua_State *L) {
- lua_pushliteral(L, "JSONRPC machine class");
- return 1;
-}
-
-/// \brief This is the cartesi.machine() constructor implementation.
-/// \param L Lua state.
-static int jsonrpc_machine_ctor(lua_State *L) {
- const int conidx = lua_upvalueindex(1);
- const int ctxidx = lua_upvalueindex(2);
- lua_settop(L, 4);
- auto &c = clua_check>(L, conidx, ctxidx);
- auto &m = clua_push_to(L, clua_managed_cm_ptr(nullptr), ctxidx);
- const char *runtime_config = !lua_isnil(L, 3) ? clua_check_json_string(L, 3, -1, ctxidx) : nullptr;
- if (!lua_isstring(L, 2)) {
- const char *config = clua_check_json_string(L, 2, -1, ctxidx);
- if (cm_jsonrpc_create_machine(c.get(), lua_toboolean(L, 4), config, runtime_config, &m.get()) != 0) {
- return luaL_error(L, "%s", cm_get_last_error_message());
- }
- } else {
- const char *dir = luaL_checkstring(L, 2);
- if (cm_jsonrpc_load_machine(c.get(), lua_toboolean(L, 4), dir, runtime_config, &m.get()) != 0) {
- return luaL_error(L, "%s", cm_get_last_error_message());
- }
- }
- return 1;
-}
-
-/// \brief Contents of the jsonrpc machine class metatable.
-static const auto jsonrpc_machine_class_meta = cartesi::clua_make_luaL_Reg_array({
- {"__call", jsonrpc_machine_ctor},
- {"__tostring", jsonrpc_machine_tostring},
-});
-
-/// \brief This is the connection.get_machine() static method implementation.
-static int jsonrpc_connection_class_get_machine(lua_State *L) {
- lua_settop(L, 1);
- auto &c = clua_check>(L, lua_upvalueindex(1), lua_upvalueindex(2));
- const int ctxidx = lua_upvalueindex(2);
- auto &m = clua_push_to(L, clua_managed_cm_ptr(nullptr), ctxidx);
- if (cm_jsonrpc_get_machine(c.get(), lua_toboolean(L, 1), &m.get()) != 0) {
- return luaL_error(L, "%s", cm_get_last_error_message());
- }
- return 1;
-}
-
-/// \brief This is the connection.get_server_version() static method implementation.
-static int jsonrpc_connection_class_get_server_version(lua_State *L) {
- const int conidx = lua_upvalueindex(1);
- const int ctxidx = lua_upvalueindex(2);
- auto &c = clua_check>(L, conidx, ctxidx);
- const char *version = nullptr;
- if (cm_jsonrpc_get_server_version(c.get(), &version) != 0) {
- return luaL_error(L, "%s", cm_get_last_error_message());
- }
- clua_push_json_table(L, version, ctxidx);
- return 1;
-}
-
-/// \brief This is the connection.rebind_server() static method implementation.
-static int jsonrpc_connection_class_rebind_server(lua_State *L) {
- auto &c = clua_check>(L, lua_upvalueindex(1), lua_upvalueindex(2));
- const char *address = luaL_checkstring(L, 1);
- const char *new_address = nullptr;
- if (cm_jsonrpc_rebind_server(c.get(), address, &new_address) != 0) {
- return luaL_error(L, "%s", cm_get_last_error_message());
- }
- if (new_address) {
- lua_pushstring(L, new_address);
- } else {
- lua_pushnil(L);
- }
- return 1;
-}
-
-/// \brief This is the connection.fork_server() static method implementation.
-static int jsonrpc_connection_class_fork_server(lua_State *L) {
- auto &c = clua_check>(L, lua_upvalueindex(1), lua_upvalueindex(2));
- const char *address = nullptr;
- int32_t pid = 0;
- if (cm_jsonrpc_fork_server(c.get(), &address, &pid) != 0) {
- return luaL_error(L, "%s", cm_get_last_error_message());
- }
- lua_pushstring(L, address);
- lua_pushinteger(L, pid);
- return 2;
-}
-
-/// \brief This is the connection.shutdown_server() method implementation.
-static int jsonrpc_connection_class_shutdown_server(lua_State *L) {
- auto &c = clua_check>(L, lua_upvalueindex(1), lua_upvalueindex(2));
- if (cm_jsonrpc_shutdown_server(c.get()) != 0) {
- return luaL_error(L, "%s", cm_get_last_error_message());
- }
- c.release();
- return 0;
-}
-
-/// \brief JSONRPC connection static methods
-static const auto jsonrpc_connection_static_methods = cartesi::clua_make_luaL_Reg_array({
- {"get_machine", jsonrpc_connection_class_get_machine},
- {"get_server_version", jsonrpc_connection_class_get_server_version},
- {"fork_server", jsonrpc_connection_class_fork_server},
- {"rebind_server", jsonrpc_connection_class_rebind_server},
- {"shutdown_server", jsonrpc_connection_class_shutdown_server},
-});
-
-/// \brief Takes underlying cm_jsonrpc_connection in top of stack and encapsulates it in its Lua interface
-static void wrap_jsonrpc_connection(lua_State *L) {
- lua_newtable(L); // ccon luacon
- lua_newtable(L); // ccon luacon mtab
- lua_pushvalue(L, -3); // ccon luacon mtab ccon
- lua_pushvalue(L, lua_upvalueindex(1)); // ccon luacon mtab ccon cluactx
- luaL_setfuncs(L, jsonrpc_machine_static_methods.data(), 2); // ccon luacon mtab
- lua_newtable(L); // ccon luacon mtab mmeta
- lua_pushvalue(L, -4); // ccon luacon mtab mmeta ccon
- lua_pushvalue(L, lua_upvalueindex(1)); // ccon luacon mtab mmeta ccon cluactx
- luaL_setfuncs(L, jsonrpc_machine_class_meta.data(), 2); // ccon luacon mtab mmeta
- lua_setmetatable(L, -2); // ccon luacon mtab
- lua_setfield(L, -2, "machine"); // ccon luacon
- lua_pushvalue(L, -2); // ccon luacon ccon
- lua_pushvalue(L, lua_upvalueindex(1)); // ccon luacon ccon cluactx
- luaL_setfuncs(L, jsonrpc_connection_static_methods.data(), 2); // ccon luacon
- lua_insert(L, -2); // luacon ccon
- lua_pop(L, 1); // luacon
-}
-
-/// \brief This is the jsonrpc.connect() method implementation.
-static int mod_connect(lua_State *L) {
- // create and push the underlying cm_jsonrpc_connection
- const char *address = luaL_checkstring(L, 1);
- auto detach_server = lua_toboolean(L, 2);
- auto &c = clua_push_to(L, clua_managed_cm_ptr(nullptr));
- if (cm_jsonrpc_connect(address, detach_server, &c.get()) != 0) {
- return luaL_error(L, "%s", cm_get_last_error_message());
- }
- // wrap it into its Lua interface
- wrap_jsonrpc_connection(L);
- return 1;
-}
-
-/// \brief This is the jsonrpc.connect() method implementation.
-static int mod_spawn_server(lua_State *L) {
- const char *address = luaL_checkstring(L, 1);
- auto detach_server = lua_toboolean(L, 2);
- // create and push the underlying cm_jsonrpc_connection
- auto &c = clua_push_to(L, clua_managed_cm_ptr(nullptr));
- const char *bound_address = nullptr;
- int32_t pid = 0;
- if (cm_jsonrpc_spawn_server(address, detach_server, &c.get(), &bound_address, &pid) != 0) {
- return luaL_error(L, "%s", cm_get_last_error_message());
- }
- // wrap it into its Lua interface
- wrap_jsonrpc_connection(L);
- lua_pushstring(L, bound_address);
- lua_pushinteger(L, pid);
- return 3;
-}
-
-/// \brief Contents of the jsonrpc module.
-static const auto mod = cartesi::clua_make_luaL_Reg_array({
- {"connect", mod_connect},
- {"spawn_server", mod_spawn_server},
-});
-
-int clua_jsonrpc_machine_init(lua_State *L, int ctxidx) {
- clua_createnewtype>(L, ctxidx);
- clua_createnewtype>(L, ctxidx);
- clua_createnewtype>(L, ctxidx);
- clua_createnewtype>(L, ctxidx);
- return 1;
-}
-
-/// \brief This is the machine:destroy() method implementation for remote machines.
-/// \param L Lua state.
-static int jsonrpc_machine_obj_index_destroy(lua_State *L) {
- auto &m = clua_check>(L, 1);
- if (cm_jsonrpc_destroy_machine(m.get()) != 0) {
- return luaL_error(L, "%s", cm_get_last_error_message());
- }
- m.release();
- return 0;
-}
-
-/// \brief Contents of the machine object metatable __index table.
-static const auto jsonrpc_machine_obj_index = cartesi::clua_make_luaL_Reg_array({
- {"destroy", jsonrpc_machine_obj_index_destroy},
-});
-
-int clua_jsonrpc_machine_export(lua_State *L, int ctxidx) {
- const int ctxabsidx = lua_absindex(L, ctxidx);
- // jsonrpc
- clua_i_virtual_machine_export(L, ctxabsidx); // jsonrpc
- clua_setmethods>(L, jsonrpc_machine_obj_index.data(), 0, ctxabsidx);
- clua_jsonrpc_machine_init(L, ctxabsidx); // jsonrpc
- lua_pushvalue(L, ctxabsidx); // jsonrpc cluactx
- luaL_setfuncs(L, mod.data(), 1); // jsonrpc
- return 0;
-}
-
-} // namespace cartesi
diff --git a/src/clua-jsonrpc-machine.h b/src/clua-jsonrpc-machine.h
deleted file mode 100644
index 5bcd5772f..000000000
--- a/src/clua-jsonrpc-machine.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright Cartesi and individual authors (see AUTHORS)
-// SPDX-License-Identifier: LGPL-3.0-or-later
-//
-// This program is free software: you can redistribute it and/or modify it under
-// the terms of the GNU Lesser General Public License as published by the Free
-// Software Foundation, either version 3 of the License, or (at your option) any
-// later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT ANY
-// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License along
-// with this program (see COPYING). If not, see .
-//
-
-#ifndef CLUA_JSONRPC_MACHINE_H
-#define CLUA_JSONRPC_MACHINE_H
-
-extern "C" {
-#include
-}
-
-/// \file
-/// \brief Remote Cartesi machine Lua interface
-
-namespace cartesi {
-
-/// \brief Initialize remote Cartesi machine Lua interface
-/// \param L Lua state
-/// \param ctxidx Index of clua context
-int clua_jsonrpc_machine_init(lua_State *L, int ctxidx);
-
-/// \brief Exports symbols to table on top of Lua stack
-/// \param L Lua state
-/// \param ctxidx Index of clua context
-int clua_jsonrpc_machine_export(lua_State *L, int ctxidx);
-
-} // namespace cartesi
-
-#endif
diff --git a/src/clua-machine-util.cpp b/src/clua-machine-util.cpp
deleted file mode 100644
index e5fc92b78..000000000
--- a/src/clua-machine-util.cpp
+++ /dev/null
@@ -1,514 +0,0 @@
-// Copyright Cartesi and individual authors (see AUTHORS)
-// SPDX-License-Identifier: LGPL-3.0-or-later
-//
-// This program is free software: you can redistribute it and/or modify it under
-// the terms of the GNU Lesser General Public License as published by the Free
-// Software Foundation, either version 3 of the License, or (at your option) any
-// later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT ANY
-// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License along
-// with this program (see COPYING). If not, see .
-//
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "base64.h"
-#include "clua-machine-util.h"
-#include "machine-c-api.h"
-
-namespace cartesi {
-
-template <>
-void clua_delete(unsigned char *ptr) { // NOLINT(readability-non-const-parameter)
- delete[] ptr;
-}
-
-template <>
-void clua_delete(cm_machine *ptr) {
- cm_destroy(ptr); // this call should never fail
-}
-
-template <>
-void clua_delete(std::string *ptr) {
- delete ptr;
-}
-
-template <>
-void clua_delete(nlohmann::json *ptr) {
- delete ptr;
-}
-
-cm_reg clua_check_cm_proc_reg(lua_State *L, int idx) try {
- /// \brief Mapping between register names and C API constants
- const static std::unordered_map g_cm_proc_reg_name = {
- // clang-format off
- {"x0", CM_REG_X0},
- {"x1", CM_REG_X1},
- {"x2", CM_REG_X2},
- {"x3", CM_REG_X3},
- {"x4", CM_REG_X4},
- {"x5", CM_REG_X5},
- {"x6", CM_REG_X6},
- {"x7", CM_REG_X7},
- {"x8", CM_REG_X8},
- {"x9", CM_REG_X9},
- {"x10", CM_REG_X10},
- {"x11", CM_REG_X11},
- {"x12", CM_REG_X12},
- {"x13", CM_REG_X13},
- {"x14", CM_REG_X14},
- {"x15", CM_REG_X15},
- {"x16", CM_REG_X16},
- {"x17", CM_REG_X17},
- {"x18", CM_REG_X18},
- {"x19", CM_REG_X19},
- {"x20", CM_REG_X20},
- {"x21", CM_REG_X21},
- {"x22", CM_REG_X22},
- {"x23", CM_REG_X23},
- {"x24", CM_REG_X24},
- {"x25", CM_REG_X25},
- {"x26", CM_REG_X26},
- {"x27", CM_REG_X27},
- {"x28", CM_REG_X28},
- {"x29", CM_REG_X29},
- {"x30", CM_REG_X30},
- {"x31", CM_REG_X31},
- {"f0", CM_REG_F0},
- {"f1", CM_REG_F1},
- {"f2", CM_REG_F2},
- {"f3", CM_REG_F3},
- {"f4", CM_REG_F4},
- {"f5", CM_REG_F5},
- {"f6", CM_REG_F6},
- {"f7", CM_REG_F7},
- {"f8", CM_REG_F8},
- {"f9", CM_REG_F9},
- {"f10", CM_REG_F10},
- {"f11", CM_REG_F11},
- {"f12", CM_REG_F12},
- {"f13", CM_REG_F13},
- {"f14", CM_REG_F14},
- {"f15", CM_REG_F15},
- {"f16", CM_REG_F16},
- {"f17", CM_REG_F17},
- {"f18", CM_REG_F18},
- {"f19", CM_REG_F19},
- {"f20", CM_REG_F20},
- {"f21", CM_REG_F21},
- {"f22", CM_REG_F22},
- {"f23", CM_REG_F23},
- {"f24", CM_REG_F24},
- {"f25", CM_REG_F25},
- {"f26", CM_REG_F26},
- {"f27", CM_REG_F27},
- {"f28", CM_REG_F28},
- {"f29", CM_REG_F29},
- {"f30", CM_REG_F30},
- {"f31", CM_REG_F31},
- {"pc", CM_REG_PC},
- {"fcsr", CM_REG_FCSR},
- {"mvendorid", CM_REG_MVENDORID},
- {"marchid", CM_REG_MARCHID},
- {"mimpid", CM_REG_MIMPID},
- {"mcycle", CM_REG_MCYCLE},
- {"icycleinstret", CM_REG_ICYCLEINSTRET},
- {"mstatus", CM_REG_MSTATUS},
- {"mtvec", CM_REG_MTVEC},
- {"mscratch", CM_REG_MSCRATCH},
- {"mepc", CM_REG_MEPC},
- {"mcause", CM_REG_MCAUSE},
- {"mtval", CM_REG_MTVAL},
- {"misa", CM_REG_MISA},
- {"mie", CM_REG_MIE},
- {"mip", CM_REG_MIP},
- {"medeleg", CM_REG_MEDELEG},
- {"mideleg", CM_REG_MIDELEG},
- {"mcounteren", CM_REG_MCOUNTEREN},
- {"menvcfg", CM_REG_MENVCFG},
- {"stvec", CM_REG_STVEC},
- {"sscratch", CM_REG_SSCRATCH},
- {"sepc", CM_REG_SEPC},
- {"scause", CM_REG_SCAUSE},
- {"stval", CM_REG_STVAL},
- {"satp", CM_REG_SATP},
- {"scounteren", CM_REG_SCOUNTEREN},
- {"senvcfg", CM_REG_SENVCFG},
- {"ilrsc", CM_REG_ILRSC},
- {"iflags", CM_REG_IFLAGS},
- {"iflags_prv", CM_REG_IFLAGS_PRV},
- {"iflags_x", CM_REG_IFLAGS_X},
- {"iflags_y", CM_REG_IFLAGS_Y},
- {"iflags_h", CM_REG_IFLAGS_H},
- {"iunrep", CM_REG_IUNREP},
- {"clint_mtimecmp", CM_REG_CLINT_MTIMECMP},
- {"plic_girqpend", CM_REG_PLIC_GIRQPEND},
- {"plic_girqsrvd", CM_REG_PLIC_GIRQSRVD},
- {"htif_tohost", CM_REG_HTIF_TOHOST},
- {"htif_tohost_dev", CM_REG_HTIF_TOHOST_DEV},
- {"htif_tohost_cmd", CM_REG_HTIF_TOHOST_CMD},
- {"htif_tohost_reason", CM_REG_HTIF_TOHOST_REASON},
- {"htif_tohost_data", CM_REG_HTIF_TOHOST_DATA},
- {"htif_fromhost", CM_REG_HTIF_FROMHOST},
- {"htif_fromhost_dev", CM_REG_HTIF_FROMHOST_DEV},
- {"htif_fromhost_cmd", CM_REG_HTIF_FROMHOST_CMD},
- {"htif_fromhost_reason", CM_REG_HTIF_FROMHOST_REASON},
- {"htif_fromhost_data", CM_REG_HTIF_FROMHOST_DATA},
- {"htif_ihalt", CM_REG_HTIF_IHALT},
- {"htif_iconsole", CM_REG_HTIF_ICONSOLE},
- {"htif_iyield", CM_REG_HTIF_IYIELD},
- {"uarch_x0", CM_REG_UARCH_X0},
- {"uarch_x1", CM_REG_UARCH_X1},
- {"uarch_x2", CM_REG_UARCH_X2},
- {"uarch_x3", CM_REG_UARCH_X3},
- {"uarch_x4", CM_REG_UARCH_X4},
- {"uarch_x5", CM_REG_UARCH_X5},
- {"uarch_x6", CM_REG_UARCH_X6},
- {"uarch_x7", CM_REG_UARCH_X7},
- {"uarch_x8", CM_REG_UARCH_X8},
- {"uarch_x9", CM_REG_UARCH_X9},
- {"uarch_x10", CM_REG_UARCH_X10},
- {"uarch_x11", CM_REG_UARCH_X11},
- {"uarch_x12", CM_REG_UARCH_X12},
- {"uarch_x13", CM_REG_UARCH_X13},
- {"uarch_x14", CM_REG_UARCH_X14},
- {"uarch_x15", CM_REG_UARCH_X15},
- {"uarch_x16", CM_REG_UARCH_X16},
- {"uarch_x17", CM_REG_UARCH_X17},
- {"uarch_x18", CM_REG_UARCH_X18},
- {"uarch_x19", CM_REG_UARCH_X19},
- {"uarch_x20", CM_REG_UARCH_X20},
- {"uarch_x21", CM_REG_UARCH_X21},
- {"uarch_x22", CM_REG_UARCH_X22},
- {"uarch_x23", CM_REG_UARCH_X23},
- {"uarch_x24", CM_REG_UARCH_X24},
- {"uarch_x25", CM_REG_UARCH_X25},
- {"uarch_x26", CM_REG_UARCH_X26},
- {"uarch_x27", CM_REG_UARCH_X27},
- {"uarch_x28", CM_REG_UARCH_X28},
- {"uarch_x29", CM_REG_UARCH_X29},
- {"uarch_x30", CM_REG_UARCH_X30},
- {"uarch_x31", CM_REG_UARCH_X31},
- {"uarch_pc", CM_REG_UARCH_PC},
- {"uarch_cycle", CM_REG_UARCH_CYCLE},
- {"uarch_halt_flag", CM_REG_UARCH_HALT_FLAG},
- // clang-format on
- };
- const char *name = luaL_checkstring(L, idx);
- auto got = g_cm_proc_reg_name.find(name);
- if (got == g_cm_proc_reg_name.end()) {
- luaL_argerror(L, idx, "unknown register");
- }
- return got->second;
-} catch (const std::exception &e) {
- luaL_error(L, "%s", e.what());
- return CM_REG_UNKNOWN; // will not be reached
-} catch (...) {
- luaL_error(L, "unknown error with register type conversion");
- return CM_REG_UNKNOWN; // will not be reached
-}
-
-void clua_check_cm_hash(lua_State *L, int idx, cm_hash *c_hash) {
- if (lua_isstring(L, idx)) {
- size_t len = 0;
- const char *data = lua_tolstring(L, idx, &len);
- if (len != sizeof(cm_hash)) {
- luaL_error(L, "hash length must be 32 bytes");
- }
- memcpy(c_hash, data, sizeof(cm_hash));
- } else {
- luaL_error(L, "hash length must be 32 bytes");
- }
-}
-
-void clua_push_cm_hash(lua_State *L, const cm_hash *hash) {
- // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- lua_pushlstring(L, reinterpret_cast(hash), CM_HASH_SIZE);
-}
-
-static int64_t clua_get_array_table_len(lua_State *L, int tabidx) {
- if (!lua_istable(L, tabidx)) {
- return -1;
- }
- int64_t len = 0;
- lua_pushvalue(L, tabidx); // push table
- lua_pushnil(L); // push key
- while (lua_next(L, -2)) { // replace key, push value
- if (!lua_isinteger(L, -2)) { // non integer key, not an array
- lua_pop(L, 3);
- return -1;
- }
- const int64_t i = lua_tointeger(L, -2);
- if (i <= 0) { // invalid index, not an array
- lua_pop(L, 3);
- return -1;
- }
- len = std::max(i, len);
- lua_pop(L, 1); // pop value
- }
- lua_pop(L, 1); // pop key
- return len;
-}
-
-static const nlohmann::json &clua_get_json_field_schema(const std::string_view field_name, const nlohmann::json &schema,
- const nlohmann::json &schema_dict) {
- static const nlohmann::json empty_schema;
- if (!schema.contains(field_name)) {
- return empty_schema;
- }
- const auto &type_name = schema.at(field_name).template get();
- return schema_dict.at(type_name);
-}
-
-static nlohmann::json &clua_push_json_value_ref(lua_State *L, int idx, int ctxidx, const nlohmann::json &schema,
- const nlohmann::json &schema_dict) {
- nlohmann::json &j = *clua_push_new_managed_toclose_ptr(L, nlohmann::json(), ctxidx);
- idx -= idx < 0 ? 1 : 0; // adjust offset after pushing j reference
- switch (lua_type(L, idx)) {
- case LUA_TTABLE: {
- const int64_t len = clua_get_array_table_len(L, idx);
- if (len >= 0) { // array
- j = nlohmann::json::array();
- const auto &field_schema = clua_get_json_field_schema("items", schema, schema_dict);
- for (int64_t i = 1; i <= len; ++i) {
- lua_geti(L, idx, i);
- j.push_back(clua_push_json_value_ref(L, -1, ctxidx, field_schema, schema_dict));
- lua_pop(L, 2); // pop value, child j reference
- }
- } else { // object
- j = nlohmann::json::object();
- lua_pushvalue(L, idx); // push table
- lua_pushnil(L); // push key
- while (lua_next(L, -2)) { // update key, push value
- if (!lua_isstring(L, -2)) {
- luaL_error(L, "table maps cannot contain keys of type %s", lua_typename(L, lua_type(L, -2)));
- }
- const char *field_name = lua_tostring(L, -2);
- const auto &field_schema = clua_get_json_field_schema(field_name, schema, schema_dict);
- j[field_name] = clua_push_json_value_ref(L, -1, ctxidx, field_schema, schema_dict);
- lua_pop(L, 2); // pop value, child j reference
- }
- lua_pop(L, 1); // pop table
- }
- break;
- }
- case LUA_TNUMBER: {
- if (lua_isinteger(L, idx)) {
- int64_t v = lua_tointeger(L, idx);
- if (schema.is_string() && schema.template get() == "ArrayIndex") {
- v -= 1;
- }
- j = v;
- } else { // floating point
- j = lua_tonumber(L, idx);
- }
- break;
- }
- case LUA_TSTRING: {
- size_t len = 0;
- const char *ptr = lua_tolstring(L, idx, &len);
- const std::string_view data(ptr, len);
- if (schema.is_string() && schema.template get() == "Base64") {
- j = encode_base64(data);
- } else {
- j = data;
- }
- break;
- }
- case LUA_TBOOLEAN:
- j = static_cast(lua_toboolean(L, idx));
- break;
- case LUA_TNIL:
- j = nullptr;
- break;
- default:
- luaL_error(L, "lua value of type %s cannot be serialized to JSON", lua_typename(L, lua_type(L, idx)));
- break;
- }
- return j;
-}
-
-const char *clua_check_json_string(lua_State *L, int idx, int indent, int ctxidx, const nlohmann::json &schema,
- const nlohmann::json &schema_dict) {
- assert(idx > 0);
- if (!lua_istable(L, idx)) {
- luaL_error(L, "failed to parse JSON from a Lua value: expected a table but got type \"%s\"",
- lua_typename(L, lua_type(L, idx)));
- }
- try {
- const nlohmann::json &j = clua_push_json_value_ref(L, idx, ctxidx, schema, schema_dict);
- std::string &s = *clua_push_new_managed_toclose_ptr(L, j.dump(indent), ctxidx);
- lua_pushlstring(L, s.data(), s.size());
- lua_replace(L, idx); // replace the Lua value with its JSON string representation
- lua_pop(L, 2); // pop s, j references
- return luaL_checkstring(L, idx); // return the string
- } catch (std::exception &e) {
- luaL_error(L, "failed to parse JSON from a Lua table: %s", e.what());
- return nullptr;
- }
-}
-
-static void clua_push_json_value(lua_State *L, const nlohmann::json &j, int ctxidx, const nlohmann::json &schema,
- const nlohmann::json &schema_dict) {
- switch (j.type()) {
- case nlohmann::json::value_t::array: {
- const auto &field_schema = clua_get_json_field_schema("items", schema, schema_dict);
- lua_createtable(L, static_cast(j.size()), 0);
- int64_t i = 1;
- for (auto it = j.begin(); it != j.end(); ++it, ++i) {
- clua_push_json_value(L, *it, ctxidx, field_schema, schema_dict);
- lua_rawseti(L, -2, i);
- }
- break;
- }
- case nlohmann::json::value_t::object: {
- lua_createtable(L, 0, static_cast(j.size()));
- for (const auto &el : j.items()) {
- const auto &field_name = el.key();
- const auto &field_schema = clua_get_json_field_schema(field_name, schema, schema_dict);
- clua_push_json_value(L, el.value(), ctxidx, field_schema, schema_dict);
- lua_setfield(L, -2, field_name.c_str());
- }
- break;
- }
- case nlohmann::json::value_t::string: {
- const std::string_view &data = j.template get();
- if (schema.is_string() && schema.template get() == "Base64") {
- lua_pushnil(L); // reserve a slot in the stack (needed because of lua_toclose semantics)
- std::string &binary_data = *clua_push_new_managed_toclose_ptr(L, decode_base64(data), ctxidx);
- lua_pushlstring(L, binary_data.data(), binary_data.length());
- lua_replace(L, -3); // move into the placeholder slot
- lua_pop(L, 1); // pop binary_data reference
- } else {
- lua_pushlstring(L, data.data(), data.length());
- }
- break;
- }
- case nlohmann::json::value_t::number_integer: {
- int64_t v = j.template get();
- if (schema.is_string() && schema.template get() == "ArrayIndex") {
- v += 1;
- }
- lua_pushinteger(L, v);
- break;
- }
- case nlohmann::json::value_t::number_unsigned: {
- int64_t v = static_cast(j.template get());
- if (schema.is_string() && schema.template get() == "ArrayIndex") {
- v += 1;
- }
- lua_pushinteger(L, v);
- break;
- }
- case nlohmann::json::value_t::number_float:
- lua_pushnumber(L, j.template get());
- break;
- case nlohmann::json::value_t::boolean:
- lua_pushboolean(L, j.template get());
- break;
- case nlohmann::json::value_t::null:
- lua_pushnil(L);
- break;
- default:
- luaL_error(L, "JSON value of type %s cannot be to Lua", j.type_name());
- break;
- }
-}
-
-void clua_push_json_table(lua_State *L, const char *s, int ctxidx, const nlohmann::json &schema,
- const nlohmann::json &schema_dict) {
- try {
- lua_pushnil(L); // reserve a slot in the stack (needed because of lua_toclose semantics)
- const nlohmann::json &j = *clua_push_new_managed_toclose_ptr(L, nlohmann::json::parse(s), ctxidx);
- clua_push_json_value(L, j, ctxidx, schema, schema_dict);
- lua_replace(L, -3); // move into the placeholder slot
- lua_pop(L, 1); // pop j reference
- } catch (std::exception &e) {
- luaL_error(L, "failed to parse JSON from a string: %s", e.what());
- }
-}
-
-static const nlohmann::json &clua_get_machine_schema_dict(lua_State *L) {
- static nlohmann::json machine_schema_dict;
- try {
- if (machine_schema_dict.is_null()) {
- // In order to convert Lua tables <-> JSON objects we have to define a schema
- // to transform some special fields, we only care about:
- // - Binary strings (translate Base64 strings in JSON to binary strings in Lua)
- // - Array indexes (translate 0 based index in JSON to 1 based index in Lua)
- machine_schema_dict = {
- {"Base64", "Base64"},
- {"ArrayIndex", "ArrayIndex"},
- {"Base64Array",
- {
- {"items", "Base64"},
- }},
- {"Proof",
- {
- {"target_hash", "Base64"},
- {"root_hash", "Base64"},
- {"sibling_hashes", "Base64Array"},
- }},
- {"Access",
- {
- {"read", "Base64"},
- {"read_hash", "Base64"},
- {"written", "Base64"},
- {"written_hash", "Base64"},
- {"sibling_hashes", "Base64Array"},
- }},
- {"AccessArray",
- {
- {"items", "Access"},
- }},
- {"Bracket",
- {
- {"where", "ArrayIndex"},
- }},
- {"BracketArray",
- {
- {"items", "Bracket"},
- }},
- {"AccessLog",
- {
- {"accesses", "AccessArray"},
- {"brackets", "BracketArray"},
- }},
- };
- }
- } catch (std::exception &e) {
- luaL_error(L, "failed to create machine schema dictionary: %s", e.what());
- }
- return machine_schema_dict;
-};
-
-const char *clua_check_schemed_json_string(lua_State *L, int idx, const std::string &schema_name, int ctxidx) {
- const auto &machine_schema_dict = clua_get_machine_schema_dict(L);
- const auto it = machine_schema_dict.find(schema_name);
- if (it == machine_schema_dict.end()) {
- luaL_error(L, "type \"%s\" is not defined in machine schema dictionary", schema_name.c_str());
- }
- return clua_check_json_string(L, idx, -1, ctxidx, *it, machine_schema_dict);
-}
-
-void clua_push_schemed_json_table(lua_State *L, const char *s, const std::string &schema_name, int ctxidx) {
- const auto &machine_schema_dict = clua_get_machine_schema_dict(L);
- const auto it = machine_schema_dict.find(schema_name);
- if (it == machine_schema_dict.end()) {
- luaL_error(L, "type \"%s\" is not defined in machine schema dictionary", schema_name.c_str());
- }
- return clua_push_json_table(L, s, ctxidx, *it, machine_schema_dict);
-}
-
-} // namespace cartesi
diff --git a/src/clua-machine-util.h b/src/clua-machine-util.h
deleted file mode 100644
index 81201201a..000000000
--- a/src/clua-machine-util.h
+++ /dev/null
@@ -1,194 +0,0 @@
-// Copyright Cartesi and individual authors (see AUTHORS)
-// SPDX-License-Identifier: LGPL-3.0-or-later
-//
-// This program is free software: you can redistribute it and/or modify it under
-// the terms of the GNU Lesser General Public License as published by the Free
-// Software Foundation, either version 3 of the License, or (at your option) any
-// later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT ANY
-// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License along
-// with this program (see COPYING). If not, see .
-//
-
-#ifndef CLUA_MACHINE_UTIL_H
-#define CLUA_MACHINE_UTIL_H
-
-#include
-#include
-
-extern "C" {
-#include
-}
-
-#include "clua.h"
-#include "json-util.h"
-#include "machine-c-api.h"
-
-/// \file
-/// \brief Cartesi machine Lua interface helper functions
-
-namespace cartesi {
-
-/// \brief Create overloaded deleters for C API objects
-template
-void clua_delete(T *ptr);
-
-/// \brief Deleter for C data buffer
-template <>
-void clua_delete(unsigned char *ptr);
-
-/// \brief Deleter for machine
-template <>
-void clua_delete(cm_machine *ptr);
-
-/// \brief Deleter for string
-template <>
-void clua_delete(std::string *ptr);
-
-/// \brief Deleter for JSON
-template <>
-void clua_delete(nlohmann::json *ptr);
-
-// clua_managed_cm_ptr is a smart pointer,
-// however we don't use all its functionally, therefore we exclude it from code coverage.
-// LCOV_EXCL_START
-template
-class clua_managed_cm_ptr final {
-public:
- clua_managed_cm_ptr() : m_ptr{nullptr} {}
-
- explicit clua_managed_cm_ptr(T *ptr) : m_ptr{ptr} {}
-
- explicit clua_managed_cm_ptr(clua_managed_cm_ptr &&other) noexcept : m_ptr{other.m_ptr} {
- other.m_ptr = nullptr;
- }
-
- clua_managed_cm_ptr &operator=(clua_managed_cm_ptr &&other) noexcept {
- reset();
- std::swap(m_ptr, other.m_ptr);
- return *this;
- };
-
- ~clua_managed_cm_ptr() {
- reset();
- }
-
- clua_managed_cm_ptr(const clua_managed_cm_ptr &other) = delete;
- void operator=(const clua_managed_cm_ptr &other) = delete;
-
- T *operator->() const noexcept {
- return m_ptr;
- }
-
- T &operator*() const {
- return *m_ptr;
- }
-
- void reset(T *ptr = nullptr) {
- clua_delete(m_ptr); // use overloaded deleter
- m_ptr = ptr;
- }
-
- T *release(void) noexcept {
- auto *tmp_ptr = m_ptr;
- m_ptr = nullptr;
- return tmp_ptr;
- }
-
- T *&get(void) noexcept { // return reference to internal ptr
- return m_ptr;
- }
-
- T *get(void) const noexcept {
- return m_ptr;
- }
-
-private:
- T *m_ptr;
-};
-// LCOV_EXCL_STOP
-
-/// \brief Allocates a new type, pushes its reference into the Lua stack and returns its pointer.
-/// \param L Lua state
-/// \param value Initial value
-/// \param ctxidx Index (or pseudo-index) of clua context
-/// \returns The value pointer, valid until its reference is removed from the Lua stack.
-/// \details The value is marked to-be-closed when popped from the Lua stack.
-/// This follow lua_toclose semantics (check Lua 5.4 manual),
-/// therefore the stack index can only be removed via lua_pop (e.g. don't use lua_remove).
-template
-T *clua_push_new_managed_toclose_ptr(lua_State *L, T &&value, int ctxidx = lua_upvalueindex(1)) {
- auto &managed_value = clua_push_to(L, clua_managed_cm_ptr(new T(std::forward(value))), ctxidx);
- // ??(edubart): Unfortunately Lua 5.4.4 (default on Ubuntu 22.04) has a bug that causes a crash
- // when using lua_settop with lua_toclose, it was fixed only in Lua 5.4.5 in
- // https://github.com/lua/lua/commit/196bb94d66e727e0aec053a0276c3ad701500762 .
- // Without lua_toclose call, reference will be only collected by the GC (non deterministic).
-#if LUA_VERSION_RELEASE_NUM > 50404
- lua_toclose(L, -1);
-#endif
- return managed_value.get();
-}
-
-/// \brief Returns a register selector from Lua
-/// \param L Lua state
-/// \param idx Index in stack
-/// \returns C API register selector. Lua argument error if unknown
-cm_reg clua_check_cm_proc_reg(lua_State *L, int idx);
-
-/// \brief Pushes a C api hash object to the Lua stack
-/// \param L Lua state
-/// \param hash Hash to be pushed
-void clua_push_cm_hash(lua_State *L, const cm_hash *hash);
-
-/// \brief Return C hash from Lua
-/// \param L Lua state
-/// \param idx Index in stack
-/// \param c_hash Receives hash
-void clua_check_cm_hash(lua_State *L, int idx, cm_hash *c_hash);
-
-/// \brief Replaces a Lua table with its JSON string representation and returns the string
-/// \param L Lua state
-/// \param idx Lua table stack index which will be converted to a Lua string
-/// \param indent JSON indentation when converting it to a string
-/// \param ctxidx Index (or pseudo-index) of clua context
-/// \param schema Schema for the table
-/// \param schema_dict Dictionary containing schema for all types
-/// \returns It traverses the Lua value while converting to a JSON object
-/// \details In case the Lua valua is already a string, it just returns it
-const char *clua_check_json_string(lua_State *L, int idx, int indent = -1, int ctxidx = lua_upvalueindex(1),
- const nlohmann::json &schema = nlohmann::json(), const nlohmann::json &schema_dict = nlohmann::json());
-
-/// \brief Parses a JSON from a string and pushes it as a Lua table
-/// \param L Lua state
-/// \param s JSON string
-/// \param ctxidx Index (or pseudo-index) of clua context
-/// \param schema Schema for the table
-/// \param schema_dict Dictionary containing schema for all types
-/// \returns It traverses the JSON object while converting to a Lua object
-void clua_push_json_table(lua_State *L, const char *s, int ctxidx = lua_upvalueindex(1),
- const nlohmann::json &schema = nlohmann::json(), const nlohmann::json &schema_dict = nlohmann::json());
-
-/// \brief Replaces a Lua table with its JSON string representation and returns the string (schemed version)
-/// \param L Lua state
-/// \param idx Lua table stack index which will be converted to a Lua string
-/// \param schema_name Schema name to be used while converting the table
-/// \param ctxidx Index (or pseudo-index) of clua context
-const char *clua_check_schemed_json_string(lua_State *L, int idx, const std::string &schema_name,
- int ctxidx = lua_upvalueindex(1));
-
-/// \brief Parses a JSON from a string and pushes it as a Lua table (schemed version)
-/// \param L Lua state
-/// \param s JSON string
-/// \param idx Lua table stack index which will be converted to a Lua string
-/// \param schema_name Schema name to be used while converting the table
-/// \param ctxidx Index (or pseudo-index) of clua context
-void clua_push_schemed_json_table(lua_State *L, const char *s, const std::string &schema_name,
- int ctxidx = lua_upvalueindex(1));
-
-} // namespace cartesi
-
-#endif
diff --git a/src/clua-machine.cpp b/src/clua-machine.cpp
deleted file mode 100644
index 2b1b869b7..000000000
--- a/src/clua-machine.cpp
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright Cartesi and individual authors (see AUTHORS)
-// SPDX-License-Identifier: LGPL-3.0-or-later
-//
-// This program is free software: you can redistribute it and/or modify it under
-// the terms of the GNU Lesser General Public License as published by the Free
-// Software Foundation, either version 3 of the License, or (at your option) any
-// later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT ANY
-// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License along
-// with this program (see COPYING). If not, see .
-//
-
-#include
-#include
-#include
-
-#include "clua-machine-util.h"
-#include "clua.h"
-#include "machine-c-api.h"
-
-namespace cartesi {
-
-/// \brief This is the machine.get_default_machine_config()
-/// method implementation.
-static int machine_class_index_get_default_config(lua_State *L) {
- const char *config{};
- if (cm_get_default_config(&config) != 0) {
- return luaL_error(L, "%s", cm_get_last_error_message());
- }
- clua_push_json_table(L, config);
- return 1;
-}
-
-/// \brief This is the machine.get_reg_address() method implementation.
-static int machine_class_index_get_reg_address(lua_State *L) {
- uint64_t addr{};
- if (cm_get_reg_address(clua_check_cm_proc_reg(L, 1), &addr) != 0) {
- return luaL_error(L, "%s", cm_get_last_error_message());
- }
- lua_pushinteger(L, static_cast(addr));
- return 1;
-}
-
-/// \brief This is the machine.verify_step_uarch() method implementation.
-static int machine_class_index_verify_step_uarch(lua_State *L) {
- lua_settop(L, 4);
- const char *log = clua_check_schemed_json_string(L, 2, "AccessLog");
- cm_hash root_hash{};
- clua_check_cm_hash(L, 1, &root_hash);
- cm_hash target_hash{};
- clua_check_cm_hash(L, 3, &target_hash);
- if (cm_verify_step_uarch(&root_hash, log, &target_hash) != 0) {
- return luaL_error(L, "%s", cm_get_last_error_message());
- }
- return 0;
-}
-
-/// \brief This is the machine.verify_reset_uarch() method implementation.
-static int machine_class_index_verify_reset_uarch(lua_State *L) {
- lua_settop(L, 4);
- const char *log = clua_check_schemed_json_string(L, 2, "AccessLog");
- cm_hash root_hash{};
- clua_check_cm_hash(L, 1, &root_hash);
- cm_hash target_hash{};
- clua_check_cm_hash(L, 3, &target_hash);
- if (cm_verify_reset_uarch(&root_hash, log, &target_hash) != 0) {
- return luaL_error(L, "%s", cm_get_last_error_message());
- }
- return 0;
-}
-
-/// \brief This is the machine.verify_send_cmio_response() method implementation.
-static int machine_class_index_verify_send_cmio_response(lua_State *L) {
- lua_settop(L, 6);
- const uint16_t reason = static_cast(luaL_checkinteger(L, 1));
- size_t length{0};
- // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- const auto *data = reinterpret_cast(luaL_checklstring(L, 2, &length));
- const char *log = clua_check_schemed_json_string(L, 4, "AccessLog");
- cm_hash root_hash{};
- clua_check_cm_hash(L, 3, &root_hash);
- cm_hash target_hash{};
- clua_check_cm_hash(L, 5, &target_hash);
- if (cm_verify_send_cmio_response(reason, data, length, &root_hash, log, &target_hash) != 0) {
- return luaL_error(L, "%s", cm_get_last_error_message());
- }
- return 0;
-}
-
-/// \brief Contents of the machine class metatable __index table.
-static const auto machine_class_index = cartesi::clua_make_luaL_Reg_array({
- {"get_default_config", machine_class_index_get_default_config},
- {"get_reg_address", machine_class_index_get_reg_address},
- {"verify_step_uarch", machine_class_index_verify_step_uarch},
- {"verify_reset_uarch", machine_class_index_verify_reset_uarch},
- {"verify_send_cmio_response", machine_class_index_verify_send_cmio_response},
-});
-
-/// \brief This is the cartesi.machine() constructor implementation.
-/// \param L Lua state.
-static int machine_ctor(lua_State *L) {
- lua_settop(L, 3);
- auto &managed_machine = clua_push_to(L, clua_managed_cm_ptr(nullptr));
- const char *runtime_config = !lua_isnil(L, 3) ? clua_check_json_string(L, 3) : nullptr;
- if (!lua_isstring(L, 2)) {
- const char *config = clua_check_json_string(L, 2);
- if (cm_create(config, runtime_config, &managed_machine.get()) != 0) {
- return luaL_error(L, "%s", cm_get_last_error_message());
- }
- } else {
- const char *dir = luaL_checkstring(L, 2);
- if (cm_load(dir, runtime_config, &managed_machine.get()) != 0) {
- return luaL_error(L, "%s", cm_get_last_error_message());
- }
- }
- return 1;
-}
-
-/// \brief Tag to identify the machine class-like constructor
-struct machine_class {};
-
-int clua_machine_init(lua_State *L, int ctxidx) {
- clua_createnewtype>(L, ctxidx);
- clua_createnewtype>(L, ctxidx);
- clua_createnewtype>(L, ctxidx);
- if (!clua_typeexists(L, ctxidx)) {
- clua_createtype(L, "cartesi machine class", ctxidx);
- clua_setmethods(L, machine_class_index.data(), 0, ctxidx);
- static const auto machine_class_meta = cartesi::clua_make_luaL_Reg_array({
- {"__call", machine_ctor},
- });
- clua_setmetamethods(L, machine_class_meta.data(), 0, ctxidx);
- }
- return 1;
-}
-
-int clua_machine_export(lua_State *L, int ctxidx) {
- const int ctxabsidx = lua_absindex(L, ctxidx);
- // cartesi
- clua_machine_init(L, ctxabsidx); // cartesi
- lua_newtable(L); // cartesi machine_class
- clua_setmetatable(L, -1, ctxabsidx); // cartesi machine_class
- lua_setfield(L, -2, "machine"); // cartesi
- return 0;
-}
-
-} // namespace cartesi
diff --git a/src/clua.h b/src/clua.h
index a6f74a22e..4ff32ec54 100644
--- a/src/clua.h
+++ b/src/clua.h
@@ -226,6 +226,7 @@ void clua_setmetatable(lua_State *L, int objidx, int ctxidx = lua_upvalueindex(1
///
template
int clua_push(lua_State *L, T &&value, int ctxidx = lua_upvalueindex(1)) {
+ ctxidx = lua_absindex(L, ctxidx);
T *ptr = static_cast(lua_newuserdata(L, sizeof(T)));
new (ptr) T{std::forward(value)};
clua_setmetatable(L, -1, ctxidx);
diff --git a/src/i-virtual-machine.h b/src/i-virtual-machine.h
index 043cdfa74..75fc3f227 100644
--- a/src/i-virtual-machine.h
+++ b/src/i-virtual-machine.h
@@ -51,6 +51,26 @@ class i_virtual_machine {
i_virtual_machine &operator=(const i_virtual_machine &other) = delete;
i_virtual_machine &operator=(i_virtual_machine &&other) noexcept = delete;
+ /// \brief Clone an object of same underlying type but without a machine instance
+ i_virtual_machine *clone_empty(void) const {
+ return do_clone_empty();
+ }
+
+ /// \brief Tells if object is empty (does not holds a machine instance)
+ bool is_empty(void) const {
+ return do_is_empty();
+ }
+
+ /// \brief Create a machine from config
+ void create(const machine_config &config, const machine_runtime_config &runtime = {}) {
+ return do_create(config, runtime);
+ }
+
+ /// \brief Load Create a machine from config
+ void load(const std::string &directory, const machine_runtime_config &runtime = {}) {
+ return do_load(directory, runtime);
+ }
+
/// \brief Runs the machine until mcycle reaches mcycle_end or the machine halts.
interpreter_break_reason run(uint64_t mcycle_end) {
return do_run(mcycle_end);
@@ -136,24 +156,17 @@ class i_virtual_machine {
return do_get_initial_config();
}
- /// \brief destroy
- void destroy(void) {
- do_destroy();
+ machine_runtime_config get_runtime_config(void) const {
+ return do_get_runtime_config();
}
- /// \brief snapshot
- void snapshot(void) {
- do_snapshot();
+ void set_runtime_config(const machine_runtime_config &r) {
+ return do_set_runtime_config(r);
}
- /// \brief commit
- void commit(void) {
- do_commit();
- }
-
- /// \brief rollback
- void rollback(void) {
- do_rollback();
+ /// \brief destroy
+ void destroy(void) {
+ do_destroy();
}
/// \brief Resets the microarchitecture state to pristine value
@@ -184,13 +197,50 @@ class i_virtual_machine {
do_send_cmio_response(reason, data, length);
}
- /// \brief Sends cmio response. and returns an access log
+ /// \brief Sends cmio response and returns an access log
access_log log_send_cmio_response(uint16_t reason, const unsigned char *data, uint64_t length,
const access_log::type &log_type) {
return do_log_send_cmio_response(reason, data, length, log_type);
}
+ /// \brief Gets the address of any register
+ uint64_t get_reg_address(reg r) const {
+ return do_get_reg_address(r);
+ }
+
+ /// \brief Returns copy of default machine config
+ machine_config get_default_config(void) const {
+ return do_get_default_config();
+ }
+
+ /// \brief Checks the validity of a state transition caused by log_step_uarch.
+ void verify_step_uarch(const hash_type &root_hash_before, const access_log &log,
+ const hash_type &root_hash_after) const {
+ return do_verify_step_uarch(root_hash_before, log, root_hash_after);
+ }
+
+ /// \brief Checks the validity of a state transition caused by log_reset_uarch.
+ void verify_reset_uarch(const hash_type &root_hash_before, const access_log &log,
+ const hash_type &root_hash_after) const {
+ return do_verify_reset_uarch(root_hash_before, log, root_hash_after);
+ }
+
+ /// \brief Checks the validity of state transitions caused by log_send_cmio_response.
+ void verify_send_cmio_response(uint16_t reason, const unsigned char *data, uint64_t length,
+ const hash_type &root_hash_before, const access_log &log, const hash_type &root_hash_after) const {
+ return do_verify_send_cmio_response(reason, data, length, root_hash_before, log, root_hash_after);
+ }
+
+ /// \brief Checks if implementation is jsorpc-virtual-machine
+ bool is_jsonrpc_virtual_machine(void) const {
+ return do_is_jsonrpc_virtual_machine();
+ }
+
private:
+ virtual i_virtual_machine *do_clone_empty(void) const = 0;
+ virtual bool do_is_empty(void) const = 0;
+ virtual void do_create(const machine_config &config, const machine_runtime_config &runtime) = 0;
+ virtual void do_load(const std::string &directory, const machine_runtime_config &runtime) = 0;
virtual interpreter_break_reason do_run(uint64_t mcycle_end) = 0;
virtual void do_store(const std::string &dir) const = 0;
virtual access_log do_log_step_uarch(const access_log::type &log_type) = 0;
@@ -208,10 +258,9 @@ class i_virtual_machine {
virtual uint64_t do_read_word(uint64_t address) const = 0;
virtual bool do_verify_dirty_page_maps(void) const = 0;
virtual machine_config do_get_initial_config(void) const = 0;
- virtual void do_snapshot() = 0;
+ virtual machine_runtime_config do_get_runtime_config(void) const = 0;
+ virtual void do_set_runtime_config(const machine_runtime_config &r) = 0;
virtual void do_destroy() = 0;
- virtual void do_commit() = 0;
- virtual void do_rollback() = 0;
virtual void do_reset_uarch() = 0;
virtual access_log do_log_reset_uarch(const access_log::type &log_type) = 0;
virtual uarch_interpreter_break_reason do_run_uarch(uint64_t uarch_cycle_end) = 0;
@@ -219,6 +268,17 @@ class i_virtual_machine {
virtual void do_send_cmio_response(uint16_t reason, const unsigned char *data, uint64_t length) = 0;
virtual access_log do_log_send_cmio_response(uint16_t reason, const unsigned char *data, uint64_t length,
const access_log::type &log_type) = 0;
+ virtual uint64_t do_get_reg_address(reg r) const = 0;
+ virtual machine_config do_get_default_config(void) const = 0;
+ virtual void do_verify_step_uarch(const hash_type &root_hash_before, const access_log &log,
+ const hash_type &root_hash_after) const = 0;
+ virtual void do_verify_reset_uarch(const hash_type &root_hash_before, const access_log &log,
+ const hash_type &root_hash_after) const = 0;
+ virtual void do_verify_send_cmio_response(uint16_t reason, const unsigned char *data, uint64_t length,
+ const hash_type &root_hash_before, const access_log &log, const hash_type &root_hash_after) const = 0;
+ virtual bool do_is_jsonrpc_virtual_machine(void) const {
+ return false;
+ }
};
} // namespace cartesi
diff --git a/src/json-util.h b/src/json-util.h
index f47e3f5e4..5680cdf9e 100644
--- a/src/json-util.h
+++ b/src/json-util.h
@@ -25,8 +25,7 @@
#define JSON_HAS_FILESYSTEM 0 // NOLINT(cppcoreguidelines-macro-usage)
#include
-#include "jsonrpc-connection.h"
-#include "jsonrpc-virtual-machine.h"
+#include "jsonrpc-fork-result.h"
#include "machine-merkle-tree.h"
#include "machine.h"
#include "semantic-version.h"
diff --git a/src/jsonrpc-connection.h b/src/jsonrpc-connection.h
deleted file mode 100644
index e1ed5d292..000000000
--- a/src/jsonrpc-connection.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright Cartesi and individual authors (see AUTHORS)
-// SPDX-License-Identifier: LGPL-3.0-or-later
-//
-// This program is free software: you can redistribute it and/or modify it under
-// the terms of the GNU Lesser General Public License as published by the Free
-// Software Foundation, either version 3 of the License, or (at your option) any
-// later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT ANY
-// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License along
-// with this program (see COPYING). If not, see .
-//
-
-#ifndef JSONRPC_CONNECTION_H
-#define JSONRPC_CONNECTION_H
-
-#include
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#include "asio-config.h" // must be included before any ASIO header
-#include
-#include
-#include
-#pragma GCC diagnostic pop
-
-#include "semantic-version.h"
-
-namespace cartesi {
-
-/// Result of a fork
-struct fork_result final {
- std::string address{};
- uint32_t pid{};
-};
-
-class jsonrpc_connection final {
-public:
- jsonrpc_connection(std::string remote_address, bool detach_server);
- jsonrpc_connection(const jsonrpc_connection &other) = delete;
- jsonrpc_connection(jsonrpc_connection &&other) noexcept = delete;
- jsonrpc_connection &operator=(const jsonrpc_connection &other) = delete;
- jsonrpc_connection &operator=(jsonrpc_connection &&other) noexcept = delete;
- ~jsonrpc_connection();
- bool is_snapshot(void) const;
- bool is_shutdown(void) const;
- boost::beast::tcp_stream &get_stream(void);
- const boost::beast::tcp_stream &get_stream(void) const;
- const std::string &get_remote_address(void) const;
- const std::string &get_remote_parent_address(void) const;
- void snapshot(void);
- void commit(void);
- void rollback(void);
-
- void shutdown_server(void);
- fork_result fork_server(void);
- std::string rebind_server(const std::string &address);
- semantic_version get_server_version(void);
-
-private:
- boost::asio::io_context m_ioc{1}; // The io_context is required for all I/O
- boost::beast::tcp_stream m_stream{m_ioc}; // TCP stream for keep alive connections
- boost::container::static_vector m_address{};
- const bool m_detach_server;
-};
-
-} // namespace cartesi
-
-#endif
diff --git a/src/jsonrpc-discover.json b/src/jsonrpc-discover.json
index a28928611..1b9fea631 100644
--- a/src/jsonrpc-discover.json
+++ b/src/jsonrpc-discover.json
@@ -1,43 +1,31 @@
{
"openrpc": "1.0.0-rc1",
"info": {
- "version": "0.0.2",
"title": "Remote Cartesi Machine",
+ "version": "0.5.0",
+ "description": "API for controling a remote Cartesi Machine server",
"license": {
"name": "MIT"
}
},
"methods": [
-
- {
- "name": "rpc.discover",
- "description": "Returns an OpenRPC schema as a description of this service",
- "params": [],
- "result": {
- "name": "OpenRPC Schema",
- "schema": {
- "$ref": "https://raw.githubusercontent.com/open-rpc/meta-schema/master/schema.json"
- }
- }
- },
-
{
"name": "fork",
"summary": "Forks the server",
"params": [],
"result": {
"name": "fork_result",
- "description": "Result of the fork containing address and pid",
+ "description": "Information about the forked child",
"schema": {
"$ref": "#/components/schemas/ForkResult"
}
}
},
-
{
"name": "rebind",
"summary": "Changes the address the server is listening to",
- "params": [ {
+ "params": [
+ {
"name": "address",
"description": "URL of the new address",
"required": true,
@@ -48,13 +36,12 @@
],
"result": {
"name": "address",
- "description": "URL of the rebound address",
+ "description": "URL of address server actually bound to",
"schema": {
"type": "string"
}
}
},
-
{
"name": "shutdown",
"summary": "Causes the server to shutdown and exit",
@@ -67,7 +54,6 @@
}
}
},
-
{
"name": "get_version",
"summary": "Returns the server version",
@@ -80,19 +66,65 @@
}
}
},
-
{
- "name": "machine.machine.config",
+ "name": "is_empty",
+ "summary": "Checks if server has no machine instance",
+ "params": [],
+ "result": {
+ "name": "empty",
+ "description": "True if server has no machine",
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ },
+ {
+ "name": "machine.set_runtime_config",
+ "summary": "Changes the machine runtime configuration",
+ "params": [
+ {
+ "name": "runtime_config",
+ "description": "Machine runtime configuration",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/MachineRuntimeConfig"
+ }
+ }
+ ],
+ "result": {
+ "name": "status",
+ "description": "True when operation succeeded",
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ },
+ {
+ "name": "machine.get_runtime_config",
+ "summary": "Returns the current machine runtime configuration",
+ "params": [],
+ "result": {
+ "name": "runtime_config",
+ "description": "Machine runtime configuration",
+ "schema": {
+ "$ref": "#/components/schemas/MachineRuntimeConfig"
+ }
+ }
+ },
+ {
+ "name": "machine.create",
"summary": "Instantiates a machine from a machine configuration",
- "params": [ {
- "name":"config",
+ "params": [
+ {
+ "name": "config",
"description": "Machine configuration",
"required": true,
"schema": {
"$ref": "#/components/schemas/MachineConfig"
}
- }, {
- "name":"runtime",
+ },
+ {
+ "name": "runtime_config",
"description": "Machine runtime configuration",
"required": false,
"schema": {
@@ -108,19 +140,20 @@
}
}
},
-
{
- "name": "machine.machine.directory",
+ "name": "machine.load",
"summary": "Instantiates a machine from a directory where a machine instance was stored",
- "params": [ {
- "name":"directory",
+ "params": [
+ {
+ "name": "directory",
"description": "Directory with stored machine instance",
"required": true,
"schema": {
"type": "string"
}
- }, {
- "name":"runtime",
+ },
+ {
+ "name": "runtime_config",
"description": "Machine runtime configuration",
"required": false,
"schema": {
@@ -136,11 +169,10 @@
}
}
},
-
{
"name": "machine.destroy",
"summary": "Destroys current machine instance",
- "params": [ ],
+ "params": [],
"result": {
"name": "status",
"description": "True when operation succeeded",
@@ -149,12 +181,12 @@
}
}
},
-
{
"name": "machine.store",
"summary": "Stores machine instance in a directory",
- "params": [ {
- "name":"directory",
+ "params": [
+ {
+ "name": "directory",
"description": "Directory to stored machine instance",
"required": true,
"schema": {
@@ -170,12 +202,12 @@
}
}
},
-
{
"name": "machine.run",
"summary": "Runs the emulator until a given cycle",
- "params": [ {
- "name":"mcycle_end",
+ "params": [
+ {
+ "name": "mcycle_end",
"description": "The maximum value of the cycle counter",
"required": true,
"schema": {
@@ -191,12 +223,12 @@
}
}
},
-
{
"name": "machine.run_uarch",
"summary": "Runs the small emulator until a given cycle",
- "params": [ {
- "name":"uarch_cycle_end",
+ "params": [
+ {
+ "name": "uarch_cycle_end",
"description": "The maximum value of the cycle counter",
"required": true,
"schema": {
@@ -212,12 +244,12 @@
}
}
},
-
{
"name": "machine.log_step_uarch",
"summary": "Runs the small emulator for one cycle and return a log of state accesses",
- "params": [ {
- "name":"log_type",
+ "params": [
+ {
+ "name": "log_type",
"description": "The maximum value of the cycle counter",
"required": true,
"schema": {
@@ -233,26 +265,28 @@
}
}
},
-
{
"name": "machine.verify_step_uarch",
"summary": "Verifies a state transition caused by log_step_uarch",
- "params": [ {
- "name":"root_hash_before",
+ "params": [
+ {
+ "name": "root_hash_before",
"description": "State hash before transition described by access log",
"required": true,
"schema": {
"$ref": "#/components/schemas/Base64Hash"
}
- }, {
- "name":"log",
+ },
+ {
+ "name": "log",
"description": "Access log describing transition",
"required": true,
"schema": {
"$ref": "#/components/schemas/AccessLog"
}
- }, {
- "name":"root_hash_after",
+ },
+ {
+ "name": "root_hash_after",
"description": "State hash after transition described by access log",
"required": true,
"schema": {
@@ -268,26 +302,28 @@
}
}
},
-
{
"name": "machine.verify_reset_uarch",
"summary": "Verifies a state transition caused by log_reset_uarch",
- "params": [ {
- "name":"root_hash_before",
+ "params": [
+ {
+ "name": "root_hash_before",
"description": "State hash before transition described by access log",
"required": true,
"schema": {
"$ref": "#/components/schemas/Base64Hash"
}
- }, {
- "name":"log",
+ },
+ {
+ "name": "log",
"description": "Access log describing transition",
"required": true,
"schema": {
"$ref": "#/components/schemas/AccessLog"
}
- }, {
- "name":"root_hash_after",
+ },
+ {
+ "name": "root_hash_after",
"description": "State hash after transition described by access log",
"required": true,
"schema": {
@@ -303,11 +339,10 @@
}
}
},
-
{
"name": "machine.get_root_hash",
"summary": "Obtains the Merkle hash of the current machine state",
- "params": [ ],
+ "params": [],
"result": {
"name": "hash",
"description": "Merkle hash",
@@ -316,19 +351,20 @@
}
}
},
-
{
"name": "machine.get_proof",
"summary": "Obtains a Merkle proof for a range in the machine state",
- "params": [ {
- "name":"address",
+ "params": [
+ {
+ "name": "address",
"description": "Starting address of range in state (must be aligned to size)",
"required": true,
"schema": {
"$ref": "#/components/schemas/UnsignedInteger"
}
- }, {
- "name":"log2_size",
+ },
+ {
+ "name": "log2_size",
"description": "Log2 of size of range",
"required": true,
"schema": {
@@ -344,12 +380,12 @@
}
}
},
-
{
"name": "machine.read_word",
"summary": "Reads a 64-bit word from memory (must be aligned)",
- "params": [ {
- "name":"address",
+ "params": [
+ {
+ "name": "address",
"description": "Starting physical address of word",
"required": true,
"schema": {
@@ -365,19 +401,20 @@
}
}
},
-
{
"name": "machine.read_memory",
"summary": "Reads a span of memory from the state (must be contained in the same memory range)",
- "params": [ {
- "name":"address",
+ "params": [
+ {
+ "name": "address",
"description": "Starting physical address of span",
"required": true,
"schema": {
"$ref": "#/components/schemas/UnsignedInteger"
}
- } , {
- "name":"length",
+ },
+ {
+ "name": "length",
"description": "Length of span",
"required": true,
"schema": {
@@ -393,19 +430,20 @@
}
}
},
-
{
"name": "machine.write_memory",
"summary": "Writes a span of memory to the state (must be contained in the same memory range)",
- "params": [ {
- "name":"address",
+ "params": [
+ {
+ "name": "address",
"description": "Starting physical address of span",
"required": true,
"schema": {
"$ref": "#/components/schemas/UnsignedInteger"
}
- } , {
- "name":"data",
+ },
+ {
+ "name": "data",
"description": "Span of memory",
"required": true,
"schema": {
@@ -421,19 +459,20 @@
}
}
},
-
{
"name": "machine.read_virtual_memory",
"summary": "Reads a span of memory from the state (must be contained in the same memory range)",
- "params": [ {
- "name":"address",
+ "params": [
+ {
+ "name": "address",
"description": "Starting virtual address of span according to current mapping",
"required": true,
"schema": {
"$ref": "#/components/schemas/UnsignedInteger"
}
- } , {
- "name":"length",
+ },
+ {
+ "name": "length",
"description": "Length of span",
"required": true,
"schema": {
@@ -449,19 +488,20 @@
}
}
},
-
{
"name": "machine.write_virtual_memory",
"summary": "Writes a span of memory to the state (must be contained in the same memory range)",
- "params": [ {
- "name":"address",
+ "params": [
+ {
+ "name": "address",
"description": "Starting virtual address of span according to current mapping",
"required": true,
"schema": {
"$ref": "#/components/schemas/UnsignedInteger"
}
- } , {
- "name":"data",
+ },
+ {
+ "name": "data",
"description": "Span of memory",
"required": true,
"schema": {
@@ -477,12 +517,12 @@
}
}
},
-
{
"name": "machine.translate_virtual_address",
"summary": "Translates a virtual memory address to its corresponding physical memory address",
- "params": [ {
- "name":"vaddr",
+ "params": [
+ {
+ "name": "vaddr",
"description": "Virtual address to translate",
"required": true,
"schema": {
@@ -498,12 +538,12 @@
}
}
},
-
{
"name": "machine.replace_memory_range",
"summary": "Replaces a memory range",
- "params": [ {
- "name":"range",
+ "params": [
+ {
+ "name": "range",
"description": "New memory range (must be compatible with existing range)",
"required": true,
"schema": {
@@ -519,12 +559,12 @@
}
}
},
-
{
"name": "machine.read_register",
"summary": "Reads the value of a register",
- "params": [ {
- "name":"reg",
+ "params": [
+ {
+ "name": "reg",
"description": "Register to read",
"required": true,
"schema": {
@@ -540,19 +580,20 @@
}
}
},
-
{
"name": "machine.write_reg",
"summary": "Writes the value of a register",
- "params": [ {
- "name":"reg",
+ "params": [
+ {
+ "name": "reg",
"description": "Register to write",
"required": true,
"schema": {
"$ref": "#/components/schemas/REG"
}
- }, {
- "name":"value",
+ },
+ {
+ "name": "value",
"description": "Value of register (little-endian)",
"required": true,
"schema": {
@@ -568,12 +609,12 @@
}
}
},
-
{
"name": "machine.get_reg_address",
"summary": "Returns the address of a register",
- "params": [ {
- "name":"reg",
+ "params": [
+ {
+ "name": "reg",
"description": "Register to obtain address",
"required": true,
"schema": {
@@ -589,7 +630,6 @@
}
}
},
-
{
"name": "machine.reset_uarch",
"summary": "Reset uarch to pristine state",
@@ -602,12 +642,12 @@
}
}
},
-
{
"name": "machine.log_reset_uarch",
"summary": "Reset uarch to pristine state and return a log of state accesses",
- "params": [ {
- "name":"log_type",
+ "params": [
+ {
+ "name": "log_type",
"description": "The maximum value of the cycle counter",
"required": true,
"schema": {
@@ -623,7 +663,6 @@
}
}
},
-
{
"name": "machine.get_initial_config",
"summary": "Returns initial machine configuration for instance",
@@ -636,7 +675,6 @@
}
}
},
-
{
"name": "machine.get_default_config",
"summary": "Returns default machine configuration",
@@ -649,7 +687,6 @@
}
}
},
-
{
"name": "machine.verify_merkle_tree",
"summary": "Verifies sanity of Merkle tree",
@@ -662,7 +699,6 @@
}
}
},
-
{
"name": "machine.verify_dirty_page_maps",
"summary": "Verifies sanity of dirty page maps",
@@ -675,7 +711,6 @@
}
}
},
-
{
"name": "machine.get_memory_ranges",
"summary": "Returns a list with descriptions for all of the machine's memory ranges",
@@ -688,19 +723,20 @@
}
}
},
-
{
"name": "machine.send_cmio_response",
"summary": "Sends cmio response.",
- "params": [ {
- "name":"reason",
+ "params": [
+ {
+ "name": "reason",
"description": "Reason for sending response",
"required": true,
"schema": {
"$ref": "#/components/schemas/UnsignedInteger"
}
- },{
- "name":"data",
+ },
+ {
+ "name": "data",
"description": "Response data to send",
"required": true,
"schema": {
@@ -716,26 +752,28 @@
}
}
},
-
{
"name": "machine.log_send_cmio_response",
"summary": "Sends cmio response and returns an access log",
- "params": [ {
- "name":"reason",
- "description": "Reason for sending response",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/UnsignedInteger"
- }
- }, {
- "name":"data",
+ "params": [
+ {
+ "name": "reason",
+ "description": "Reason for sending response",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/UnsignedInteger"
+ }
+ },
+ {
+ "name": "data",
"description": "Response data to send",
"required": true,
"schema": {
"$ref": "#/components/schemas/Base64String"
}
- },{
- "name":"log_type",
+ },
+ {
+ "name": "log_type",
"description": "The log type to generate",
"required": true,
"schema": {
@@ -751,40 +789,44 @@
}
}
},
-
{
"name": "machine.verify_send_cmio_response",
"summary": "Verifies a state transition caused by log_send_cmio_response",
- "params": [ {
- "name":"reason",
- "description": "Reason for sending response",
- "required": true,
- "schema": {
- "$ref": "#/components/schemas/UnsignedInteger"
- }
- }, {
- "name":"data",
+ "params": [
+ {
+ "name": "reason",
+ "description": "Reason for sending response",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/UnsignedInteger"
+ }
+ },
+ {
+ "name": "data",
"description": "The response data sent when the log was generated.",
"required": true,
"schema": {
"$ref": "#/components/schemas/Base64String"
}
- }, {
- "name":"root_hash_before",
+ },
+ {
+ "name": "root_hash_before",
"description": "State hash before response was sent.",
"required": true,
"schema": {
"$ref": "#/components/schemas/Base64Hash"
}
- }, {
- "name":"log",
+ },
+ {
+ "name": "log",
"description": "State access log to be verified.",
"required": true,
"schema": {
"$ref": "#/components/schemas/AccessLog"
}
- }, {
- "name":"root_hash_after",
+ },
+ {
+ "name": "root_hash_after",
"description": "State hash after response was sent.",
"required": true,
"schema": {
@@ -800,188 +842,26 @@
}
}
}
-
],
-
"components": {
"schemas": {
-
- "REG": {
- "title": "REG",
- "enum": [
- "x0",
- "x1",
- "x2",
- "x3",
- "x4",
- "x5",
- "x6",
- "x7",
- "x8",
- "x9",
- "x10",
- "x11",
- "x12",
- "x13",
- "x14",
- "x15",
- "x16",
- "x17",
- "x18",
- "x19",
- "x20",
- "x21",
- "x22",
- "x23",
- "x24",
- "x25",
- "x26",
- "x27",
- "x28",
- "x29",
- "x30",
- "x31",
- "f0",
- "f1",
- "f2",
- "f3",
- "f4",
- "f5",
- "f6",
- "f7",
- "f8",
- "f9",
- "f10",
- "f11",
- "f12",
- "f13",
- "f14",
- "f15",
- "f16",
- "f17",
- "f18",
- "f19",
- "f20",
- "f21",
- "f22",
- "f23",
- "f24",
- "f25",
- "f26",
- "f27",
- "f28",
- "f29",
- "f30",
- "f31",
- "pc",
- "fcsr",
- "mvendorid",
- "marchid",
- "mimpid",
- "mcycle",
- "icycleinstret",
- "mstatus",
- "mtvec",
- "mscratch",
- "mepc",
- "mcause",
- "mtval",
- "misa",
- "mie",
- "mip",
- "medeleg",
- "mideleg",
- "mcounteren",
- "menvcfg",
- "stvec",
- "sscratch",
- "sepc",
- "scause",
- "stval",
- "satp",
- "scounteren",
- "senvcfg",
- "ilrsc",
- "iflags",
- "iunrep",
- "clint_mtimecmp",
- "plic_girqpend",
- "plic_girqsrvd",
- "htif_tohost",
- "htif_fromhost",
- "htif_ihalt",
- "htif_iconsole",
- "htif_iyield",
- "uarch_x0",
- "uarch_x1",
- "uarch_x2",
- "uarch_x3",
- "uarch_x4",
- "uarch_x5",
- "uarch_x6",
- "uarch_x7",
- "uarch_x8",
- "uarch_x9",
- "uarch_x10",
- "uarch_x11",
- "uarch_x12",
- "uarch_x13",
- "uarch_x14",
- "uarch_x15",
- "uarch_x16",
- "uarch_x17",
- "uarch_x18",
- "uarch_x19",
- "uarch_x20",
- "uarch_x21",
- "uarch_x22",
- "uarch_x23",
- "uarch_x24",
- "uarch_x25",
- "uarch_x26",
- "uarch_x27",
- "uarch_x28",
- "uarch_x29",
- "uarch_x30",
- "uarch_x31",
- "uarch_pc",
- "uarch_cycle",
- "uarch_halt_flag",
- "iflags_prv",
- "iflags_x",
- "iflags_y",
- "iflags_h",
- "htif_tohost_dev",
- "htif_tohost_cmd",
- "htif_tohost_reason",
- "htif_tohost_data",
- "htif_fromhost_dev",
- "htif_fromhost_cmd",
- "htif_fromhost_reason",
- "htif_fromhost_data"
- ]
- },
-
- "InterpreterBreakReason": {
- "title": "InterpreterBreakReason",
- "enum": [
- "failed",
- "halted",
- "yielded_manually",
- "yielded_automatically",
- "yielded_softly",
- "reached_target_mcycle"
- ]
+ "UnsignedInteger": {
+ "title": "UnsignedInteger",
+ "type": "integer",
+ "minimum": 0
},
-
- "UarchInterpreterBreakReason": {
- "title": "UarchInterpreterBreakReason",
- "enum": [
- "reached_target_cycle",
- "uarch_halted"
- ]
+ "ForkResult": {
+ "title": "ForkResult",
+ "type": "object",
+ "properties": {
+ "address": {
+ "type": "string"
+ },
+ "pid": {
+ "$ref": "#/components/schemas/UnsignedInteger"
+ }
+ }
},
-
"SemanticVersion": {
"title": "SemanticVersion",
"type": "object",
@@ -1008,7 +888,48 @@
}
}
},
-
+ "ConcurrencyRuntimeConfig": {
+ "title": "ConcurrencyRuntimeConfig",
+ "type": "object",
+ "properties": {
+ "update_merkle_tree": {
+ "$ref": "#/components/schemas/UnsignedInteger"
+ }
+ }
+ },
+ "HTIFRuntimeConfig": {
+ "title": "HTIFRuntimeConfig",
+ "type": "object",
+ "properties": {
+ "no_console_putchar": {
+ "type": "boolean"
+ }
+ }
+ },
+ "MachineRuntimeConfig": {
+ "title": "MachineRuntimeConfig",
+ "type": "object",
+ "properties": {
+ "concurrency": {
+ "$ref": "#/components/schemas/ConcurrencyRuntimeConfig"
+ },
+ "htif": {
+ "$ref": "#/components/schemas/HTIFRuntimeConfig"
+ },
+ "skip_root_hash_check": {
+ "type": "boolean"
+ },
+ "skip_root_hash_store": {
+ "type": "boolean"
+ },
+ "skip_version_check": {
+ "type": "boolean"
+ },
+ "soft_yield": {
+ "type": "boolean"
+ }
+ }
+ },
"ProcessorConfig": {
"title": "ProcessorConfig",
"type": "object",
@@ -1300,206 +1221,6 @@
}
}
},
-
- "UnsignedInteger": {
- "title": "UnsignedInteger",
- "type": "integer",
- "minimum": 0
- },
-
- "Base64String": {
- "title": "Base64String",
- "type": "string",
- "contentEncoding": "base64"
- },
-
- "Base64Hash": {
- "title": "Base64Hash",
- "type": "string",
- "contentEncoding": "base64",
- "minLength": 45,
- "maxLength": 45
- },
-
- "Base64HashArray": {
- "title": "Base64HashArray",
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/Base64Hash"
- }
- },
-
- "NoteArray": {
- "title": "NoteArray",
- "type": "array",
- "items": {
- "type": "string"
- }
- },
-
- "BracketArray": {
- "title": "BracketArray",
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/Bracket"
- }
- },
-
- "BracketType": {
- "title": "BracketType",
- "enum": [
- "begin",
- "end"
- ]
- },
-
- "Bracket": {
- "title": "Bracket",
- "type": "object",
- "properties": {
- "type": {
- "$ref": "#/components/schemas/BracketType"
- },
- "where": {
- "$ref": "#/components/schemas/UnsignedInteger"
- },
- "text": {
- "type": "string"
- }
- },
- "required": [
- "type",
- "where",
- "text"
- ]
- },
-
- "Proof": {
- "title": "Proof",
- "type": "object",
- "properties": {
- "target_address": {
- "$ref": "#/components/schemas/UnsignedInteger"
- },
- "log2_target_size": {
- "$ref": "#/components/schemas/UnsignedInteger"
- },
- "target_hash": {
- "$ref": "#/components/schemas/Base64Hash"
- },
- "log2_root_size": {
- "$ref": "#/components/schemas/UnsignedInteger"
- },
- "root_hash": {
- "$ref": "#/components/schemas/Base64Hash"
- },
- "sibling_hashes": {
- "$ref": "#/components/schemas/Base64HashArray"
- }
- },
- "required": [
- "target_address",
- "log2_target_size",
- "target_hash",
- "log2_root_size",
- "root_hash",
- "sibling_hashes"
- ]
- },
-
- "Access": {
- "title": "Access",
- "type": "object",
- "properties": {
- "type": {
- "$ref": "#/components/schemas/AccessType"
- },
- "address": {
- "$ref": "#/components/schemas/UnsignedInteger"
- },
- "log2_size": {
- "$ref": "#/components/schemas/UnsignedInteger"
- },
- "read_hash": {
- "$ref": "#/components/schemas/Base64String"
- },
- "read": {
- "$ref": "#/components/schemas/Base64String"
- },
- "written_hash": {
- "$ref": "#/components/schemas/Base64String"
- },
- "written": {
- "$ref": "#/components/schemas/Base64String"
- },
- "sibling_hashes": {
- "$ref": "#/components/schemas/Base64HashArray"
- }
- },
- "required": [
- "type",
- "address",
- "log2_size",
- "read_hash"
- ]
- },
-
- "AccessArray": {
- "title": "AccessArray",
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/Access"
- }
- },
-
- "AccessType": {
- "title": "AccessType",
- "enum": [
- "read",
- "write"
- ]
- },
-
- "AccessLogType": {
- "title": "AccessLogType",
- "type": "object",
- "properties": {
- "has_annotations": {
- "type": "boolean"
- },
- "has_large_data": {
- "type": "boolean"
- }
- },
- "required": [
- "has_annotations",
- "has_large_data"
- ]
- },
-
- "AccessLog": {
- "title": "AccessLog",
- "type": "object",
- "properties": {
- "log_type": {
- "$ref": "#/components/schemas/AccessLogType"
- },
- "accesses": {
- "$ref": "#/components/schemas/AccessArray"
- },
- "notes": {
- "$ref": "#/components/schemas/NoteArray"
- },
- "brackets": {
- "$ref": "#/components/schemas/BracketArray"
- }
- },
- "required": [
- "log_type",
- "accesses"
- ]
- },
-
"RAMConfig": {
"title": "RAMConfig",
"type": "object",
@@ -1515,7 +1236,6 @@
"length"
]
},
-
"DTBConfig": {
"title": "DTBConfig",
"type": "object",
@@ -1534,7 +1254,6 @@
}
}
},
-
"MemoryRangeConfig": {
"title": "MemoryRangeConfig",
"type": "object",
@@ -1553,7 +1272,6 @@
}
}
},
-
"CmioBufferConfig": {
"title": "CmioBufferConfig",
"type": "object",
@@ -1566,7 +1284,6 @@
}
}
},
-
"VirtIOHostfwd": {
"title": "VirtIOHostfwd",
"type": "object",
@@ -1588,7 +1305,6 @@
}
}
},
-
"VirtIOHostfwdArray": {
"title": "VirtIOHostfwdArray",
"type": "array",
@@ -1596,7 +1312,6 @@
"$ref": "#/components/schemas/VirtIOHostfwd"
}
},
-
"VirtIODeviceConfig": {
"title": "VirtIODeviceConfig",
"type": "object",
@@ -1621,7 +1336,6 @@
}
}
},
-
"FlashDriveConfigs": {
"title": "FlashDriveConfigs",
"type": "array",
@@ -1629,7 +1343,6 @@
"$ref": "#/components/schemas/MemoryRangeConfig"
}
},
-
"TLBConfig": {
"title": "TLBConfig",
"type": "object",
@@ -1639,7 +1352,6 @@
}
}
},
-
"CLINTConfig": {
"title": "CLINTConfig",
"type": "object",
@@ -1649,7 +1361,6 @@
}
}
},
-
"PLICConfig": {
"title": "PLICConfig",
"type": "object",
@@ -1662,7 +1373,6 @@
}
}
},
-
"HTIFConfig": {
"title": "HTIFConfig",
"type": "object",
@@ -1684,7 +1394,6 @@
}
}
},
-
"UarchProcessorConfig": {
"title": "UarchProcessorConfig",
"type": "object",
@@ -1796,7 +1505,6 @@
}
}
},
-
"UarchRAMConfig": {
"title": "UarchRAMConfig",
"type": "object",
@@ -1809,7 +1517,6 @@
}
}
},
-
"UarchConfig": {
"title": "UarchConfig",
"type": "object",
@@ -1822,7 +1529,6 @@
}
}
},
-
"CmioConfig": {
"title": "CmioConfig",
"type": "object",
@@ -1835,7 +1541,6 @@
}
}
},
-
"VirtIOConfigs": {
"title": "VirtIOConfigs",
"type": "array",
@@ -1843,7 +1548,6 @@
"$ref": "#/components/schemas/VirtIODeviceConfig"
}
},
-
"MachineConfig": {
"title": "MachineConfig",
"type": "object",
@@ -1883,52 +1587,359 @@
}
}
},
-
- "ConcurrencyRuntimeConfig": {
- "title": "ConcurrencyRuntimeConfig",
- "type": "object",
- "properties": {
- "update_merkle_tree": {
- "$ref": "#/components/schemas/UnsignedInteger"
- }
- }
+ "InterpreterBreakReason": {
+ "title": "InterpreterBreakReason",
+ "enum": [
+ "failed",
+ "halted",
+ "yielded_manually",
+ "yielded_automatically",
+ "yielded_softly",
+ "reached_target_mcycle"
+ ]
},
-
- "HTIFRuntimeConfig": {
- "title": "HTIFRuntimeConfig",
+ "UarchInterpreterBreakReason": {
+ "title": "UarchInterpreterBreakReason",
+ "enum": [
+ "reached_target_cycle",
+ "uarch_halted"
+ ]
+ },
+ "Base64String": {
+ "title": "Base64String",
+ "type": "string",
+ "contentEncoding": "base64"
+ },
+ "Base64Hash": {
+ "title": "Base64Hash",
+ "type": "string",
+ "contentEncoding": "base64",
+ "minLength": 45,
+ "maxLength": 45
+ },
+ "Base64HashArray": {
+ "title": "Base64HashArray",
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Base64Hash"
+ }
+ },
+ "NoteArray": {
+ "title": "NoteArray",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "BracketArray": {
+ "title": "BracketArray",
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Bracket"
+ }
+ },
+ "BracketType": {
+ "title": "BracketType",
+ "enum": [
+ "begin",
+ "end"
+ ]
+ },
+ "Bracket": {
+ "title": "Bracket",
"type": "object",
"properties": {
- "no_console_putchar": {
- "type": "boolean"
+ "type": {
+ "$ref": "#/components/schemas/BracketType"
+ },
+ "where": {
+ "$ref": "#/components/schemas/UnsignedInteger"
+ },
+ "text": {
+ "type": "string"
}
- }
+ },
+ "required": [
+ "type",
+ "where",
+ "text"
+ ]
},
-
- "MachineRuntimeConfig": {
- "title": "MachineRuntimeConfig",
+ "Proof": {
+ "title": "Proof",
"type": "object",
"properties": {
- "concurrency": {
- "$ref": "#/components/schemas/ConcurrencyRuntimeConfig"
+ "target_address": {
+ "$ref": "#/components/schemas/UnsignedInteger"
},
- "htif": {
- "$ref": "#/components/schemas/HTIFRuntimeConfig"
+ "log2_target_size": {
+ "$ref": "#/components/schemas/UnsignedInteger"
},
- "skip_root_hash_check": {
- "type": "boolean"
+ "target_hash": {
+ "$ref": "#/components/schemas/Base64Hash"
},
- "skip_root_hash_store": {
- "type": "boolean"
+ "log2_root_size": {
+ "$ref": "#/components/schemas/UnsignedInteger"
},
- "skip_version_check": {
+ "root_hash": {
+ "$ref": "#/components/schemas/Base64Hash"
+ },
+ "sibling_hashes": {
+ "$ref": "#/components/schemas/Base64HashArray"
+ }
+ },
+ "required": [
+ "target_address",
+ "log2_target_size",
+ "target_hash",
+ "log2_root_size",
+ "root_hash",
+ "sibling_hashes"
+ ]
+ },
+ "Access": {
+ "title": "Access",
+ "type": "object",
+ "properties": {
+ "type": {
+ "$ref": "#/components/schemas/AccessType"
+ },
+ "address": {
+ "$ref": "#/components/schemas/UnsignedInteger"
+ },
+ "log2_size": {
+ "$ref": "#/components/schemas/UnsignedInteger"
+ },
+ "read_hash": {
+ "$ref": "#/components/schemas/Base64String"
+ },
+ "read": {
+ "$ref": "#/components/schemas/Base64String"
+ },
+ "written_hash": {
+ "$ref": "#/components/schemas/Base64String"
+ },
+ "written": {
+ "$ref": "#/components/schemas/Base64String"
+ },
+ "sibling_hashes": {
+ "$ref": "#/components/schemas/Base64HashArray"
+ }
+ },
+ "required": [
+ "type",
+ "address",
+ "log2_size",
+ "read_hash"
+ ]
+ },
+ "AccessArray": {
+ "title": "AccessArray",
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Access"
+ }
+ },
+ "AccessType": {
+ "title": "AccessType",
+ "enum": [
+ "read",
+ "write"
+ ]
+ },
+ "AccessLogType": {
+ "title": "AccessLogType",
+ "type": "object",
+ "properties": {
+ "has_annotations": {
"type": "boolean"
},
- "soft_yield": {
+ "has_large_data": {
"type": "boolean"
}
- }
+ },
+ "required": [
+ "has_annotations",
+ "has_large_data"
+ ]
+ },
+ "AccessLog": {
+ "title": "AccessLog",
+ "type": "object",
+ "properties": {
+ "log_type": {
+ "$ref": "#/components/schemas/AccessLogType"
+ },
+ "accesses": {
+ "$ref": "#/components/schemas/AccessArray"
+ },
+ "notes": {
+ "$ref": "#/components/schemas/NoteArray"
+ },
+ "brackets": {
+ "$ref": "#/components/schemas/BracketArray"
+ }
+ },
+ "required": [
+ "log_type",
+ "accesses"
+ ]
+ },
+ "REG": {
+ "title": "REG",
+ "enum": [
+ "x0",
+ "x1",
+ "x2",
+ "x3",
+ "x4",
+ "x5",
+ "x6",
+ "x7",
+ "x8",
+ "x9",
+ "x10",
+ "x11",
+ "x12",
+ "x13",
+ "x14",
+ "x15",
+ "x16",
+ "x17",
+ "x18",
+ "x19",
+ "x20",
+ "x21",
+ "x22",
+ "x23",
+ "x24",
+ "x25",
+ "x26",
+ "x27",
+ "x28",
+ "x29",
+ "x30",
+ "x31",
+ "f0",
+ "f1",
+ "f2",
+ "f3",
+ "f4",
+ "f5",
+ "f6",
+ "f7",
+ "f8",
+ "f9",
+ "f10",
+ "f11",
+ "f12",
+ "f13",
+ "f14",
+ "f15",
+ "f16",
+ "f17",
+ "f18",
+ "f19",
+ "f20",
+ "f21",
+ "f22",
+ "f23",
+ "f24",
+ "f25",
+ "f26",
+ "f27",
+ "f28",
+ "f29",
+ "f30",
+ "f31",
+ "pc",
+ "fcsr",
+ "mvendorid",
+ "marchid",
+ "mimpid",
+ "mcycle",
+ "icycleinstret",
+ "mstatus",
+ "mtvec",
+ "mscratch",
+ "mepc",
+ "mcause",
+ "mtval",
+ "misa",
+ "mie",
+ "mip",
+ "medeleg",
+ "mideleg",
+ "mcounteren",
+ "menvcfg",
+ "stvec",
+ "sscratch",
+ "sepc",
+ "scause",
+ "stval",
+ "satp",
+ "scounteren",
+ "senvcfg",
+ "ilrsc",
+ "iflags",
+ "iunrep",
+ "clint_mtimecmp",
+ "plic_girqpend",
+ "plic_girqsrvd",
+ "htif_tohost",
+ "htif_fromhost",
+ "htif_ihalt",
+ "htif_iconsole",
+ "htif_iyield",
+ "uarch_x0",
+ "uarch_x1",
+ "uarch_x2",
+ "uarch_x3",
+ "uarch_x4",
+ "uarch_x5",
+ "uarch_x6",
+ "uarch_x7",
+ "uarch_x8",
+ "uarch_x9",
+ "uarch_x10",
+ "uarch_x11",
+ "uarch_x12",
+ "uarch_x13",
+ "uarch_x14",
+ "uarch_x15",
+ "uarch_x16",
+ "uarch_x17",
+ "uarch_x18",
+ "uarch_x19",
+ "uarch_x20",
+ "uarch_x21",
+ "uarch_x22",
+ "uarch_x23",
+ "uarch_x24",
+ "uarch_x25",
+ "uarch_x26",
+ "uarch_x27",
+ "uarch_x28",
+ "uarch_x29",
+ "uarch_x30",
+ "uarch_x31",
+ "uarch_pc",
+ "uarch_cycle",
+ "uarch_halt_flag",
+ "iflags_prv",
+ "iflags_x",
+ "iflags_y",
+ "iflags_h",
+ "htif_tohost_dev",
+ "htif_tohost_cmd",
+ "htif_tohost_reason",
+ "htif_tohost_data",
+ "htif_fromhost_dev",
+ "htif_fromhost_cmd",
+ "htif_fromhost_reason",
+ "htif_fromhost_data"
+ ]
},
-
"MemoryRangeDescription": {
"title": "MemoryRangeDescription",
"type": "object",
@@ -1944,26 +1955,12 @@
}
}
},
-
"MemoryRangeDescriptionArray": {
"title": "MemoryRangeDescriptionArray",
"type": "array",
"items": {
"$ref": "#/components/schemas/MemoryRangeDescription"
}
- },
-
- "ForkResult": {
- "title": "ForkResult",
- "type": "object",
- "properties": {
- "address": {
- "type": "string"
- },
- "pid": {
- "$ref": "#/components/schemas/UnsignedInteger"
- }
- }
}
}
}
diff --git a/src/clua-machine.h b/src/jsonrpc-fork-result.h
similarity index 63%
rename from src/clua-machine.h
rename to src/jsonrpc-fork-result.h
index 2b08af9b7..fc5a66b05 100644
--- a/src/clua-machine.h
+++ b/src/jsonrpc-fork-result.h
@@ -14,28 +14,16 @@
// with this program (see COPYING). If not, see .
//
-#ifndef CLUA_MACHINE_H
-#define CLUA_MACHINE_H
-
-extern "C" {
-#include
-}
-
-/// \file
-/// \brief Cartesi machine Lua interface
+#ifndef JSONRPC_FORK_RESULT
+#define JSONRPC_FORK_RESULT
namespace cartesi {
-/// \brief Initialize Cartesi machine Lua interface
-/// \param L Lua state
-/// \param ctxidx Index of Clua context
-int clua_machine_init(lua_State *L, int ctxidx);
-
-/// \brief Exports symbols to table on top of Lua stack
-/// \param L Lua state
-/// \param ctxidx Index of Clua context
-int clua_machine_export(lua_State *L, int ctxidx);
+struct fork_result final {
+ std::string address{};
+ uint32_t pid{};
+};
} // namespace cartesi
-#endif
+#endif // JSONRPC_FORK_RESULT
diff --git a/src/jsonrpc-machine-c-api.cpp b/src/jsonrpc-machine-c-api.cpp
index 18abc0dd4..5a8b042fb 100644
--- a/src/jsonrpc-machine-c-api.cpp
+++ b/src/jsonrpc-machine-c-api.cpp
@@ -22,17 +22,12 @@
#include
#include
#include
-#include
#include
#include
-#include
-#include
-#include
#include "access-log.h"
#include "i-virtual-machine.h"
#include "json-util.h"
-#include "jsonrpc-connection.h"
#include "jsonrpc-machine-c-api.h"
#include "jsonrpc-virtual-machine.h"
#include "machine-c-api-internal.h"
@@ -44,436 +39,226 @@
#include "os.h"
#include "semantic-version.h"
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#include "asio-config.h"
-#include
-#include
-#pragma GCC diagnostic pop
-
using namespace std::string_literals;
-static const cartesi::jsonrpc_connection_ptr *convert_from_c(const cm_jsonrpc_connection *con) {
- if (con == nullptr) {
- throw std::invalid_argument("invalid connection");
+static cartesi::jsonrpc_virtual_machine::cleanup_call convert_from_c(cm_jsonrpc_cleanup_call call) {
+ switch (call) {
+ case CM_JSONRPC_DESTROY:
+ return cartesi::jsonrpc_virtual_machine::cleanup_call::destroy;
+ case CM_JSONRPC_SHUTDOWN:
+ return cartesi::jsonrpc_virtual_machine::cleanup_call::shutdown;
+ case CM_JSONRPC_NOTHING:
+ return cartesi::jsonrpc_virtual_machine::cleanup_call::nothing;
+ default:
+ throw std::invalid_argument("invalid cleanup call");
}
- // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- return reinterpret_cast(con);
}
-static cartesi::i_virtual_machine *convert_from_c(cm_machine *m) {
- if (m == nullptr) {
- throw std::invalid_argument("invalid machine");
+static cm_jsonrpc_cleanup_call convert_to_c(cartesi::jsonrpc_virtual_machine::cleanup_call call) {
+ switch (call) {
+ case cartesi::jsonrpc_virtual_machine::cleanup_call::destroy:
+ return CM_JSONRPC_DESTROY;
+ case cartesi::jsonrpc_virtual_machine::cleanup_call::shutdown:
+ return CM_JSONRPC_SHUTDOWN;
+ case cartesi::jsonrpc_virtual_machine::cleanup_call::nothing:
+ return CM_JSONRPC_NOTHING;
+ default:
+ throw std::invalid_argument("invalid cleanup call");
}
- // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- return reinterpret_cast(m);
}
-cm_error cm_jsonrpc_connect(const char *address, int detach_server, cm_jsonrpc_connection **con) try {
- if (address == nullptr) {
- throw std::invalid_argument("invalid address");
- }
- if (con == nullptr) {
- throw std::invalid_argument("invalid connection output");
+static cartesi::jsonrpc_virtual_machine *convert_from_c(cm_machine *m) {
+ if (m == nullptr) {
+ throw std::invalid_argument("invalid machine");
}
- auto *cpp_con =
- new cartesi::jsonrpc_connection_ptr(std::make_shared(address, detach_server));
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- *con = reinterpret_cast(cpp_con);
- return cm_result_success();
-} catch (...) {
- if (con) {
- *con = nullptr;
+ auto *cpp_m = reinterpret_cast(m);
+ if (!cpp_m->is_jsonrpc_virtual_machine()) {
+ throw std::invalid_argument("not a JSONRPC remote machine");
}
- return cm_result_failure();
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ return reinterpret_cast(m);
}
-static boost::asio::ip::tcp::endpoint address_to_endpoint(const std::string &address) {
- try {
- const auto pos = address.find_last_of(':');
- const std::string ip = address.substr(0, pos);
- const int port = std::stoi(address.substr(pos + 1));
- if (port < 0 || port > 65535) {
- throw std::runtime_error{"invalid port"};
- }
- return {boost::asio::ip::make_address(ip), static_cast(port)};
- } catch (std::exception &e) {
- throw std::runtime_error{"invalid endpoint address \"" + address + "\""};
- }
+static const cartesi::jsonrpc_virtual_machine *convert_from_c(const cm_machine *m) {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
+ return convert_from_c(const_cast(m));
}
-static std::string endpoint_to_string(const boost::asio::ip::tcp::endpoint &endpoint) {
- std::ostringstream ss;
- ss << endpoint;
- return ss.str();
+static cm_machine *convert_to_c(cartesi::i_virtual_machine *cpp_m) {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ return reinterpret_cast(cpp_m);
}
-cm_error cm_jsonrpc_spawn_server(const char *address, int detach_server, cm_jsonrpc_connection **con,
- const char **bound_address, int32_t *pid) try {
- // this function first blocks SIGUSR1, SIGUSR2 and SIGALRM.
- // then it double-forks.
- // the grand-child sends the parent a SIGUSR2 and suicides if failed before execing jsonrpc-remote-cartesi-machine.
- // otherwise, jsonrpc-remote-cartesi-machine itself sends the parent a SIGUSR1 to notify it is ready.
- // the parent sets up to receive a SIGALRM after 15 seconds and then waits for SIGUSR1, SIGUSR2 or SIGALRM
- // if it gets SIGALRM, the grand-child is unresponsive, so the parent kills it and cm_jsonrpc_spawn fails.
- // if it gets SIGUSR2, the grand-child failed before exec and suicided, so cm_jsonrpc_spawn fails.
- // if it gets SIGUSR1, jsonrpc-remote-cartesi-machine is ready and cm_jsonrpc_span succeeds.
+cm_error cm_jsonrpc_connect_server(const char *address, cm_machine **new_m) try {
+ using namespace cartesi;
if (address == nullptr) {
throw std::invalid_argument("invalid address");
}
- if (con == nullptr) {
- throw std::invalid_argument("invalid connection output");
- }
- if (bound_address == nullptr) {
- throw std::invalid_argument("invalid bound address output");
- }
- if (pid == nullptr) {
- throw std::invalid_argument("invalid pid output");
- }
- sigset_t mask{};
- sigset_t omask{};
- sigemptyset(&mask); // always returns 0
- sigaddset(&mask, SIGUSR1); // always returns 0
- sigaddset(&mask, SIGUSR2); // always returns 0
- sigaddset(&mask, SIGALRM); // always returns 0
- if (sigprocmask(SIG_BLOCK, &mask, &omask) < 0) {
- // sigprocmask can only fail if we screwed up the values. this can't happen.
- // being paranoid, if it *did* happen, we are trying to avoid a situation where
- // our process gets killed when the grand-child or the alarm tries to signal us
- // and the signals are not blocked
- throw std::system_error{errno, std::generic_category(), "sigprocmask failed"};
- }
- bool restore_sigprocmask = true;
- boost::asio::io_context ioc{1};
- boost::asio::ip::tcp::acceptor a(ioc, address_to_endpoint(address));
- // already done by constructor
- // a.open(endpoint.protocol());
- // a.set_option(asio::socket_base::reuse_address(true));
- // a.bind(endpoint);
- // a.listen(asio::socket_base::max_listen_connections);
- const char *bin = getenv("JSONRPC_REMOTE_CARTESI_MACHINE");
- if (!bin) {
- bin = "jsonrpc-remote-cartesi-machine";
- }
- auto ppid = getpid();
- bool restore_grand_child = false;
- const int32_t grand_child = cartesi::os_double_fork_or_throw(true);
- if (grand_child == 0) { // grand-child and double-fork() succeeded
- sigprocmask(SIG_SETMASK, &omask, nullptr);
- char sigusr1[256] = "";
- (void) snprintf(sigusr1, std::size(sigusr1), "--sigusr1=%d", ppid);
- char server_fd[256] = "";
- (void) snprintf(server_fd, std::size(server_fd), "--server-fd=%d", a.native_handle());
- // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
- char *args[] = {const_cast(bin), server_fd, sigusr1, nullptr};
- if (execvp(bin, args) < 0) {
- // here we failed to run jsonrpc-remote-cartesi-machine. nothing we can do.
- kill(ppid, SIGUSR2); // notify parent as soon as possible that we failed.
- exit(1);
- };
- return cm_result_success(); // code never reaches here
- } else if (grand_child > 0) { // parent and double-fork() succeeded
- restore_grand_child = true; // make sure grand-child is killed if we fail
- static THREAD_LOCAL std::string bound_address_storage = endpoint_to_string(a.local_endpoint());
- a.close();
- struct itimerval ovalue {};
- bool restore_itimer = false;
- try {
- struct itimerval value {};
- memset(&value, 0, sizeof(value));
- value.it_interval.tv_sec = 0;
- value.it_interval.tv_usec = 0;
- value.it_value.tv_sec = 15;
- value.it_value.tv_usec = 0;
- if (setitimer(ITIMER_REAL, &value, &ovalue) < 0) {
- // setitimer only fails if we screwed up with the values. this should not happen.
- // being paranoid, if it *did* happen, and if the grand-child also failed to signal us,
- // we might hang forever in the following call to sigwait.
- // we prefer to give up instead of risking a deadlock.
- throw std::system_error{errno, std::generic_category(), "setitimer failed"};
- }
- restore_itimer = true;
- int sig = 0;
- if (auto ret = sigwait(&mask, &sig); ret != 0) {
- throw std::system_error{ret, std::generic_category(), "sigwait failed"};
- }
- if (sig == SIGALRM) { // grand-child didn't signal us before alarm
- throw std::runtime_error{"grand-child process unresponsive"};
- }
- if (sig == SIGUSR2) { // grand-child signaled us that it failed to exec
- // grand-child will have exited on its own
- restore_grand_child = false;
- throw std::runtime_error{"failed to run '"s + bin + "'"s};
- }
- // grand-child signaled us that everything is fine
- assert(sig == SIGUSR1);
- setitimer(ITIMER_REAL, &ovalue, nullptr);
- restore_itimer = false;
- sigprocmask(SIG_SETMASK, &omask, nullptr);
- restore_sigprocmask = false;
- *bound_address = bound_address_storage.c_str();
- *pid = grand_child;
- auto ret = cm_jsonrpc_connect(*bound_address, detach_server, con);
- if (ret < 0) { // and yet we failed to connect
- kill(grand_child, SIGTERM);
- *bound_address = nullptr;
- *pid = 0;
- }
- return ret;
- } catch (...) {
- if (restore_sigprocmask) {
- sigprocmask(SIG_SETMASK, &omask, nullptr);
- }
- if (restore_grand_child) {
- kill(grand_child, SIGTERM);
- }
- if (restore_itimer) {
- setitimer(ITIMER_REAL, &ovalue, nullptr);
- }
- *con = nullptr;
- *bound_address = nullptr;
- *pid = 0;
- return cm_result_failure();
- }
- }
- return cm_result_success(); // code never reaches here
-} catch (...) {
- *con = nullptr;
- *bound_address = nullptr;
- *pid = 0;
- return cm_result_failure();
-}
-
-void cm_jsonrpc_release_connection(const cm_jsonrpc_connection *con) {
- if (con == nullptr) {
- return;
- }
- // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- const auto *cpp_con = reinterpret_cast(con);
- delete cpp_con;
-}
-
-cm_error cm_jsonrpc_create_machine(const cm_jsonrpc_connection *con, int detach_machine, const char *config,
- const char *runtime_config, cm_machine **new_machine) try {
- if (new_machine == nullptr) {
+ if (new_m == nullptr) {
throw std::invalid_argument("invalid new machine output");
}
- const cartesi::machine_config c = cartesi::from_json(config);
- cartesi::machine_runtime_config r;
- if (runtime_config) {
- r = cartesi::from_json(runtime_config);
- }
- const auto *cpp_con = convert_from_c(con);
- // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- *new_machine = reinterpret_cast(new cartesi::jsonrpc_virtual_machine(*cpp_con, detach_machine, c, r));
+ *new_m = convert_to_c(new jsonrpc_virtual_machine(address));
return cm_result_success();
} catch (...) {
- if (new_machine) {
- *new_machine = nullptr;
+ if (new_m) {
+ *new_m = nullptr;
}
return cm_result_failure();
}
-cm_error cm_jsonrpc_load_machine(const cm_jsonrpc_connection *con, int detach_machine, const char *dir,
- const char *runtime_config, cm_machine **new_machine) try {
- if (new_machine == nullptr) {
+cm_error cm_jsonrpc_spawn_server(const char *address, cm_machine **new_m, const char **bound_address,
+ uint32_t *pid) try {
+ using namespace cartesi;
+ if (address == nullptr) {
+ throw std::invalid_argument("invalid address");
+ }
+ if (new_m == nullptr) {
throw std::invalid_argument("invalid new machine output");
}
- if (dir == nullptr) {
- throw std::invalid_argument("invalid dir");
+ fork_result spawned;
+ *new_m = convert_to_c(new jsonrpc_virtual_machine(address, spawned));
+ if (bound_address) {
+ *bound_address = cm_set_temp_string(spawned.address);
}
- cartesi::machine_runtime_config r;
- if (runtime_config) {
- r = cartesi::from_json(runtime_config);
+ if (pid) {
+ *pid = spawned.pid;
}
- const auto *cpp_con = convert_from_c(con);
- *new_machine =
- // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- reinterpret_cast(new cartesi::jsonrpc_virtual_machine(*cpp_con, detach_machine, dir, r));
return cm_result_success();
} catch (...) {
- if (new_machine) {
- *new_machine = nullptr;
+ if (new_m) {
+ *new_m = nullptr;
}
- return cm_result_failure();
-}
-
-cm_error cm_jsonrpc_destroy_machine(cm_machine *m) try {
- if (m != nullptr) {
- auto *cpp_machine = convert_from_c(m);
- cpp_machine->destroy();
- delete cpp_machine;
+ if (bound_address) {
+ *bound_address = nullptr;
+ }
+ if (pid) {
+ *pid = 0;
}
- return cm_result_success();
-} catch (...) {
return cm_result_failure();
}
-cm_error cm_jsonrpc_get_machine(const cm_jsonrpc_connection *con, int detach_machine, cm_machine **new_machine) try {
- if (new_machine == nullptr) {
- throw std::invalid_argument("invalid new machine output");
+cm_error cm_jsonrpc_fork_server(const cm_machine *m, cm_machine **forked_m, const char **address, uint32_t *pid) try {
+ using namespace cartesi;
+ if (address == nullptr) {
+ throw std::invalid_argument("invalid address output");
}
- const auto *cpp_con = convert_from_c(con);
- // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- *new_machine = reinterpret_cast(new cartesi::jsonrpc_virtual_machine(*cpp_con, detach_machine));
+ const auto *cpp_m = convert_from_c(m);
+ const auto forked = cpp_m->fork_server();
+ *address = cm_set_temp_string(forked.address);
+ if (pid) {
+ *pid = static_cast(forked.pid);
+ }
+ *forked_m = convert_to_c(new jsonrpc_virtual_machine(forked.address));
return cm_result_success();
} catch (...) {
- if (new_machine) {
- *new_machine = nullptr;
+ if (address) {
+ *address = nullptr;
+ }
+ if (pid) {
+ *pid = 0;
}
return cm_result_failure();
}
-CM_API cm_error cm_jsonrpc_get_connection(cm_machine *m, const cm_jsonrpc_connection **con) try {
- auto *cpp_machine = convert_from_c(m);
- cartesi::jsonrpc_virtual_machine *cpp_json_machine = dynamic_cast(cpp_machine);
- if (!cpp_json_machine) {
- throw std::invalid_argument("not a remote machine");
+cm_error cm_jsonrpc_rebind_server(cm_machine *m, const char *address, const char **address_bound) try {
+ auto *cpp_m = convert_from_c(m);
+ const auto cpp_address_bound = cpp_m->rebind_server(address);
+ if (address_bound) {
+ *address_bound = cm_set_temp_string(cpp_address_bound);
}
- auto *cpp_con = new cartesi::jsonrpc_connection_ptr(cpp_json_machine->get_connection());
- // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- *con = reinterpret_cast(cpp_con);
return cm_result_success();
} catch (...) {
- *con = nullptr;
+ if (address_bound) {
+ *address_bound = nullptr;
+ }
return cm_result_failure();
}
-cm_error cm_jsonrpc_get_default_config(const cm_jsonrpc_connection *con, const char **config) try {
- if (config == nullptr) {
- throw std::invalid_argument("invalid config output");
+cm_error cm_jsonrpc_get_server_version(const cm_machine *m, const char **version) try {
+ if (version == nullptr) {
+ throw std::invalid_argument("invalid version output");
}
- const auto *cpp_con = convert_from_c(con);
- const cartesi::machine_config cpp_config = cartesi::jsonrpc_virtual_machine::get_default_config(*cpp_con);
- *config = cm_set_temp_string(cartesi::to_json(cpp_config).dump());
+ const auto *cpp_m = convert_from_c(m);
+ const auto cpp_version = cpp_m->get_server_version();
+ *version = cm_set_temp_string(cartesi::to_json(cpp_version).dump());
return cm_result_success();
} catch (...) {
- if (config) {
- *config = nullptr;
+ if (version) {
+ *version = nullptr;
}
return cm_result_failure();
}
-cm_error cm_jsonrpc_verify_step_uarch(const cm_jsonrpc_connection *con, const cm_hash *root_hash_before,
- const char *log, const cm_hash *root_hash_after) try {
- if (log == nullptr) {
- throw std::invalid_argument("invalid access log");
- }
- const auto *cpp_con = convert_from_c(con);
- const auto cpp_log = // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
- cartesi::from_json>(log).value();
- const cartesi::machine::hash_type cpp_root_hash_before = convert_from_c(root_hash_before);
- const cartesi::machine::hash_type cpp_root_hash_after = convert_from_c(root_hash_after);
- cartesi::jsonrpc_virtual_machine::verify_step_uarch(*cpp_con, cpp_root_hash_before, cpp_log, cpp_root_hash_after);
+cm_error cm_jsonrpc_shutdown_server(cm_machine *m) try {
+ auto *cpp_m = convert_from_c(m);
+ cpp_m->shutdown_server();
return cm_result_success();
} catch (...) {
return cm_result_failure();
}
-cm_error cm_jsonrpc_verify_reset_uarch(const cm_jsonrpc_connection *con, const cm_hash *root_hash_before,
- const char *log, const cm_hash *root_hash_after) try {
- if (log == nullptr) {
- throw std::invalid_argument("invalid access log");
- }
- const auto *cpp_con = convert_from_c(con);
- const auto cpp_log = // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
- cartesi::from_json>(log).value();
- const cartesi::machine::hash_type cpp_root_hash_before = convert_from_c(root_hash_before);
- const cartesi::machine::hash_type cpp_root_hash_after = convert_from_c(root_hash_after);
- cartesi::jsonrpc_virtual_machine::verify_reset_uarch(*cpp_con, cpp_root_hash_before, cpp_log, cpp_root_hash_after);
+cm_error cm_jsonrpc_delay_next_request(cm_machine *m, uint64_t ms) try {
+ auto *cpp_m = convert_from_c(m);
+ cpp_m->delay_next_request(ms);
return cm_result_success();
} catch (...) {
return cm_result_failure();
}
-cm_error cm_jsonrpc_fork_server(const cm_jsonrpc_connection *con, const char **address, int32_t *pid) try {
- if (address == nullptr) {
- throw std::invalid_argument("invalid address output");
- }
- const auto *cpp_con = convert_from_c(con);
- const auto result = (*cpp_con)->fork_server();
- *address = cm_set_temp_string(result.address);
- if (pid) {
- *pid = static_cast(result.pid);
- }
+cm_error cm_jsonrpc_set_timeout(cm_machine *m, int64_t ms) try {
+ auto *cpp_m = convert_from_c(m);
+ cpp_m->set_timeout(ms);
return cm_result_success();
} catch (...) {
- if (address) {
- *address = nullptr;
- }
- if (pid) {
- *pid = 0;
- }
return cm_result_failure();
}
-cm_error cm_jsonrpc_rebind_server(const cm_jsonrpc_connection *con, const char *address, const char **new_address) try {
- const auto *cpp_con = convert_from_c(con);
- const std::string cpp_new_address = (*cpp_con)->rebind_server(address);
- if (new_address) {
- *new_address = cm_set_temp_string(cpp_new_address);
+cm_error cm_jsonrpc_get_timeout(cm_machine *m, int64_t *ms) try {
+ if (ms == nullptr) {
+ throw std::invalid_argument("invalid ms output");
}
+ auto *cpp_m = convert_from_c(m);
+ *ms = cpp_m->get_timeout();
return cm_result_success();
} catch (...) {
- if (new_address) {
- *new_address = nullptr;
- }
return cm_result_failure();
}
-cm_error cm_jsonrpc_get_reg_address(const cm_jsonrpc_connection *con, cm_reg reg, uint64_t *val) try {
- if (val == nullptr) {
- throw std::invalid_argument("invalid val output");
- }
- const auto *cpp_con = convert_from_c(con);
- const auto cpp_reg = static_cast(reg);
- *val = cartesi::jsonrpc_virtual_machine::get_reg_address(*cpp_con, cpp_reg);
+cm_error cm_jsonrpc_set_cleanup_call(cm_machine *m, cm_jsonrpc_cleanup_call call) try {
+ auto *cpp_m = convert_from_c(m);
+ auto cpp_call = convert_from_c(call);
+ cpp_m->set_cleanup_call(cpp_call);
return cm_result_success();
} catch (...) {
- if (val) {
- *val = 0;
- }
return cm_result_failure();
}
-cm_error cm_jsonrpc_get_server_version(const cm_jsonrpc_connection *con, const char **version) try {
- if (version == nullptr) {
- throw std::invalid_argument("invalid version output");
+cm_error cm_jsonrpc_get_cleanup_call(cm_machine *m, cm_jsonrpc_cleanup_call *call) try {
+ if (call == nullptr) {
+ throw std::invalid_argument("invalid call output");
}
- const auto *cpp_con = convert_from_c(con);
- const cartesi::semantic_version cpp_version = (*cpp_con)->get_server_version();
- *version = cm_set_temp_string(cartesi::to_json(cpp_version).dump());
+ auto *cpp_m = convert_from_c(m);
+ *call = convert_to_c(cpp_m->get_cleanup_call());
return cm_result_success();
} catch (...) {
- if (version) {
- *version = nullptr;
- }
return cm_result_failure();
}
-cm_error cm_jsonrpc_shutdown_server(const cm_jsonrpc_connection *con) try {
- if (con != nullptr) {
- const auto *cpp_con = convert_from_c(con);
- (*cpp_con)->shutdown_server();
- delete cpp_con;
+cm_error cm_jsonrpc_get_server_address(cm_machine *m, const char **address) try {
+ if (address == nullptr) {
+ throw std::invalid_argument("invalid address output");
}
+ auto *cpp_m = convert_from_c(m);
+ *address = cm_set_temp_string(cpp_m->get_server_address());
return cm_result_success();
} catch (...) {
return cm_result_failure();
}
-cm_error cm_jsonrpc_verify_send_cmio_response(const cm_jsonrpc_connection *con, uint16_t reason, const uint8_t *data,
- uint64_t length, const cm_hash *root_hash_before, const char *log, const cm_hash *root_hash_after) try {
- if (log == nullptr) {
- throw std::invalid_argument("invalid access log");
- }
- const auto *cpp_con = convert_from_c(con);
- const auto cpp_log = // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
- cartesi::from_json>(log).value();
- const cartesi::machine::hash_type cpp_root_hash_before = convert_from_c(root_hash_before);
- const cartesi::machine::hash_type cpp_root_hash_after = convert_from_c(root_hash_after);
- cartesi::jsonrpc_virtual_machine::verify_send_cmio_response(*cpp_con, reason, data, length, cpp_root_hash_before,
- cpp_log, cpp_root_hash_after);
+cm_error cm_jsonrpc_emancipate_server(cm_machine *m) try {
+ auto *cpp_m = convert_from_c(m);
+ cpp_m->emancipate_server();
return cm_result_success();
} catch (...) {
return cm_result_failure();
diff --git a/src/jsonrpc-machine-c-api.h b/src/jsonrpc-machine-c-api.h
index f87f0fafc..abfbf43ef 100644
--- a/src/jsonrpc-machine-c-api.h
+++ b/src/jsonrpc-machine-c-api.h
@@ -24,189 +24,148 @@ extern "C" {
#endif
// -----------------------------------------------------------------------------
-// API enums and structures
+// API enums
// -----------------------------------------------------------------------------
-/// \brief Handle of the JSONRPC connection.
-/// \details It's used only as an opaque handle to pass JSONRPC connection through the C API.
-typedef struct cm_jsonrpc_connection cm_jsonrpc_connection;
+/// \brief Resources to cleanup when machine object is deleted
+typedef enum cm_jsonrpc_cleanup_call {
+ CM_JSONRPC_NOTHING, ///< Just delete object
+ CM_JSONRPC_DESTROY, ///< Implicitly call cm_destroy()
+ CM_JSONRPC_SHUTDOWN ///< Implicitly call cm_jsonrpc_shutdown_server()
+} cm_jsonrpc_cleanup_call;
// -----------------------------------------------------------------------------
-// API functions
+// Server API functions
// -----------------------------------------------------------------------------
-// ------------------------------------
-// Remote server management
-// ------------------------------------
-
-/// \brief Connects to an existing JSONRPC remote machine server.
+/// \brief Spawns a new remote machine server.
+/// \param address Address (in local host) to bind the new remote machine server.
+/// \param new_m Receives the pointer to the new JSONRPC remote machine object. Set to NULL on failure.
+/// \param bound_address_bound Receives the address that the remote machine server actually bound to,
+/// guaranteed to remain valid only until the next CM_API function is called again on the same thread.
+/// Set to NULL on failure.
+/// \param pid Receives the spawned server process id. Set to 0 on failure.
+/// \returns 0 for success, non zero code for error.
+/// \details A newly spawned remote machine server does not hold a machine instance.
+/// Use cm_create() or cm_load() to instantiate a machine into the object.
+/// Use cm_delete() to delete the object.
+/// \details The spawned process is in the process group of the caller.
+/// Use cm_jsonrpc_emancipate_server() to make it leader of its own process group.
+/// \details The machine object is not configured to implicitly cleanup anything on cm_delete().
+/// Use cm_jsonrpc_set_cleanup_call() to change this setting.
+/// \details Unless the desired jsonrpc-remote-cartesi-machine executable is in the path,
+/// the environment variable JSONRPC_REMOTE_CARTESI_MACHINE must point directly to the executable.
+CM_API cm_error cm_jsonrpc_spawn_server(const char *address, cm_machine **new_m, const char **bound_address,
+ uint32_t *pid);
+
+/// \brief Connects to an existing remote machine server.
/// \param address Address of the remote machine server to connect to.
-/// \param detach_server When non-zero, do not implicitly call
-/// cm_jsonrpc_shutdown_server() server on cm_jsonrpc_release_connection().
-/// \param con If function succeeds, receives new JSONRPC connection. Set to NULL on failure.
-/// \returns 0 for success, non zero code for error.
-CM_API cm_error cm_jsonrpc_connect(const char *address, int detach_server, cm_jsonrpc_connection **con);
-
-/// \brief Spawns a new JSONRPC remote machine server and connect to it.
-/// \param address Address (in local host) to bind the new JSONRPC remote machine server.
-/// \param detach_server When non-zero, do not implicitly call
-/// cm_jsonrpc_shutdown_server() server on cm_jsonrpc_release_connection().
-/// \param con If function succeeds, receives new JSONRPC connection. Set to NULL on failure.
-/// \param bound_address_bound Receives the address that the remote server
-/// actually bound to, guaranteed to remain valid only until the next CM_API
-/// function is called again on the same thread. Set to NULL on failure.
-/// \param pid If function suceeds, receives the forked child process id. Set to 0 on failure.
-/// \details If the jsonrpc-remote-cartesi-machine executable is not in the path,
-/// the environment variable JSONRPC_REMOTE_CARTESI_MACHINE should point to the executable.
-CM_API cm_error cm_jsonrpc_spawn_server(const char *address, int detach_server, cm_jsonrpc_connection **con,
- const char **bound_address, int32_t *pid);
-
-/// \brief Releases a reference to a JSONRPC connection to remote machine server.
-/// \param con Pointer a JSONRPC connection (can be NULL).
-/// \returns 0 for success, non zero code for error.
-/// \details When the last reference to the connection is released, if the
-/// server is not detached, implicitly call cm_jsonrpc_shutdown_server() to
-/// shutdown the server. This might fail silently. To know if the shutdown of
-/// a server was successful, call cm_jsonrpc_shutdown_server() explicitly.
-/// The connection pointer must not be used after this call.
-CM_API void cm_jsonrpc_release_connection(const cm_jsonrpc_connection *con);
-
-/// \brief Forks the remote server.
-/// \param con Pointer to a valid JSONRPC connection.
-/// \param address If function succeeds, receives address of new server, guaranteed to remain valid
-/// only until the next CM_API function is called again on the same thread. Set to NULL on failure.
+/// \param new_m Receives the pointer to the new JSONRPC remote machine object. Set to NULL on failure.
+/// \returns 0 for success, non zero code for error.
+/// \details The machine object is not configured to implicitly cleanup anything on cm_delete().
+/// Use cm_jsonrpc_set_cleanup_call() to change this setting.
+/// \details If the remote machine server already holds a machine instance, it is ready for use.
+/// Otherwise, use cm_create() or cm_load() to instantiate a machine into the object.
+/// Use cm_delete() to delete the object.
+CM_API cm_error cm_jsonrpc_connect_server(const char *address, cm_machine **new_m);
+
+/// \brief Forks the remote machine server.
+/// \param m Pointer to a valid JSONRPC remote machine object.
+/// \param forked_m Receives the pointer to the forked JSONRPC remote machine object. Set to NULL on failure.
+/// \param address If function succeeds, receives address the forked server bound to,
+/// guaranteed to remain valid only until the next CM_API function is called again on the same thread.
+/// Set to NULL on failure.
/// \param pid If function suceeds, receives the forked child process id (can be NULL). Set to 0 on failure.
/// \returns 0 for success, non zero code for error.
-CM_API cm_error cm_jsonrpc_fork_server(const cm_jsonrpc_connection *con, const char **address, int32_t *pid);
-
-/// \brief Shutdowns remote server.
-/// \param con Pointer to a valid JSONRPC connection (can be NULL).
-/// \returns 0 for success, non zero code for error.
-/// \details If the call succeeds, the pointer to the JSONRPC connection must not be used after this call.
-CM_API cm_error cm_jsonrpc_shutdown_server(const cm_jsonrpc_connection *con);
-
-/// \brief Changes the address the remote server is listening to.
-/// \param con Pointer to a valid JSONRPC connection.
-/// \param address Address the remote server should bind to.
-/// \param address_bound Receives the address that the remote server actually bound to, guaranteed to remain valid
-/// only until the next CM_API function is called again on the same thread. Set to NULL on failure.
-/// \returns 0 for success, non zero code for error.
-CM_API cm_error cm_jsonrpc_rebind_server(const cm_jsonrpc_connection *con, const char *address,
- const char **address_bound);
-
-/// \brief Gets the semantic version of the remote server.
-/// \param con Pointer to a valid JSONRPC connection.
+/// \details If the remote machine server already holds a machine instance, the forked copy is ready for use.
+/// Otherwise, use cm_create() or cm_load() to instantiate a machine into the forked server object.
+/// Use cm_delete() to delete the object.
+/// \details The forked process is in the process group of the remote server.
+/// Use cm_jsonrpc_emancipate_server() to make it leader of its own process group.
+/// \details The machine object is not configured to implicitly cleanup anything on cm_delete().
+/// Use cm_jsonrpc_set_cleanup_call() to change this setting.
+/// \warning If the server is running on a remote host, the \p pid is also remote and cannot be signaled.
+/// Trying to do so may signal an entirely unrelated process in the local host.
+CM_API cm_error cm_jsonrpc_fork_server(const cm_machine *m, cm_machine **forked_m, const char **address, uint32_t *pid);
+
+/// \brief Shuts down the remote machine server.
+/// \param m Pointer to a valid JSONRPC remote machine object.
+/// \returns 0 for success, non zero code for error.
+/// \details cm_delete() may fail silently when implicitly calling cm_jsonrpc_shutdown_server().
+/// To make sure the server was successfully shutdown, call cm_jsonrpc_shutdown_server() explicitly.
+/// \details This function does not delete the machine object.
+/// You must still call cm_delete() afterwards.
+CM_API cm_error cm_jsonrpc_shutdown_server(cm_machine *m);
+
+/// \brief Changes the address the remote machine server is listening to.
+/// \param m Pointer to a valid JSONRPC remote machine object.
+/// \param address Address the remote machine server should bind to.
+/// \param address_bound Receives the address that the remote machine server actually bound to,
+/// guaranteed to remain valid only until the next CM_API function is called again on the same thread.
+/// Set to NULL on failure.
+/// \returns 0 for success, non zero code for error.
+/// \detail The function automatically updates the address the machine object uses to communicate with the server.
+CM_API cm_error cm_jsonrpc_rebind_server(cm_machine *m, const char *address, const char **address_bound);
+
+/// \brief Gets the semantic version of the remote machine server.
+/// \param m Pointer to a valid JSONRPC remote machine object.
/// \param semantic_version Receives the semantic version as a JSON object in a string,
-/// guaranteed to remain valid only until the next CM_API function is called again on the
-/// same thread. Set to NULL on failure.
+/// guaranteed to remain valid only until the next CM_API function is called again on the same thread.
+/// Set to NULL on failure.
/// \returns 0 for success, non zero code for error.
-CM_API cm_error cm_jsonrpc_get_server_version(const cm_jsonrpc_connection *con, const char **version);
+CM_API cm_error cm_jsonrpc_get_server_version(const cm_machine *m, const char **version);
-/// \brief Returns a JSON object in a string with the default machine config from the remote server.
-/// \param con Pointer to a valid JSONRPC connection.
-/// \param config Receives the default configuration, guaranteed to remain valid only until
-/// the next CM_API function is called again on the same thread.
-/// \returns 0 for success, non zero code for error.
-/// \details The returned config is not sufficient to run a machine.
-/// Additional configurations, such as RAM length, RAM image, flash drives,
-/// and entrypoint are still needed.
-CM_API cm_error cm_jsonrpc_get_default_config(const cm_jsonrpc_connection *con, const char **config);
-
-/// \brief Gets the address of any x, f, or control state register from the remote server.
-/// \param con Pointer to a valid JSONRPC connection.
-/// \param reg The register.
-/// \param val Receives address of the register.
-/// \returns 0 for success, non zero code for error.
-CM_API cm_error cm_jsonrpc_get_reg_address(const cm_jsonrpc_connection *con, cm_reg reg, uint64_t *val);
-
-// ------------------------------------
-// Machine API functions
-// ------------------------------------
-
-/// \brief Creates a remote machine instance.
-/// \param con Pointer to a valid JSONRPC connection.
-/// \param detach_machine When non-zero, cm_destroy() does not attempt to destroy the remote machine.
-/// \param config Machine configuration as a JSON string.
-/// \param runtime_config Machine runtime configuration as a JSON string (can be NULL).
-/// \param m Receives the pointer to new remote machine instance.
+/// \brief Breaks server out of parent program group.
+/// \param m Pointer to a valid JSONRPC remote machine object.
/// \returns 0 for success, non zero code for error.
-/// \details The machine instance holds its own reference to the connection.
-/// \details cm_destroy() might fail silently when attempting to destroy an
-/// attached remote machine. To make sure the destruction of a remote machine was
-/// successful, call cm_jsonrpc_destroy_machine() instead.
-CM_API cm_error cm_jsonrpc_create_machine(const cm_jsonrpc_connection *con, int detach_machine, const char *config,
- const char *runtime_config, cm_machine **m);
-
-/// \brief Creates a remote machine instance from previously stored directory in the remote server.
-/// \param con Pointer to a valid JSONRPC connection.
-/// \param detach_machine When non-zero, cm_destroy() does not attempt to destroy the remote machine.
-/// \param dir Directory where previous machine is stored.
-/// \param runtime_config Machine runtime configuration as a JSON string (can be NULL).
-/// \param m Receives the pointer to new remote machine instance.
-/// \returns 0 for success, non zero code for error.
-/// \details The machine instance holds its own reference to the connection.
-/// cm_destroy() might fail silently when attempting to destroy an attached
-/// remote machine. To make sure the destruction of a remote machine was
-/// successful, call cm_jsonrpc_destroy_machine() instead.
-CM_API cm_error cm_jsonrpc_load_machine(const cm_jsonrpc_connection *con, int detach_machine, const char *dir,
- const char *runtime_config, cm_machine **m);
-
-/// \brief Destroy a machine, whether it is attached or not, and releases handle.
-/// \param m Pointer to the existing machine handle (can be NULL).
-/// \returns 0 for success, non zero code for error.
-/// \details Local machines are always destroyed sucessfully.
-/// In contrast, the attempt to destroy a remote machine might fail.
-/// If the call succeeds, the machine handle must not be used after this call.
-CM_API cm_error cm_jsonrpc_destroy_machine(cm_machine *m);
-
-/// \brief Get remote machine instance that was previously created in the remote server.
-/// \param con Pointer to a valid JSONRPC connection.
-/// \param detach_machine When non-zero, do not implicitly call cm_destroy_machine() on cm_release_machine().
-/// \param m Receives the pointer to remote machine instance.
+/// \detail A spawned/forked server process starts in the same process group as its parent.
+/// 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.
-/// \details The machine instance holds its own reference to the connection.
-CM_API cm_error cm_jsonrpc_get_machine(const cm_jsonrpc_connection *con, int detach_machine, cm_machine **m);
+CM_API cm_error cm_jsonrpc_delay_next_request(cm_machine *m, uint64_t ms);
+
+// -----------------------------------------------------------------------------
+// Client API functions
+// -----------------------------------------------------------------------------
-/// \brief Get new reference to a connection to JSONRPC server given a remote machine instance.
-/// \param m Pointer to a valid machine instance.
-/// \param con Receives the pointer to JSONRPC connection. Set to NULL on failure.
+/// \brief Sets a timeout for communication with remote machine server.
+/// \param m Pointer to a valid JSONRPC remote machine object.
+/// \param ms Number of milliseconds to wait before returning with a timeout. Use -1 to block indefinitely.
/// \returns 0 for success, non zero code for error.
-CM_API cm_error cm_jsonrpc_get_connection(cm_machine *m, const cm_jsonrpc_connection **con);
+CM_API cm_error cm_jsonrpc_set_timeout(cm_machine *m, int64_t ms);
-// ------------------------------------
-// Verifying
-// ------------------------------------
+/// \brief Gets the current timeout for communication with remote machine server.
+/// \param m Pointer to a valid JSONRPC remote machine object.
+/// \param ms Receives the number of milliseconds to wait before returning with a timeout. (-1 blocks indefinitely).
+/// \returns 0 for success, non zero code for error.
+CM_API cm_error cm_jsonrpc_get_timeout(cm_machine *m, int64_t *ms);
-/// \brief Checks the validity of a state transition produced by cm_log_step_uarch.
-/// \param con Pointer to a valid JSONRPC connection.
-/// \param root_hash_before State hash before load.
-/// \param log State access log to be verified as a JSON string.
-/// \param root_hash_after State hash after load.
+/// \brief Configures the implicit cleanup call at object deletion.
+/// \param m Pointer to a valid JSONRPC remote machine object.
+/// \param call If set to CM_JSONRPC_DESTROY, implicitly call cm_destroy() on cm_delete().
+/// If set to CM_JSONRPC_SHUTDOWN, implicitly call cm_jsonrpc_shutdown_server() on cm_delete().
+/// Otherwise (i.e., CM_JSONRPC_NOTHING), simply delete object on cm_delete().
+/// This is the default behavior.
/// \returns 0 for success, non zero code for error.
-CM_API cm_error cm_jsonrpc_verify_step_uarch(const cm_jsonrpc_connection *con, const cm_hash *root_hash_before,
- const char *log, const cm_hash *root_hash_after);
-
-/// \brief Checks the validity of a state transition produced by cm_log_verify_reset_uarch.
-/// \param con Pointer to a valid JSONRPC connection.
-/// \param root_hash_before State hash before load.
-/// \param log State access log to be verified as a JSON string.
-/// \param root_hash_after State hash after load.
+CM_API cm_error cm_jsonrpc_set_cleanup_call(cm_machine *m, cm_jsonrpc_cleanup_call call);
+
+/// \brief Retrieves the implicit cleanup call at object is deletion.
+/// \param m Pointer to a valid JSONRPC remote machine object.
+/// \param call Receives either CM_JSONRPC_NOTHING, CM_JSONRPC_DESTROY, or CM_JSONRPC_SHUTDOWN.
+/// See cm_jsonrpc_set_cleanup_call().
/// \returns 0 for success, non zero code for error.
-CM_API cm_error cm_jsonrpc_verify_reset_uarch(const cm_jsonrpc_connection *con, const cm_hash *root_hash_before,
- const char *log, const cm_hash *root_hash_after);
-
-/// \brief Checks the validity of a state transition produced by cm_log_send_cmio_response.
-/// \param con Pointer to a valid JSONRPC connection.
-/// \param reason Reason for sending the response.
-/// \param data The response sent when the log was generated.
-/// \param length Length of response.
-/// \param root_hash_before State hash before load.
-/// \param log State access log to be verified as a JSON string.
-/// \param root_hash_after State hash after load.
+CM_API cm_error cm_jsonrpc_get_cleanup_call(cm_machine *m, cm_jsonrpc_cleanup_call *call);
+
+/// \brief Retrieves the address of remote server.
+/// \param m Pointer to a valid JSONRPC remote machine object.
+/// \param address Receives the address of the remote machine, guaranteed to remain valid only until
+/// the next CM_API function is called again on the same thread.
/// \returns 0 for success, non zero code for error.
-CM_API cm_error cm_jsonrpc_verify_send_cmio_response(const cm_jsonrpc_connection *con, uint16_t reason,
- const uint8_t *data, uint64_t length, const cm_hash *root_hash_before, const char *log,
- const cm_hash *root_hash_after);
+CM_API cm_error cm_jsonrpc_get_server_address(cm_machine *m, const char **address);
#ifdef __cplusplus
}
diff --git a/src/jsonrpc-remote-machine.cpp b/src/jsonrpc-remote-machine.cpp
index 7f6b88207..5432ddb4c 100644
--- a/src/jsonrpc-remote-machine.cpp
+++ b/src/jsonrpc-remote-machine.cpp
@@ -16,6 +16,7 @@
#include