Repository navigation
v2 draft
Marcelo Politzer Couto edited this page Jun 23, 2026
·
6 revisions
#ifndef CMT_H
#define CMT_H
#include "libcmt/abi.h"
#include"rollup.h"
// notice variants -------------------------------------------------------------
int cmt_emit_notice(
cmt_rollup_t *me,
cmt_buf_t *data
);
int cmt_emit_notice1(
cmt_rollup_t *me,
cmt_abi_u256_t *arg1,
cmt_buf_t *data
);
int cmt_emit_notice2(
cmt_rollup_t *me,
cmt_abi_u256_t *arg1,
cmt_abi_u256_t *arg2,
cmt_buf_t *data
);
int cmt_emit_notice3(
cmt_rollup_t *me,
cmt_abi_u256_t *arg1,
cmt_abi_u256_t *arg2,
cmt_abi_u256_t *arg3,
cmt_buf_t *data
);
// call voucher variants -------------------------------------------------------
int cmt_emit_call_voucher(
cmt_rollup_t *me,
cmt_abi_address_t *destination,
cmt_abi_u256_t *value,
cmt_buf_t *data
);
int cmt_emit_call_voucher1(
cmt_rollup_t *me,
cmt_abi_address_t *destination,
cmt_abi_u256_t *value,
cmt_abi_u256_t *arg1,
cmt_buf_t *data
);
// delegate call voucher variants ----------------------------------------------
int cmt_emit_delegate_call_voucher(
cmt_rollup_t *me,
cmt_abi_address_t *destination,
cmt_buf_t *data
);
int cmt_emit_delegate_call_voucher1(
cmt_rollup_t *me,
cmt_abi_address_t *destination,
cmt_abi_u256_t *arg1,
cmt_buf_t *data
);
int cmt_emit_delegate_call_voucher2(
cmt_rollup_t *me,
cmt_abi_address_t *destination,
cmt_abi_u256_t *arg1,
cmt_abi_u256_t *arg2,
cmt_buf_t *data
);
// ether transfers variants ----------------------------------------------------
int cmt_emit_ether_transfer(
cmt_rollup_t *me,
cmt_abi_address_t *recipient,
cmt_abi_u256_t *value,
cmt_buf_t *data
);
int cmt_emit_ether_transfer1(
cmt_rollup_t *me,
cmt_abi_address_t *recipient,
cmt_abi_u256_t *value,
cmt_abi_u256_t *arg1,
cmt_buf_t *data
);
// erc-20 transfers variants ---------------------------------------------------
int cmt_emit_erc20_transfer(
cmt_rollup_t *me,
cmt_abi_address_t *recipient,
cmt_abi_u256_t *token,
cmt_abi_u256_t *value,
cmt_buf_t *data
);
// erc-721 transfers variants --------------------------------------------------
int cmt_emit_erc721_transfer(
cmt_rollup_t *me,
cmt_abi_address_t *recipient,
cmt_abi_u256_t *token,
cmt_abi_u256_t *token_id,
cmt_buf_t *data
);
// erc-1155 single transfers ---------------------------------------------------
int cmt_emit_erc1155_single_transfer(
cmt_rollup_t *me,
cmt_abi_address_t *recipient,
cmt_abi_u256_t *token,
cmt_abi_u256_t *token_id,
cmt_buf_t *data
);
// erc-1155 batch transfers ----------------------------------------------------
int cmt_emit_erc1155_batch_transfer(
cmt_rollup_t *me,
cmt_abi_address_t *recipient,
cmt_abi_u256_t *token,
cmt_abi_u256_t *token_id,
cmt_buf_t *data
);
#endif /* CMT_H */// htif.h ----------------------------------------------------------------------
// For internal usage, lib exports only ADVANCE & INSPECT?
enum {
HTIF_YIELD_CMD_AUTOMATIC = 0,
HTIF_YIELD_CMD_MANUAL = 1,
};
enum {
HTIF_YIELD_AUTOMATIC_REASON_PROGRESS = 1,
HTIF_YIELD_AUTOMATIC_REASON_TX_OUTPUT = 2,
HTIF_YIELD_AUTOMATIC_REASON_TX_REPORT = 4,
};
enum {
HTIF_YIELD_MANUAL_REASON_RX_ACCEPTED = 1,
HTIF_YIELD_MANUAL_REASON_RX_REJECTED = 2,
HTIF_YIELD_MANUAL_REASON_TX_EXCEPTION = 4,
};
enum {
HTIF_YIELD_REASON_ADVANCE = 0,
HTIF_YIELD_REASON_INSPECT = 1,
};
// buf.h -----------------------------------------------------------------------
// mostly unchanged. begin & end types changed to better handle UB on pointer
// arithmetic based bounds checking.
#include<stdint.h>
#include<stddef.h>
typedef struct cmt_buf {
uintptr_t begin;
uintptr_t end;
} cmt_buf_t;
cmt_buf_t cmt_buf_init (size_t length, uint8_t data[length]);
void *cmt_buf_view (cmt_buf_t *, size_t *length);
int cmt_buf_split(cmt_buf_t *, size_t length, cmt_buf_t *lhs, cmt_buf_t *rhs);
// io.h ------------------------------------------------------------------------
// mostly unchanged. cmt_io_t contents got type erased to better support the
// multiple backends. This simplify the symbols exported by the lib. Each
// implementation checks its own type against the reserved space with a
// static_assert.
#include<stddef.h>
#include<stdint.h>
typedef struct {
uint64_t data[26];
} cmt_io_t;
typedef struct {
uint8_t dev;
uint8_t cmd;
uint16_t reason;
uint32_t data;
} cmt_io_yield_t;
int cmt_io_init (cmt_io_t *);
void cmt_io_fini (cmt_io_t *);
cmt_buf_t cmt_io_get_tx(cmt_io_t *);
cmt_buf_t cmt_io_get_rx(cmt_io_t *);
int cmt_io_yield (cmt_io_t *, cmt_io_yield_t *rr);
// io.c ------------------------------------------------------------------------
// renamed from cmt_io_driver_ioctl_t to cmt_io_linux_t.
typedef struct {
cmt_buf_t tx[1];
cmt_buf_t rx[1];
int fd;
} cmt_io_linux_t;
static_assert(sizeof(cmt_io_linux_t) <= sizeof(cmt_io_t));
// io-mock.c -------------------------------------------------------------------
// renamed from cmt_io_driver_mock_t to cmt_io_mock_t.
typedef struct {
cmt_buf_t tx[1];
cmt_buf_t rx[1];
cmt_buf_t inputs_left;
int input_type;
char input_filename[128];
char input_fileext[8];
uint32_t input_seq;
uint32_t output_seq;
uint32_t report_seq;
uint32_t exception_seq;
} cmt_io_mock_t;
static_assert(sizeof(cmt_io_mock_t) <= sizeof(cmt_io_t));
// io-guest.c ------------------------------------------------------------------
// host mode (guest side) uses the same APIs as linux and mock versions.
// cmt_io_init will open a UNIX socket created by the host; retrive two file
// descriptors for tx and rx.
typedef struct {
cmt_buf_t tx[1];
cmt_buf_t rx[1];
int fd;
} cmt_io_guest_t;
static_assert(sizeof(cmt_io_guest_t) <= sizeof(cmt_io_t));
// io-host.c -------------------------------------------------------------------
// host mode (host side) has a API that mimics the machine-emulator.
// cmt_io_host_init creates a UNIX socket and two temporary files, one for tx,
// one for rx, buffers. It then waits for a connection from guest before proceding.
typedef struct {
cmt_buf_t tx[1];
cmt_buf_t rx[1];
int fd;
} cmt_io_host_t;
static_assert(sizeof(cmt_io_host_t) <= sizeof(cmt_io_t));
int cmt_io_host_init (cmt_io_host_t *);
int cmt_io_host_fini (cmt_io_host_t *);
int cmt_io_host_send_cmio_response (cmt_io_host_t *, uint16_t reason, const uint8_t *data, uint64_t length);
int cmt_io_host_run (cmt_io_host_t *, uint16_t *break_reason);
int cmt_io_host_receive_cmio_request(cmt_io_host_t *, uint8_t *cmd, uint16_t *reason, uint8_t *data, uint64_t *length);
// keccak.h --------------------------------------------------------------------
// unchanged
enum { CMT_KECCAK_LENGTH = 32 };
typedef union {
uint8_t b[200];
uint64_t q[25];
} cmt_keccak_state_t;
typedef struct cmt_keccak {
cmt_keccak_state_t st;
int pt, rsiz;
} cmt_keccak_t;
void cmt_keccak_init (cmt_keccak_t *);
void cmt_keccak_update(cmt_keccak_t *, size_t n, const void *data);
void cmt_keccak_final (cmt_keccak_t *, void *md);
uint8_t *cmt_keccak_data (size_t length, const void *data, uint8_t md[CMT_KECCAK_LENGTH]);
uint32_t cmt_keccak_funsel(const char *decl);
// merkle.h --------------------------------------------------------------------
// unchanged
enum { CMT_MERKLE_TREE_HEIGHT = 63 };
typedef struct {
uint64_t leaf_count;
uint8_t state[CMT_MERKLE_TREE_HEIGHT][CMT_KECCAK_LENGTH];
} cmt_merkle_t;
void cmt_merkle_init (cmt_merkle_t *);
void cmt_merkle_fini (cmt_merkle_t *);
int cmt_merkle_load (cmt_merkle_t *, const char *filepath);
int cmt_merkle_save (cmt_merkle_t *, const char *filepath);
uint64_t cmt_merkle_get_leaf_count(cmt_merkle_t *);
int cmt_merkle_push_back (cmt_merkle_t *, const uint8_t hash[CMT_KECCAK_LENGTH]);
int cmt_merkle_push_back_data(cmt_merkle_t *, size_t length, const void *data);
void cmt_merkle_get_root_hash (cmt_merkle_t *, uint8_t root[CMT_KECCAK_LENGTH]);
// rollup.h --------------------------------------------------------------------
// rollup got simplified. cmt_rollup_get_merkle and cmt_rollup_get_io got added
// as escape hatches for advanced use cases. ABI encoded structs got removed and
// will be handled by a separate layer above.
//
// Emit output and report also have a zero copy mode when `data` is NULL. In this
// mode, neither function touches the tx buffer prior to notifying the host of
// the data. This allows users to safely encode data directly to the tx buffer.
typedef struct {
cmt_io_t io[1];
cmt_merkle_t merkle[1];
} cmt_rollup_t;
enum {
CMT_REQ_TYPE_ADVANCE = 0,
CMT_REQ_TYPE_INSPECT = 1,
};
int cmt_rollup_init (cmt_rollup_t *, cmt_buf_t *tx);
int cmt_rollup_fini (cmt_rollup_t *);
cmt_merkle_t *cmt_rollup_get_merkle (cmt_rollup_t *);
cmt_io_t *cmt_rollup_get_io (cmt_rollup_t *);
int cmt_rollup_wait_for_input (cmt_rollup_t *, cmt_buf_t *rx);
int cmt_rollup_reject (cmt_rollup_t *);
int cmt_rollup_emit_output (cmt_rollup_t *, size_t length, const void *data);
int cmt_rollup_emit_report (cmt_rollup_t *, size_t length, const void *data);
// abi.h -----------------------------------------------------------------------
// abi got new types: an ABI writter and ABI reader. Their main purpose is to
// simplify buffer size calculations. They also make the API a bit more discoverable.
// `bytes_s` and `bytes_d` got reworked and renamed to something more descriptive.
// NOTE: this may be removed in favor of a separate lib, TBD.
typedef struct {
// ...
int stub;
} cmt_wr_t;
typedef struct {
// ...
int stub;
} cmt_rd_t;
typedef struct {
// ...
int stub;
} cmt_frame_t;
typedef struct {
uint8_t data[32];
} cmt_256_t;
typedef struct {
uint8_t data[20];
} cmt_address_t;
typedef struct {
// ...
int stub;
} cmt_wr_bytes_state_t;
int cmt_wr_init (cmt_wr_t *, cmt_buf_t *tx);
size_t cmt_wr_get_used (cmt_wr_t *);
size_t cmt_wr_get_available (cmt_wr_t *);
int cmt_wr_funsel (cmt_wr_t *, uint32_t funsel);
int cmt_wr_256 (cmt_wr_t *, cmt_256_t *data);
int cmt_wr_address (cmt_wr_t *, cmt_address_t *data);
int cmt_wr_frame (cmt_wr_t *, cmt_frame_t *frame);
int cmt_wr_bytes_meta (cmt_wr_t *, cmt_wr_bytes_state_t *, const cmt_frame_t *frame);
int cmt_wr_bytes_data (cmt_wr_t *, cmt_wr_bytes_state_t *, cmt_buf_t *data);
int cmt_wr_bytes_reserve (cmt_wr_t *, cmt_wr_bytes_state_t *wr_bytes, cmt_buf_t *available);
int cmt_wr_bytes_commit (cmt_wr_t *, cmt_wr_bytes_state_t *wr_bytes, size_t used);
int cmt_rd_init (cmt_rd_t *, cmt_buf_t *rx);
int cmt_rd_funsel_check (cmt_rd_t *, uint32_t funsel);
int cmt_rd_funsel (cmt_rd_t *, uint32_t *funsel);
int cmt_rd_uint256 (cmt_rd_t *, cmt_256_t *data);
int cmt_rd_address (cmt_rd_t *, cmt_address_t *data);
int cmt_rd_frame (cmt_rd_t *, cmt_frame_t *frame);
int cmt_rd_bytes_meta (cmt_rd_t *, cmt_wr_bytes_state_t *, const cmt_frame_t *frame);
int cmt_rd_bytes_view (cmt_rd_t *, cmt_wr_bytes_state_t *, cmt_buf_t *view);
// codec.h ?--------------------------------------------------------------------
// This needs a better name!
int cmt_emit_outputN(cmt_rollup_t *,
size_t nargs,
const cmt_256_t *arg[nargs],
const cmt_buf_t *bytes);
int cmt_emit_output0(cmt_rollup_t *,
const cmt_buf_t *bytes);
int cmt_emit_output1(cmt_rollup_t *,
const cmt_256_t *arg0,
const cmt_buf_t *bytes);
int cmt_emit_output2(cmt_rollup_t *,
const cmt_256_t *arg0,
const cmt_256_t *arg1,
const cmt_buf_t *bytes);
int cmt_emit_output3(cmt_rollup_t *,
const cmt_256_t *arg0,
const cmt_256_t *arg1,
const cmt_256_t *arg2,
const cmt_buf_t *bytes);
int cmt_emit_output4(cmt_rollup_t *,
const cmt_256_t *arg0,
const cmt_256_t *arg1,
const cmt_256_t *arg2,
const cmt_256_t *arg3,
const cmt_buf_t *bytes);// memory layouts
typedef struct {
uint8_t funsel[4];
uint8_t arg0[32];
uint8_t offset[32];
uint8_t length[32];
uint8_t data[];
} cmt_output1;
typedef struct {
uint8_t funsel[4];
uint8_t arg0[32];
uint8_t arg1[32];
uint8_t offset[32];
uint8_t length[32];
uint8_t data[];
} cmt_output2;
typedef struct {
uint8_t funsel[4];
uint8_t arg0[32];
uint8_t arg1[32];
uint8_t arg2[32];
uint8_t offset[32];
uint8_t length[32];
uint8_t data[];
} cmt_output3;
typedef struct {
uint8_t funsel[4];
uint8_t arg0[32];
uint8_t arg1[32];
uint8_t arg2[32];
uint8_t arg3[32];
uint8_t offset[32];
uint8_t length[32];
uint8_t data[];
} cmt_output4;
typedef struct {
uint8_t funsel[4];
uint8_t chain_id[32];
uint8_t app_contract[32]; // read as [20]
uint8_t msg_sender[32]; // read as [20]
uint8_t block_number[32];
uint8_t block_timestamp[32];
uint8_t prev_randao[32];
uint8_t index[32];
uint8_t offset[32];
uint8_t length[32];
uint8_t data[];
} cmt_advance_t;// advance decode could look something like this using: https://github.com/mhw0/libethc
typedef struct {
uint64_t chain_id;
char app_contract[2*CMT_ADDRESS_LENGTH+1];
char msg_sender[2*CMT_ADDRESS_LENGTH+1];
uint64_t block_number;
uint64_t block_timestamp;
mp_int prev_randao;
uint64_t index;
size_t payload_length;
void *payload_data;
} advance_t;
void decode_advance(cmt_rollup_t *rollup, advance_t *advance) {
struct eth_abi abi[1] = {0};
if (eth_abi_init(abi, ETH_ABI_DECODE) != ETH_OK
cmt_rollup_reject(rollup);
if (eth_abi_uint64(abi, &advance->chain_id) != ETH_OK)
cmt_rollup_reject(rollup);
if (eth_abi_address(abi, &advance->app_contract) != ETH_OK)
cmt_rollup_reject(rollup);
if (eth_abi_address(abi, &advance->msg_sender) != ETH_OK)
cmt_rollup_reject(rollup);
if (eth_abi_uint64(abi, &advance->block_number) != ETH_OK)
cmt_rollup_reject(rollup);
if (eth_abi_uint64(abi, &advance->block_timestamp) != ETH_OK)
cmt_rollup_reject(rollup);
if (eth_abi_mpint(abi, &advance->prev_randao) != ETH_OK)
cmt_rollup_reject(rollup);
if (eth_abi_uint64(abi, &advance->index) != ETH_OK)
cmt_rollup_reject(rollup);
// how long?
advance->payload_length = 2 << 20UL;
advance->payload = malloc(max); // maximum of 2MB
if (advance->payload == NULL)
cmt_rollup_reject(rollup);
if (eth_abi_bytes(abi, advance->payload, &advance->payload_length) != ETH_OK)
cmt_rollup_reject(rollup);
return abi;
}
// and the main loop like this
int main() {
cmt_buf_t tx[1], rx[1];
cmt_rollup_t R[1];
if (cmt_rollup_init(R, tx) < 0)
exit(1);
for (int req_type; (req_type = cmt_rollup_wait_for_input(R, rx)) < 0; ) {
switch (req_type) {
case ADVANCE:
decode_advance(R, advance);
break;
case INSPECT:
break;
}
}
}