Skip to content

Commit

Permalink
refactor(!): simplify local/remote machine APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
diegonehab committed Dec 3, 2024
1 parent ca702b7 commit 8480d79
Show file tree
Hide file tree
Showing 42 changed files with 3,118 additions and 3,386 deletions.
5 changes: 2 additions & 3 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ INTERPRET_CXXFLAGS+=-DNDEBUG # disable asserts only for interpret.cpp
else ifeq ($(release),yes)
OPTFLAGS+=-O2
DEFS+=-DNDEBUG # disable all asserts
DEFS+=-DBOOST_ASIO_DISABLE_ERROR_LOCATION # remove line/function location from error messages
else ifeq ($(debug),yes)
OPTFLAGS+=-Og -g -fno-omit-frame-pointer
OPTFLAGS+=-D_FORTIFY_SOURCE=3 -D_GLIBCXX_ASSERTIONS -fstack-clash-protection -fstack-protector-strong
Expand Down Expand Up @@ -378,14 +379,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:= \
Expand All @@ -401,14 +400,14 @@ 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 \
uarch-pristine-hash.o

LUACARTESI_JSONRPC_OBJS:= \
clua-cartesi-jsonrpc.o \
clua-jsonrpc-machine.o \
$(CARTESI_CLUA_OBJS)

JSONRPC_REMOTE_CARTESI_MACHINE_OBJS:= \
Expand Down
156 changes: 90 additions & 66 deletions src/cartesi-machine.lua
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,13 @@ where options are:
--version-json
display cartesi machine semantic version and exit.
--remote-address=<address>
use a remote cartesi machine listening to <address> instead of
--remote-address=<ip>:<port>
use a remote cartesi machine listening to <ip>:<port> 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.
Expand Down Expand Up @@ -509,13 +512,13 @@ where options are:
--append-entrypoint-file=<filename>
like --append-entrypoint, but read contents from a file.
--gdb[=<address>]
listen at <address> and wait for a GDB connection to debug the machine.
if <address> is omitted, '127.0.0.1:1234' is used by default.
--gdb[=<ip>:<port>]
listen at <ip>:<port> and wait for a GDB connection to debug the machine.
if <ip>:<port> 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 <address>" [elf]
gdb -ex "set arch riscv:rv64" -ex "target remote <ip>:<port>" [elf]
elf (optional)
the binary elf file with symbols and debugging information
Expand All @@ -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).
<address> is one of the following formats:
<host>:<port>
unix:<path>
<host> 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" }
Expand Down Expand Up @@ -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,
},
Expand Down Expand Up @@ -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,
},
Expand All @@ -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 = {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1782,14 +1785,17 @@ 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, "")
out:write("\n")
end
end

-- obtain config from instantiated machine
local main_config = main_machine:get_initial_config()

if type(store_config) == "string" then
local f <close> = assert(io.open(store_config, "w"))
serialize_config(f, main_config, "lua")
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 <close> = setmetatable({}, { __close = close_snapshot })
local backup_closer <close> = 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
Expand Down Expand Up @@ -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)
Loading

0 comments on commit 8480d79

Please sign in to comment.