Cartesi's reference off-chain implementation of Cartesi Machines is based on software emulation.
The emulator is written in C/C++ with POSIX dependencies restricted to the terminal, process, and memory-mapping facilities.
The emulator/
directory in the Emulator SDK can be used to build and install the Cartesi Machine emulator.
It is written as a C++ library, but can be accessed in a variety of different ways.
When linked to a C++ application, the emulator can be controlled directly via the interface of the cartesi::machine
class.
C applications can control the emulator in a similar way, by means of a matching C API.
The emulator can also be accessed from the Lua programming language, via a cartesi
module that exposes a cartesi.machine
interface to Lua programs.
Additionally, Cartesi provides a gRPC server that can be run to control a Cartesi Machine instance remotely.
Finally, there is a command-line utility (written in Lua) that can configure and run Cartesi Machines for rapid prototyping.
The C, C++, Lua APIs as well as the command-line utility can seamlessly instantiate local emulators or connect to remote gRPC servers.
The documentation starts from the command-line utility, cartesi-machine
.
This utility is used for most prototyping tasks.
The documentation then covers the Lua interface of cartesi.machine
.
The C/C++/gRPC interfaces are very similar, and are covered only within their reference manuals.
Cartesi Machines are separated into a processor and a board. The processor performs the computations, executing the traditional fetch-execute loop while maintaining a variety of registers. The board defines the surrounding environment with an assortment of memories (ROM, RAM, flash drives) and a number of devices. Memories and devices are mapped to the 64-bit physical address space of the Cartesi Machine. The amount of RAM, as well as the number, length, and position of the flash drives in the address space can be chosen according to the needs of each particular application.
The initialization of a Cartesi Machine loads a ROM image, a RAM image, and a root file-system (as a flash drive) from regular files in the host file-system.
Execution starts from the ROM image, which contains a simple program that creates a description of the machine organization for the Linux kernel.
The ROM image rom.bin
can be built in the rom/
directory in the Emulator SDK.
The Linux kernel itself resides in the RAM image linux.bin
, built in the kernel/
directory in the Emulator SDK.
After it is done with its own initialization, the Linux kernel cedes control to the /sbin/init
program in the root file-system.
The root file-system rootfs.ext2
contains all the data files and programs that make up an embedded Linux distribution.
It can be built in the fs/
directory in the Emulator SDK.
The components of the target application can reside in the root file-system itself, or in their own, separate file-systems.
The emulator can be instructed to execute whatever command is necessary to start the target application.
For a complete description of the Cartesi Machine architecture and the boot process, see the documentation for the target perspective.
Two main types of target application are envisioned. In the first type, a Cartesi Machine is initialized and then run until the target application requests for the machine to halt. Additional flash drives can be used to provide inputs for the application, and to receive its outputs. (These can take the form entire file-systems or may contain raw data.) Once the machine halts, the outputs can be inspected by the host. This type of target application can be seen stateless, since nothing is implicitly carried over from one execution to the next.
The second type of target application runs in a loop that waits for a request carrying an input, performs any neccessary computations to service the request, and produces a suitable response. After processing a request, the target application asks the machine to yield control back to the host. The host inspects the response, prepares the input for the next request, and resumes the machine so the target application can perform the next iteration of the loop. This type of target application is statefull in the sense that whatever state changes happen during the processing of a request will remain in effect when the next request is processed.
The setup of a new development environment is often a time-consuming task.
This is particularly true in case of cross-development environments (i.e., when the development happens in a host platform but software runs in a different target platform).
With this in mind, the Cartesi team provides the cartesi/playground
Docker image, which enables immediate experimentation with Cartesi Machines.
This comes with a pre-built emulator and Lua interpreter accessible within the command-line, as well as a pre-built ROM image, RAM image, and root file-system.
It also comes with the cross-compiler for the RISC-V architecture on which the Cartesi Machine is based.
To enter the playground, open a terminal, download the Docker image from Cartesi's repository, and run it adequately mapping the current user and group information, as well as making the host's current directory available inside the container:
$ docker pull cartesi/playground:0.3.0$ docker run -it --rm -h playground \ -e USER=$(id -u -n) \ -e GROUP=$(id -g -n) \ -e UID=$(id -u) \ -e GID=$(id -g) \ -v `pwd`:/home/$(id -u -n) \ -w /home/$(id -u -n) \ cartesi/playground:0.3.0 /bin/bash
Once inside, you can execute the cartesi-machine
utility as follows:
playground:~$ cartesi-machine --helpUsage:
/opt/cartesi/bin/cartesi-machine.lua [options] [command] [arguments]
where options are:
--server-address=<address> Use a remote cartesi machine server listenning to <address> instead of running a local cartesi machine. (requires --checkin-address)
--checkin-address=<address> Address of the local checkin server to run.
--server-shutdown Shutdown the server after the execution.
--ram-image=<filename> Name of file containing RAM image (default: "linux.bin").
--no-ram-image Forget settings for RAM image.
--ram-length=<number> Set RAM length.
--rom-image=<filename> Name of file containing ROM image (default: "rom.bin").
--no-rom-bootargs Clear default bootargs.
--append-rom-bootargs=<string> Append <string> to bootargs
--no-root-flash-drive clear default root flash drive and associated bootargs parameters
--flash-drive=<key>:<value>[,<key>:<value>[,...]...] defines a new flash drive, or modify an existing flash drive definition flash drives appear as /dev/mtdblock[1-7]
<key>:<value> is one of label:<label> filename:<filename> start:<number> length:<number> shared
label (mandatory) identifies the flash drive and init attempts to mount it as /mnt/<label>
filename (optional) gives the name of the file containing the image for the flash drive when omitted or set to the empty string, the drive starts filled with 0
start (optional) sets the starting physical memory offset for flash drive in bytes when omitted, drives start at 2 << 63 and are spaced by 2 << 60 if any start offset is set, all of them must be set
length (optional) gives the length of the flash drive in bytes (must be a multiple of 4Ki) if omitted, the length is computed from the image in filename if length and filename are set, the image file size must match length
shared (optional) target modifications to flash drive modify image file as well by default, image files are not modified and changes are lost
(an option "--flash-drive=label:root,filename:rootfs.ext2" is implicit)
--replace-flash-drive=<key>:<value>[,<key>:<value>[,...]...] --replace-memory-range=<key>:<value>[,<key>:<value>[,...]...] replaces an existing flash drive or rollup memory range right after machine instantiation. (typically used in conjunction with the --load=<directory> option.)
<key>:<value> is one of filename:<filename> start:<number> length:<number> shared
semantics are the same as for the --flash-drive option with the following difference: start and length are mandatory, and must match those of a previously existing flash drive or rollup memory memory range.
--dhd=<key>:<value>[,<key>:<value>[,...]...] configures the dehashing device by default, the device is not present
<key>:<value> is one of filename:<filename> tstart:<number> tlength:<number>
filename (optional) gives the name of the file containing the initial dehashed data. when omitted or set to the empty string, the data starts filled with 0
tstart (mandatory when device present) sets the start of target physical memory range for output data must be aligned to tlength
tlength (mandatory when device present) gives the length of target physical memory range for output data must be a power of 2 greater than 4Ki, or 0 when device not present
--rollup-rx-buffer=<key>:<value>[,<key>:<value>[,...]...] --rollup-tx-buffer=<key>:<value>[,<key>:<value>[,...]...] --rollup-input-metadata=<key>:<value>[,<key>:<value>[,...]...] --rollup-voucher-hashes=<key>:<value>[,<key>:<value>[,...]...] --rollup-notice-hashes=<key>:<value>[,<key>:<value>[,...]...] defines the individual the memory ranges used by rollups
<key>:<value> is one of filename:<filename> start:<number> length:<number> shared
semantics are the same as for the --flash-drive option with the following difference: start and length are mandatory.
--rollup defines appropriate values for rollup-rx-buffer, rollup-tx-buffer, rollup-input-metadata, rollup-voucher-hashes, rollup-notice hashes, and htif yield for use with rollups. equivalent to the following options:
--rollup-rx-buffer=start:0x60000000,length:2<<20 --rollup-tx-buffer=start:0x60200000,length:2<<20 --rollup-input-metadata=start:0x60400000,length:4096 --rollup-voucher-hashes=start:0x60600000,length:2<<20 --rollup-notice-hashes=start:0x60800000,length:2<<20 --htif-yield-manual --htif-yield-automatic
--rollup-advance-epoch=<key>:<value>[,<key>:<value>[,...]...] advances the state of the machine through an entire rollup epoch
<key>:<value> is one of epoch_index:<number> input:<filename-pattern> input_metadata:<filename-pattern> input_index_start:<number> input_index_end:<number> voucher:<filename-pattern> voucher_hashes: <filename> notice:<filename-pattern> notice_hashes: <filename> report:<filename-pattern> hashes
epoch_index the index of the epoch
input (default: "epoch-%e-input-%i.bin") the pattern that derives the name of the file read for input %i of epoch index %e
input_index_start (default: 0) index of first input to advance (the first value of %i)
input_index_end (default: 0) index of last input to advance (the last value of %i)
input_metadata (default: "epoch-%e-input-metadata-%i.bin") the pattern that derives the name of the file read for input metadata %i of epoch index %e
voucher (default: "epoch-%e-input-%i-voucher-%o.bin") the pattern that derives the name of the file written for voucher %o of input %i of epoch %e
voucher_hashes (default: "epoch-%e-input-%i-voucher-hashes.bin") the pattern that derives the name of the file written for the voucher hashes of input %i of epoch %e
notice (default: "epoch-%e-input-%i-notice-%o.bin") the pattern that derives the name of the file written for notice %o of input %i of epoch %e
notice_hashes (default: "epoch-%e-input-%i-notice-hashes.bin") the pattern that derives the name of the file written for the notice hashes of input %i of epoch %e
report (default: "epoch-%e-input-%i-report-%o.bin") the pattern that derives the name of the file written for report %o of input %i of epoch %e
hashes print out hashes before every input
"%e" is replaced by the epoch index, "%i" by the input index and "%o" by the voucher, notice, or report index the process starts with input
--dhd-source=<address> server acting as source for dehashed data
--concurrency=<key>:<value>[,<key>:<value>[,...]...] configures the number of threads used in some implementation parts.
<key>:<value> is one of update_merkle_tree:<number>
update_merkle_tree (optional) defines the number of threads to use while calculating the merkle tree. when ommited or defined as 0, the number of hardware threads is used if it can be identified or else a single thread is used.
--max-mcycle=<number> stop at a given mcycle (default: 2305843009213693952)
-i or --htif-console-getchar run in interactive mode
--htif-yield-manual honor yield requests with manual reset by target
--htif-yield-automatic honor yield requests with automatic reset by target
--load=<directory> load prebuilt machine from <directory>
--store=<directory> store machine to <directory>
--initial-hash print initial state hash before running machine
--final-hash print final state hash when done
--periodic-hashes=<number-period>[,<number-start>] prints root hash every <number-period> cycles. If <number-start> is given, the periodic hashing will start at that mcycle. This option implies --initial-hash and --final-hash. (default: none)
--step print step log for 1 additional cycle when done
--json-steps=<filename> output json with step logs for all cycles to <filename>
--store-config[=<filename>] store initial machine config to <filename>. If <filename> is omitted, print the initial machine config to stderr.
--load-config=<filename> load initial machine config from <filename>. If a field is omitted on machine_config table, it will fall back into the respective command-line argument or into the default value.
--dump-pmas dump all PMA ranges to disk when done
and command and arguments:
command the full path to the program inside the target system (default: /bin/sh)
arguments the given command arguments
<number> can be specified in decimal (e.g., 16) or hexadeximal (e.g., 0x10),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.
A final check can also be performed to verify if the contents inside the container are as expected:
playground:~$ md5sum /opt/cartesi/share/images/linux.bind1be2fa1a0dfd8b9ffae9be5ffbc506b /opt/cartesi/share/images/linux.bin
playground:~$ md5sum /opt/cartesi/share/images/rom.bin670ca3cfb4618f30879e6c2f1f4eda7a /opt/cartesi/share/images/rom.bin
playground:~$ md5sum /opt/cartesi/share/images/rootfs.ext210afa1ee46365bf01f9d7c9384c42997 /opt/cartesi/share/images/rootfs.ext2