diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index a436d1dc..8c2046de 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -42,7 +42,7 @@ jobs:
           restore-keys: |
             ${{ runner.os }}-cargo-
 
-      - name: Deps
+      - name: Install Cartesi Machine
         run: |
           export CARTESI_MACHINE_VERSION=0.18.1
           sudo apt-get install -y libboost-all-dev lua5.4 libslirp0
@@ -50,9 +50,12 @@ jobs:
           sudo dpkg -i ./cartesi-machine-v${CARTESI_MACHINE_VERSION}_amd64.deb
           rm ./cartesi-machine-v${CARTESI_MACHINE_VERSION}_amd64.deb
 
-      - name: Rust fmt and check
+      - name: Setup env
         run: |
           just setup
+
+      - name: Rust fmt and check
+        run: |
           just check-fmt-rust-workspace
           just check-rust-workspace
 
diff --git a/Cargo.lock b/Cargo.lock
index e60d256c..5294299f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -24,7 +24,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
 dependencies = [
  "cfg-if",
- "getrandom",
+ "getrandom 0.2.15",
  "once_cell",
  "version_check",
  "zerocopy",
@@ -60,6 +60,7 @@ dependencies = [
  "alloy-node-bindings",
  "alloy-provider",
  "alloy-rpc-client",
+ "alloy-rpc-types",
  "alloy-serde",
  "alloy-signer",
  "alloy-signer-local",
@@ -69,9 +70,9 @@ dependencies = [
 
 [[package]]
 name = "alloy-chains"
-version = "0.1.51"
+version = "0.1.57"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4e0f0136c085132939da6b753452ebed4efaa73fe523bb855b10c199c2ebfaf"
+checksum = "4ab9d1367c6ffb90c93fb4a9a4989530aa85112438c6f73a734067255d348469"
 dependencies = [
  "alloy-primitives",
  "num_enum",
@@ -126,14 +127,14 @@ dependencies = [
  "alloy-transport",
  "futures",
  "futures-util",
- "thiserror 2.0.9",
+ "thiserror",
 ]
 
 [[package]]
 name = "alloy-core"
-version = "0.8.16"
+version = "0.8.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e3fdddfc89197319b1be19875a70ced62a72bebb67e2276dad688cd59f40e70"
+checksum = "648275bb59110f88cc5fa9a176845e52a554ebfebac2d21220bcda8c9220f797"
 dependencies = [
  "alloy-dyn-abi",
  "alloy-json-abi",
@@ -144,9 +145,9 @@ dependencies = [
 
 [[package]]
 name = "alloy-dyn-abi"
-version = "0.8.16"
+version = "0.8.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0d2ea4d7f220a19c1f8c98822026d1d26a4b75a72e1a7308d02bab1f77c9a00"
+checksum = "bc9138f4f0912793642d453523c3116bd5d9e11de73b70177aa7cb3e94b98ad2"
 dependencies = [
  "alloy-json-abi",
  "alloy-primitives",
@@ -214,9 +215,9 @@ dependencies = [
 
 [[package]]
 name = "alloy-json-abi"
-version = "0.8.16"
+version = "0.8.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e79c6b4bcc1067a7394b5b2aec7da1bd829c8c476b796c73eb14da34392a07a7"
+checksum = "24acd2f5ba97c7a320e67217274bc81fe3c3174b8e6144ec875d9d54e760e278"
 dependencies = [
  "alloy-primitives",
  "alloy-sol-type-parser",
@@ -234,7 +235,7 @@ dependencies = [
  "alloy-sol-types",
  "serde",
  "serde_json",
- "thiserror 2.0.9",
+ "thiserror",
  "tracing",
 ]
 
@@ -260,7 +261,7 @@ dependencies = [
  "futures-utils-wasm",
  "serde",
  "serde_json",
- "thiserror 2.0.9",
+ "thiserror",
 ]
 
 [[package]]
@@ -288,16 +289,16 @@ dependencies = [
  "rand",
  "serde_json",
  "tempfile",
- "thiserror 2.0.9",
+ "thiserror",
  "tracing",
  "url",
 ]
 
 [[package]]
 name = "alloy-primitives"
-version = "0.8.16"
+version = "0.8.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0540fd0355d400b59633c27bd4b42173e59943f28e9d3376b77a24771d432d04"
+checksum = "ec878088ec6283ce1e90d280316aadd3d6ce3de06ff63d68953c855e7e447e92"
 dependencies = [
  "alloy-rlp",
  "bytes",
@@ -306,7 +307,6 @@ dependencies = [
  "derive_more",
  "foldhash",
  "hashbrown 0.15.2",
- "hex-literal",
  "indexmap",
  "itoa",
  "k256",
@@ -315,7 +315,7 @@ dependencies = [
  "proptest",
  "rand",
  "ruint",
- "rustc-hash 2.1.0",
+ "rustc-hash",
  "serde",
  "sha3",
  "tiny-keccak",
@@ -355,7 +355,7 @@ dependencies = [
  "schnellru",
  "serde",
  "serde_json",
- "thiserror 2.0.9",
+ "thiserror",
  "tokio",
  "tracing",
  "url",
@@ -364,9 +364,9 @@ dependencies = [
 
 [[package]]
 name = "alloy-rlp"
-version = "0.3.10"
+version = "0.3.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f542548a609dca89fcd72b3b9f355928cf844d4363c5eed9c5273a3dd225e097"
+checksum = "3d6c1d995bff8d011f7cd6c81820d51825e6e06d6db73914c1630ecf544d83d6"
 dependencies = [
  "alloy-rlp-derive",
  "arrayvec",
@@ -375,13 +375,13 @@ dependencies = [
 
 [[package]]
 name = "alloy-rlp-derive"
-version = "0.3.10"
+version = "0.3.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a833d97bf8a5f0f878daf2c8451fff7de7f9de38baa5a45d936ec718d81255a"
+checksum = "a40e1ef334153322fd878d07e86af7a529bcb86b2439525920a88eba87bcf943"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
 
 [[package]]
@@ -407,6 +407,18 @@ dependencies = [
  "wasmtimer",
 ]
 
+[[package]]
+name = "alloy-rpc-types"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3410a472ce26c457e9780f708ee6bd540b30f88f1f31fdab7a11d00bd6aa1aee"
+dependencies = [
+ "alloy-primitives",
+ "alloy-rpc-types-eth",
+ "alloy-serde",
+ "serde",
+]
+
 [[package]]
 name = "alloy-rpc-types-anvil"
 version = "0.8.3"
@@ -472,7 +484,7 @@ dependencies = [
  "auto_impl",
  "elliptic-curve",
  "k256",
- "thiserror 2.0.9",
+ "thiserror",
 ]
 
 [[package]]
@@ -488,28 +500,28 @@ dependencies = [
  "async-trait",
  "k256",
  "rand",
- "thiserror 2.0.9",
+ "thiserror",
 ]
 
 [[package]]
 name = "alloy-sol-macro"
-version = "0.8.16"
+version = "0.8.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6d1a14b4a9f6078ad9132775a2ebb465b06b387d60f7413ddc86d7bf7453408"
+checksum = "8d039d267aa5cbb7732fa6ce1fd9b5e9e29368f580f80ba9d7a8450c794de4b2"
 dependencies = [
  "alloy-sol-macro-expander",
  "alloy-sol-macro-input",
  "proc-macro-error2",
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
 
 [[package]]
 name = "alloy-sol-macro-expander"
-version = "0.8.16"
+version = "0.8.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4436b4b96d265eb17daea26eb31525c3076d024d10901e446790afbd2f7eeaf5"
+checksum = "620ae5eee30ee7216a38027dec34e0585c55099f827f92f50d11e3d2d3a4a954"
 dependencies = [
  "alloy-json-abi",
  "alloy-sol-macro-input",
@@ -519,16 +531,16 @@ dependencies = [
  "proc-macro-error2",
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
  "syn-solidity",
  "tiny-keccak",
 ]
 
 [[package]]
 name = "alloy-sol-macro-input"
-version = "0.8.16"
+version = "0.8.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5f58698a18b96faa8513519de112b79a96010b4ff84264ce54a217c52a8e98b"
+checksum = "ad9f7d057e00f8c5994e4ff4492b76532c51ead39353aa2ed63f8c50c0f4d52e"
 dependencies = [
  "alloy-json-abi",
  "const-hex",
@@ -537,15 +549,15 @@ dependencies = [
  "proc-macro2",
  "quote",
  "serde_json",
- "syn 2.0.94",
+ "syn 2.0.96",
  "syn-solidity",
 ]
 
 [[package]]
 name = "alloy-sol-type-parser"
-version = "0.8.16"
+version = "0.8.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f3d6d2c490f650c5abd65a9a583b09a8c8931c265d3a55b18a8e349dd6d9d84"
+checksum = "74e60b084fe1aef8acecda2743ff2d93c18ff3eb67a2d3b12f62582a1e66ef5e"
 dependencies = [
  "serde",
  "winnow",
@@ -553,9 +565,9 @@ dependencies = [
 
 [[package]]
 name = "alloy-sol-types"
-version = "0.8.16"
+version = "0.8.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c766e4979fc19d70057150befe8e3ea3f0c4cbc6839b8eaaa250803451692305"
+checksum = "c1382302752cd751efd275f4d6ef65877ddf61e0e6f5ac84ef4302b79a33a31a"
 dependencies = [
  "alloy-json-abi",
  "alloy-primitives",
@@ -576,7 +588,7 @@ dependencies = [
  "futures-utils-wasm",
  "serde",
  "serde_json",
- "thiserror 2.0.9",
+ "thiserror",
  "tokio",
  "tower",
  "tracing",
@@ -656,11 +668,12 @@ dependencies = [
 
 [[package]]
 name = "anstyle-wincon"
-version = "3.0.6"
+version = "3.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
+checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
 dependencies = [
  "anstyle",
+ "once_cell",
  "windows-sys 0.59.0",
 ]
 
@@ -811,7 +824,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
 
 [[package]]
@@ -833,29 +846,29 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
 
 [[package]]
 name = "async-trait"
-version = "0.1.83"
+version = "0.1.85"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
+checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
 
 [[package]]
 name = "auto_impl"
-version = "1.2.0"
+version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42"
+checksum = "e12882f59de5360c748c4cbf569a042d5fb0eb515f7bea9c1f470b47f6ffbd73"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
 
 [[package]]
@@ -899,25 +912,22 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
 
 [[package]]
 name = "bindgen"
-version = "0.69.5"
+version = "0.71.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088"
+checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3"
 dependencies = [
  "bitflags",
  "cexpr",
  "clang-sys",
- "itertools 0.12.1",
- "lazy_static",
- "lazycell",
+ "itertools 0.13.0",
  "log",
  "prettyplease",
  "proc-macro2",
  "quote",
  "regex",
- "rustc-hash 1.1.0",
+ "rustc-hash",
  "shlex",
- "syn 2.0.94",
- "which",
+ "syn 2.0.96",
 ]
 
 [[package]]
@@ -937,9 +947,9 @@ checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
 
 [[package]]
 name = "bitflags"
-version = "2.6.0"
+version = "2.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
 
 [[package]]
 name = "bitvec"
@@ -1034,17 +1044,22 @@ dependencies = [
  "alloy",
  "hex",
  "ruint",
- "sha3",
- "thiserror 1.0.69",
+ "thiserror",
+ "tiny-keccak",
 ]
 
 [[package]]
 name = "cartesi-machine"
 version = "0.1.0"
 dependencies = [
+ "base64",
  "cartesi-machine-sys",
+ "derive_builder",
  "hex",
- "thiserror 1.0.69",
+ "serde",
+ "serde_json",
+ "tempfile",
+ "thiserror",
 ]
 
 [[package]]
@@ -1102,7 +1117,7 @@ dependencies = [
  "rusqlite_migration",
  "serde",
  "serde_json",
- "thiserror 1.0.69",
+ "thiserror",
  "tokio",
 ]
 
@@ -1117,9 +1132,9 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.2.6"
+version = "1.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333"
+checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229"
 dependencies = [
  "shlex",
 ]
@@ -1158,9 +1173,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.5.23"
+version = "4.5.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
+checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -1168,9 +1183,9 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.5.23"
+version = "4.5.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
+checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7"
 dependencies = [
  "anstream",
  "anstyle",
@@ -1180,14 +1195,14 @@ dependencies = [
 
 [[package]]
 name = "clap_derive"
-version = "4.5.18"
+version = "4.5.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
+checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c"
 dependencies = [
  "heck",
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
 
 [[package]]
@@ -1254,9 +1269,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
 
 [[package]]
 name = "crunchy"
-version = "0.2.2"
+version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929"
 
 [[package]]
 name = "crypto-bigint"
@@ -1280,6 +1295,41 @@ dependencies = [
  "typenum",
 ]
 
+[[package]]
+name = "darling"
+version = "0.20.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
+dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.20.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.20.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn 2.0.96",
+]
+
 [[package]]
 name = "dashmap"
 version = "6.1.0"
@@ -1336,6 +1386,37 @@ dependencies = [
  "syn 1.0.109",
 ]
 
+[[package]]
+name = "derive_builder"
+version = "0.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
+dependencies = [
+ "derive_builder_macro",
+]
+
+[[package]]
+name = "derive_builder_core"
+version = "0.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
+dependencies = [
+ "darling",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "derive_builder_macro"
+version = "0.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
+dependencies = [
+ "derive_builder_core",
+ "syn 2.0.96",
+]
+
 [[package]]
 name = "derive_more"
 version = "1.0.0"
@@ -1353,7 +1434,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
  "unicode-xid",
 ]
 
@@ -1386,7 +1467,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
 
 [[package]]
@@ -1633,7 +1714,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
 
 [[package]]
@@ -1692,10 +1773,22 @@ dependencies = [
  "cfg-if",
  "js-sys",
  "libc",
- "wasi",
+ "wasi 0.11.0+wasi-snapshot-preview1",
  "wasm-bindgen",
 ]
 
+[[package]]
+name = "getrandom"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.13.3+wasi-0.2.2",
+ "windows-targets",
+]
+
 [[package]]
 name = "gimli"
 version = "0.31.1"
@@ -1791,15 +1884,6 @@ dependencies = [
  "digest 0.10.7",
 ]
 
-[[package]]
-name = "home"
-version = "0.5.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
-dependencies = [
- "windows-sys 0.59.0",
-]
-
 [[package]]
 name = "http"
 version = "1.2.0"
@@ -2033,9 +2117,15 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
 
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
 [[package]]
 name = "idna"
 version = "1.0.3"
@@ -2074,14 +2164,14 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
 
 [[package]]
 name = "indexmap"
-version = "2.7.0"
+version = "2.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
+checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
 dependencies = [
  "equivalent",
  "hashbrown 0.15.2",
@@ -2090,9 +2180,9 @@ dependencies = [
 
 [[package]]
 name = "ipnet"
-version = "2.10.1"
+version = "2.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708"
+checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
 
 [[package]]
 name = "is_terminal_polyfill"
@@ -2109,15 +2199,6 @@ dependencies = [
  "either",
 ]
 
-[[package]]
-name = "itertools"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
-dependencies = [
- "either",
-]
-
 [[package]]
 name = "itertools"
 version = "0.13.0"
@@ -2135,9 +2216,9 @@ checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
 
 [[package]]
 name = "js-sys"
-version = "0.3.76"
+version = "0.3.77"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
+checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
 dependencies = [
  "once_cell",
  "wasm-bindgen",
@@ -2181,12 +2262,6 @@ version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
 
-[[package]]
-name = "lazycell"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
-
 [[package]]
 name = "libc"
 version = "0.2.169"
@@ -2231,9 +2306,9 @@ dependencies = [
 
 [[package]]
 name = "linux-raw-sys"
-version = "0.4.14"
+version = "0.4.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
+checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
 
 [[package]]
 name = "litemap"
@@ -2253,9 +2328,9 @@ dependencies = [
 
 [[package]]
 name = "log"
-version = "0.4.22"
+version = "0.4.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
 
 [[package]]
 name = "lru"
@@ -2286,9 +2361,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
 
 [[package]]
 name = "miniz_oxide"
-version = "0.8.2"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394"
+checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924"
 dependencies = [
  "adler2",
 ]
@@ -2300,7 +2375,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
 dependencies = [
  "libc",
- "wasi",
+ "wasi 0.11.0+wasi-snapshot-preview1",
  "windows-sys 0.52.0",
 ]
 
@@ -2387,14 +2462,14 @@ checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
 
 [[package]]
 name = "nybbles"
-version = "0.3.3"
+version = "0.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3409fc85ac27b27d971ea7cd1aabafd2eefa6de7e481c8d4f707225c117e81a"
+checksum = "8983bb634df7248924ee0c4c3a749609b5abcb082c28fffe3254b3eb3602b307"
 dependencies = [
  "alloy-rlp",
  "const-hex",
@@ -2441,7 +2516,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
 
 [[package]]
@@ -2530,35 +2605,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc"
 dependencies = [
  "memchr",
- "thiserror 2.0.9",
+ "thiserror",
  "ucd-trie",
 ]
 
 [[package]]
 name = "pin-project"
-version = "1.1.7"
+version = "1.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95"
+checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916"
 dependencies = [
  "pin-project-internal",
 ]
 
 [[package]]
 name = "pin-project-internal"
-version = "1.1.7"
+version = "1.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c"
+checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
 
 [[package]]
 name = "pin-project-lite"
-version = "0.2.15"
+version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
 
 [[package]]
 name = "pin-utils"
@@ -2593,12 +2668,12 @@ dependencies = [
 
 [[package]]
 name = "prettyplease"
-version = "0.2.25"
+version = "0.2.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"
+checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac"
 dependencies = [
  "proc-macro2",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
 
 [[package]]
@@ -2640,14 +2715,14 @@ dependencies = [
  "proc-macro-error-attr2",
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.92"
+version = "1.0.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
+checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
 dependencies = [
  "unicode-ident",
 ]
@@ -2688,10 +2763,10 @@ dependencies = [
  "pin-project-lite",
  "quinn-proto",
  "quinn-udp",
- "rustc-hash 2.1.0",
+ "rustc-hash",
  "rustls",
  "socket2",
- "thiserror 2.0.9",
+ "thiserror",
  "tokio",
  "tracing",
 ]
@@ -2703,14 +2778,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d"
 dependencies = [
  "bytes",
- "getrandom",
+ "getrandom 0.2.15",
  "rand",
  "ring",
- "rustc-hash 2.1.0",
+ "rustc-hash",
  "rustls",
  "rustls-pki-types",
  "slab",
- "thiserror 2.0.9",
+ "thiserror",
  "tinyvec",
  "tracing",
  "web-time",
@@ -2773,7 +2848,7 @@ version = "0.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
 dependencies = [
- "getrandom",
+ "getrandom 0.2.15",
 ]
 
 [[package]]
@@ -2888,7 +2963,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
 dependencies = [
  "cc",
  "cfg-if",
- "getrandom",
+ "getrandom 0.2.15",
  "libc",
  "spin",
  "untrusted",
@@ -2910,7 +2985,6 @@ name = "rollups-blockchain-reader"
 version = "0.1.0"
 dependencies = [
  "alloy",
- "alloy-rpc-types-eth",
  "async-recursion",
  "cartesi-dave-contracts",
  "cartesi-dave-merkle",
@@ -2923,7 +2997,7 @@ dependencies = [
  "rollups-state-manager",
  "rusqlite",
  "rusqlite_migration",
- "thiserror 1.0.69",
+ "thiserror",
  "tokio",
 ]
 
@@ -2953,7 +3027,7 @@ dependencies = [
  "cartesi-rollups-contracts",
  "hex",
  "rollups-state-manager",
- "thiserror 1.0.69",
+ "thiserror",
 ]
 
 [[package]]
@@ -2973,7 +3047,7 @@ dependencies = [
  "lazy_static",
  "rusqlite",
  "rusqlite_migration",
- "thiserror 1.0.69",
+ "thiserror",
 ]
 
 [[package]]
@@ -3038,12 +3112,6 @@ version = "0.1.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
 
-[[package]]
-name = "rustc-hash"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
-
 [[package]]
 name = "rustc-hash"
 version = "2.1.0"
@@ -3071,14 +3139,14 @@ version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
 dependencies = [
- "semver 1.0.24",
+ "semver 1.0.25",
 ]
 
 [[package]]
 name = "rustix"
-version = "0.38.42"
+version = "0.38.44"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
+checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
 dependencies = [
  "bitflags",
  "errno",
@@ -3089,9 +3157,9 @@ dependencies = [
 
 [[package]]
 name = "rustls"
-version = "0.23.20"
+version = "0.23.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b"
+checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8"
 dependencies = [
  "once_cell",
  "ring",
@@ -3165,9 +3233,9 @@ dependencies = [
 
 [[package]]
 name = "schnellru"
-version = "0.2.3"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c9a8ef13a93c54d20580de1e5c413e624e53121d42fc7e2c11d10ef7f8b02367"
+checksum = "356285bbf17bea63d9e52e96bd18f039672ac92b55b8cb997d6162a2a37d1649"
 dependencies = [
  "ahash",
  "cfg-if",
@@ -3209,9 +3277,9 @@ dependencies = [
 
 [[package]]
 name = "security-framework-sys"
-version = "2.13.0"
+version = "2.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5"
+checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32"
 dependencies = [
  "core-foundation-sys",
  "libc",
@@ -3228,9 +3296,9 @@ dependencies = [
 
 [[package]]
 name = "semver"
-version = "1.0.24"
+version = "1.0.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
+checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03"
 
 [[package]]
 name = "semver-parser"
@@ -3258,14 +3326,14 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.134"
+version = "1.0.137"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d"
+checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b"
 dependencies = [
  "itoa",
  "memchr",
@@ -3433,7 +3501,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "rustversion",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
 
 [[package]]
@@ -3455,9 +3523,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.94"
+version = "2.0.96"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "987bc0be1cdea8b10216bd06e2ca407d40b9543468fafd3ddfb02f36e77f71f3"
+checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -3466,14 +3534,14 @@ dependencies = [
 
 [[package]]
 name = "syn-solidity"
-version = "0.8.16"
+version = "0.8.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c74af950d86ec0f5b2ae2d7f1590bbfbcf4603a0a15742d8f98132ac4fe3efd4"
+checksum = "b84e4d83a0a6704561302b917a932484e1cae2d8c6354c64be8b7bac1c1fe057"
 dependencies = [
  "paste",
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
 
 [[package]]
@@ -3493,7 +3561,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
 
 [[package]]
@@ -3504,12 +3572,13 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
 
 [[package]]
 name = "tempfile"
-version = "3.14.0"
+version = "3.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
+checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91"
 dependencies = [
  "cfg-if",
  "fastrand",
+ "getrandom 0.3.1",
  "once_cell",
  "rustix",
  "windows-sys 0.59.0",
@@ -3517,42 +3586,22 @@ dependencies = [
 
 [[package]]
 name = "thiserror"
-version = "1.0.69"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
-dependencies = [
- "thiserror-impl 1.0.69",
-]
-
-[[package]]
-name = "thiserror"
-version = "2.0.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc"
-dependencies = [
- "thiserror-impl 2.0.9",
-]
-
-[[package]]
-name = "thiserror-impl"
-version = "1.0.69"
+version = "2.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
 dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.94",
+ "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "2.0.9"
+version = "2.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4"
+checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
 
 [[package]]
@@ -3600,9 +3649,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
 
 [[package]]
 name = "tokio"
-version = "1.42.0"
+version = "1.43.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551"
+checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e"
 dependencies = [
  "backtrace",
  "bytes",
@@ -3618,13 +3667,13 @@ dependencies = [
 
 [[package]]
 name = "tokio-macros"
-version = "2.4.0"
+version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
+checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
 
 [[package]]
@@ -3735,7 +3784,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
 
 [[package]]
@@ -3785,9 +3834,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
 
 [[package]]
 name = "unicode-ident"
-version = "1.0.14"
+version = "1.0.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
+checksum = "11cd88e12b17c6494200a9c1b683a04fcac9573ed74cd1b62aeb2727c5592243"
 
 [[package]]
 name = "unicode-xid"
@@ -3832,9 +3881,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
 
 [[package]]
 name = "valuable"
-version = "0.1.0"
+version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
 
 [[package]]
 name = "vcpkg"
@@ -3872,36 +3921,46 @@ version = "0.11.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
+[[package]]
+name = "wasi"
+version = "0.13.3+wasi-0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
+dependencies = [
+ "wit-bindgen-rt",
+]
+
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.99"
+version = "0.2.100"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
+checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
 dependencies = [
  "cfg-if",
  "once_cell",
+ "rustversion",
  "wasm-bindgen-macro",
 ]
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.99"
+version = "0.2.100"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
+checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
 dependencies = [
  "bumpalo",
  "log",
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-futures"
-version = "0.4.49"
+version = "0.4.50"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2"
+checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61"
 dependencies = [
  "cfg-if",
  "js-sys",
@@ -3912,9 +3971,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.99"
+version = "0.2.100"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
+checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -3922,22 +3981,25 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.99"
+version = "0.2.100"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
+checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.99"
+version = "0.2.100"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
+checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
+dependencies = [
+ "unicode-ident",
+]
 
 [[package]]
 name = "wasmtimer"
@@ -3955,9 +4017,9 @@ dependencies = [
 
 [[package]]
 name = "web-sys"
-version = "0.3.76"
+version = "0.3.77"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc"
+checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
@@ -3982,18 +4044,6 @@ dependencies = [
  "rustls-pki-types",
 ]
 
-[[package]]
-name = "which"
-version = "4.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
-dependencies = [
- "either",
- "home",
- "once_cell",
- "rustix",
-]
-
 [[package]]
 name = "windows-registry"
 version = "0.2.0"
@@ -4108,13 +4158,22 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
 
 [[package]]
 name = "winnow"
-version = "0.6.21"
+version = "0.6.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6f5bb5257f2407a5425c6e749bfd9692192a73e70a6060516ac04f889087d68"
+checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a"
 dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "wit-bindgen-rt"
+version = "0.33.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
+dependencies = [
+ "bitflags",
+]
+
 [[package]]
 name = "write16"
 version = "1.0.0"
@@ -4156,7 +4215,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
  "synstructure",
 ]
 
@@ -4178,7 +4237,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
 
 [[package]]
@@ -4198,7 +4257,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
  "synstructure",
 ]
 
@@ -4219,7 +4278,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
 
 [[package]]
@@ -4241,5 +4300,5 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.94",
+ "syn 2.0.96",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index 8dd8ad8f..b8e4fe0c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -72,12 +72,13 @@ cartesi-prt-core = { path = "prt/client-rs/core" }
 cartesi-rollups-contracts = "=2.0.0-rc.13"
 
 # eth
-alloy = { version = "0.8", features = ["sol-types", "contract", "network", "reqwest", "signers", "signer-local"] }
+alloy = { version = "0.8", features = ["contract", "network", "reqwest", "rpc-types", "signers", "signer-local", "sol-types"] }
 ruint = "1.12"
+tiny-keccak = { version = "2.0", features = ["keccak"] }
 
 # error handling
 anyhow = "1.0"
-thiserror = "1.0"
+thiserror = "2.0"
 
 # async
 async-recursion = "1"
diff --git a/cartesi-rollups/node/blockchain-reader/Cargo.toml b/cartesi-rollups/node/blockchain-reader/Cargo.toml
index e83b9329..d99116dc 100644
--- a/cartesi-rollups/node/blockchain-reader/Cargo.toml
+++ b/cartesi-rollups/node/blockchain-reader/Cargo.toml
@@ -16,7 +16,6 @@ cartesi-dave-contracts = { workspace = true }
 cartesi-rollups-contracts = { workspace = true }
 
 alloy = { workspace = true }
-alloy-rpc-types-eth = "0.8.0"
 async-recursion = { workspace = true }
 clap = { workspace = true }
 log = { workspace = true }
@@ -25,7 +24,7 @@ tokio = { workspace = true }
 num-traits = { workspace = true }
 
 [dev-dependencies]
-alloy = { workspace = true, features = ["node-bindings"] }
+alloy = { workspace = true, features = ["node-bindings", "rpc-types"] }
 cartesi-dave-merkle = { workspace = true }
 cartesi-prt-core = { workspace = true }
 cartesi-prt-contracts = { workspace = true }
diff --git a/cartesi-rollups/node/blockchain-reader/src/lib.rs b/cartesi-rollups/node/blockchain-reader/src/lib.rs
index c0f4a631..8fe1be75 100644
--- a/cartesi-rollups/node/blockchain-reader/src/lib.rs
+++ b/cartesi-rollups/node/blockchain-reader/src/lib.rs
@@ -8,19 +8,18 @@ use alloy::{
     contract::{Error, Event},
     eips::BlockNumberOrTag::Finalized,
     hex::ToHexExt,
-    primitives::Address,
+    primitives::{Address, U256},
     providers::{
         network::primitives::BlockTransactionsKind, Provider, ProviderBuilder, RootProvider,
     },
+    rpc::types::Topic,
     sol_types::SolEvent,
     transports::http::{reqwest::Url, Client, Http},
 };
-use alloy_rpc_types_eth::Topic;
 use async_recursion::async_recursion;
 use clap::Parser;
 use error::BlockchainReaderError;
 use log::{info, trace};
-use num_traits::cast::ToPrimitive;
 use std::{
     iter::Peekable,
     marker::{Send, Sync},
@@ -167,11 +166,11 @@ where
                 let epoch = Epoch {
                     epoch_number: e
                         .epochNumber
-                        .to_u64()
+                        .try_into()
                         .expect("fail to convert epoch number"),
                     input_index_boundary: e
                         .inputIndexUpperBound
-                        .to_u64()
+                        .try_into()
                         .expect("fail to convert epoch boundary"),
                     root_tournament: e.tournament.to_string(),
                 };
@@ -257,12 +256,7 @@ where
         let mut inputs = vec![];
 
         while let Some(input_added) = input_events_peekable.peek() {
-            if input_added
-                .index
-                .to_u64()
-                .expect("fail to convert input index")
-                >= input_index_boundary
-            {
+            if input_added.index >= U256::from(input_index_boundary) {
                 break;
             }
             let input = Input {
@@ -319,9 +313,9 @@ impl<E: SolEvent + Send + Sync> EventReader<E> {
                 current_finalized,
             )
             .await
-            .map_err(|err_arr| ProviderErrors(err_arr))?;
+            .map_err(ProviderErrors)?;
 
-        return Ok(logs);
+        Ok(logs)
     }
 }
 
@@ -362,7 +356,7 @@ impl PartitionProvider {
             let mut e = Event::new_sol(&self.inner, read_from)
                 .from_block(start_block)
                 .to_block(end_block)
-                .event(&E::SIGNATURE);
+                .event(E::SIGNATURE);
 
             if let Some(t) = topic1 {
                 e = e.topic1(t.clone());
diff --git a/cartesi-rollups/node/machine-runner/src/error.rs b/cartesi-rollups/node/machine-runner/src/error.rs
index 722ab12d..a22ef36e 100644
--- a/cartesi-rollups/node/machine-runner/src/error.rs
+++ b/cartesi-rollups/node/machine-runner/src/error.rs
@@ -2,7 +2,7 @@
 // SPDX-License-Identifier: Apache-2.0 (see LICENSE)
 
 use cartesi_dave_merkle::DigestError;
-use cartesi_machine::errors::MachineError;
+use cartesi_machine::error::MachineError;
 use rollups_state_manager::StateManager;
 
 use thiserror::Error;
diff --git a/cartesi-rollups/node/machine-runner/src/lib.rs b/cartesi-rollups/node/machine-runner/src/lib.rs
index c12438c6..bf60034f 100644
--- a/cartesi-rollups/node/machine-runner/src/lib.rs
+++ b/cartesi-rollups/node/machine-runner/src/lib.rs
@@ -13,7 +13,10 @@ use std::{
 
 use cartesi_dave_merkle::{Digest, DigestError, MerkleBuilder};
 use cartesi_machine::{
-    break_reason, configuration::RuntimeConfig, hash::Hash, htif, machine::Machine,
+    config::runtime::RuntimeConfig,
+    constants::break_reason,
+    machine::Machine,
+    types::{cmio::CmioResponseReason, Hash},
 };
 use cartesi_prt_core::machine::constants::{LOG2_EMULATOR_SPAN, LOG2_INPUT_SPAN, LOG2_UARCH_SPAN};
 use rollups_state_manager::{InputId, StateManager};
@@ -52,7 +55,7 @@ where
             None => (initial_machine.to_string(), 0, 0),
         };
 
-        let machine = Machine::load(Path::new(&snapshot), RuntimeConfig::default())?;
+        let machine = Machine::load(Path::new(&snapshot), &RuntimeConfig::default())?;
 
         Ok(Self {
             machine,
@@ -140,10 +143,7 @@ where
         if state_hashes.is_empty() {
             // no inputs in current epoch, add machine state hash repeatedly
             let machine_state_hash = self.add_state_hash(stride_count_in_epoch)?;
-            state_hashes.push((
-                machine_state_hash.as_bytes().to_vec(),
-                stride_count_in_epoch,
-            ));
+            state_hashes.push((machine_state_hash.to_vec(), stride_count_in_epoch));
         }
 
         let (computation_hash, total_repetitions) =
@@ -172,7 +172,7 @@ where
         self.run_machine(big_steps_in_stride)?;
 
         let mut i: u64 = 0;
-        while !self.machine.read_iflags_y()? {
+        while !self.machine.iflags_y()? {
             self.add_state_hash(1)?;
             i += 1;
             self.run_machine(big_steps_in_stride)?;
@@ -184,12 +184,12 @@ where
 
     fn feed_input(&mut self, input: &[u8]) -> Result<(), SM> {
         self.machine
-            .send_cmio_response(htif::fromhost::ADVANCE_STATE, input)?;
+            .send_cmio_response(CmioResponseReason::Advance, input)?;
         Ok(())
     }
 
     fn run_machine(&mut self, cycles: u64) -> Result<(), SM> {
-        let mcycle = self.machine.read_mcycle()?;
+        let mcycle = self.machine.mcycle()?;
 
         loop {
             let reason = self.machine.run(mcycle + cycles)?;
@@ -204,10 +204,10 @@ where
     }
 
     fn add_state_hash(&mut self, repetitions: u64) -> Result<Hash, SM> {
-        let machine_state_hash = self.machine.get_root_hash()?;
+        let machine_state_hash = self.machine.root_hash()?;
         self.state_manager
             .add_machine_state_hash(
-                machine_state_hash.as_bytes(),
+                &machine_state_hash,
                 self.epoch_number,
                 self.state_hash_index_in_epoch,
                 repetitions,
@@ -218,7 +218,7 @@ where
         Ok(machine_state_hash)
     }
 
-    fn take_snapshot(&self) -> Result<(), SM> {
+    fn take_snapshot(&mut self) -> Result<(), SM> {
         let epoch_path = self
             .state_dir
             .join("snapshots")
diff --git a/common-rs/merkle/Cargo.toml b/common-rs/merkle/Cargo.toml
index 2cc1af05..b389125f 100644
--- a/common-rs/merkle/Cargo.toml
+++ b/common-rs/merkle/Cargo.toml
@@ -15,5 +15,5 @@ alloy = { workspace = true, features = ["sol-types"] }
 ruint = { workspace = true }
 
 hex = "0.4"
-sha3 = "0.10"
-thiserror = "1.0"
+tiny-keccak = { workspace = true }
+thiserror = { workspace = true }
diff --git a/common-rs/merkle/src/digest/keccak.rs b/common-rs/merkle/src/digest/keccak.rs
index 65bc856e..df350afe 100644
--- a/common-rs/merkle/src/digest/keccak.rs
+++ b/common-rs/merkle/src/digest/keccak.rs
@@ -1,24 +1,60 @@
 //! Keccak256 hash for the Digest Type. It's used to hash the data in the Digest.
 
-use sha3::{Digest as Keccak256Digest, Keccak256};
+use tiny_keccak::{Hasher, Keccak};
 
 use super::Digest;
 
 impl Digest {
     /// Computes the Keccak256 hash of the given data and returns a new Digest.
     pub fn from_data(data: &[u8]) -> Digest {
-        let mut keccak = Keccak256::new();
+        let mut keccak = Keccak::v256();
         keccak.update(data);
-        let digest: [u8; 32] = keccak.finalize().into();
+        let mut digest: [u8; 32] = [0; 32];
+        keccak.finalize(&mut digest);
         Digest::from(digest)
     }
 
     /// Joins the current Digest with another Digest to create a new Digest.
     pub fn join(&self, digest: &Digest) -> Digest {
-        let mut keccak = Keccak256::new();
-        keccak.update(self.data);
-        keccak.update(digest.data);
-        let digest: [u8; 32] = keccak.finalize().into();
+        let mut keccak = Keccak::v256();
+        keccak.update(&self.data);
+        keccak.update(&digest.data);
+        let mut digest: [u8; 32] = [0; 32];
+        keccak.finalize(&mut digest);
         Digest::from(digest)
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::Digest;
+
+    fn assert_data_eq(expected_digest_hex: &str, digest: Digest) {
+        assert_eq!(
+            Digest::from_digest_hex(expected_digest_hex).expect("invalid hex"),
+            digest
+        );
+    }
+
+    #[test]
+    fn test_from_data() {
+        assert_data_eq(
+            "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
+            Digest::from_data(&[]), // cast keccak ""
+        );
+
+        assert_data_eq(
+            "0x6228290203658fd4987e40cbb257cabf258f9c288cdee767eaba6b234a73a2f9",
+            Digest::from_data("bananas".as_bytes()), // cast keccak "bananas"
+        );
+    }
+
+    #[test]
+    fn test_join() {
+        assert_data_eq(
+            "0x4441036546894c6fcf905b48b722f6b149ec0902955a6445c63cfec478568268",
+            // cast keccak (cast concat-hex (cast keccak "minhas") (cast keccak "bananas"))
+            Digest::from_data("minhas".as_bytes()).join(&Digest::from_data("bananas".as_bytes())),
+        );
+    }
+}
diff --git a/common-rs/merkle/src/digest/mod.rs b/common-rs/merkle/src/digest/mod.rs
index d90c8bcc..c60b468f 100644
--- a/common-rs/merkle/src/digest/mod.rs
+++ b/common-rs/merkle/src/digest/mod.rs
@@ -1,15 +1,13 @@
 //! Definition of the [Digest] type and its associated methods. A digest is the output of a hash
 //! function. It's used to identify the data in the MerkleTree.
 
-use alloy::sol_types::private::B256;
+use alloy::primitives::B256;
 use hex::FromHex;
 use std::fmt;
+use thiserror::Error;
 
 pub mod keccak;
 
-use hex;
-use thiserror::Error;
-
 const HASH_SIZE: usize = 32;
 
 #[derive(Error, Debug)]
diff --git a/machine/emulator b/machine/emulator
index b7efb64a..b5951e42 160000
--- a/machine/emulator
+++ b/machine/emulator
@@ -1 +1 @@
-Subproject commit b7efb64a92c552d9307d3c7581f1de4de94ababb
+Subproject commit b5951e42257c6054c4713bfface2b7c5d2b11287
diff --git a/machine/rust-bindings/cartesi-machine-sys/Cargo.toml b/machine/rust-bindings/cartesi-machine-sys/Cargo.toml
index 363ded2b..40ecde5a 100644
--- a/machine/rust-bindings/cartesi-machine-sys/Cargo.toml
+++ b/machine/rust-bindings/cartesi-machine-sys/Cargo.toml
@@ -34,7 +34,7 @@ link-cplusplus = "1.0"
 
 
 [build-dependencies]
-bindgen = "0.69"
+bindgen = "0.71"
 cfg-if = "1.0"
 
 hex-literal = "0.4.1"
diff --git a/machine/rust-bindings/cartesi-machine-sys/build.rs b/machine/rust-bindings/cartesi-machine-sys/build.rs
index 3e898a0f..533797f4 100644
--- a/machine/rust-bindings/cartesi-machine-sys/build.rs
+++ b/machine/rust-bindings/cartesi-machine-sys/build.rs
@@ -1,27 +1,7 @@
 // (c) Cartesi and individual authors (see AUTHORS)
 // SPDX-License-Identifier: Apache-2.0 (see LICENSE)
-
 use std::{env, path::PathBuf, process::Command};
 
-mod feature_checks {
-    #[cfg(all(feature = "build_uarch", feature = "copy_uarch",))]
-    compile_error!("Features `build_uarch` and `copy_uarch` are mutually exclusive");
-
-    #[cfg(all(feature = "build_uarch", feature = "download_uarch"))]
-    compile_error!("Features `build_uarch` and `download_uarch` are mutually exclusive");
-
-    #[cfg(all(feature = "copy_uarch", feature = "download_uarch"))]
-    compile_error!("Features `copy_uarch`, and `download_uarch` are mutually exclusive");
-
-    #[cfg(not(any(
-        feature = "copy_uarch",
-        feature = "download_uarch",
-        feature = "build_uarch",
-        feature = "external_cartesi",
-    )))]
-    compile_error!("At least one of `build_uarch`, `copy_uarch`, `download_uarch`, and `external_cartesi` must be set");
-}
-
 fn main() {
     let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
 
@@ -62,6 +42,7 @@ fn main() {
     //  Generate bindings
     //
 
+    // find headers
     #[allow(clippy::needless_late_init)]
     let include_path;
     cfg_if::cfg_if! {
@@ -75,32 +56,21 @@ fn main() {
         }
     };
 
-    // machine api
+    // generate machine api
     let machine_bindings = bindgen::Builder::default()
         .header(include_path.join("machine-c-api.h").to_str().unwrap())
+        .allowlist_item("^cm_.*")
+        .allowlist_item("^CM_.*")
+        .merge_extern_blocks(true)
+        .prepend_enum_name(false)
+        .translate_enum_integer_types(true)
         .generate()
         .expect("Unable to generate machine bindings");
 
-    // htif constants
-    let htif = bindgen::Builder::default()
-        .header(include_path.join("htif-defines.h").to_str().unwrap())
-        .generate()
-        .expect("Unable to generate htif bindings");
-
-    // pma constants
-    let pma = bindgen::Builder::default()
-        .header(include_path.join("pma-defines.h").to_str().unwrap())
-        .generate()
-        .expect("Unable to generate pma bindings");
-
     // Write the bindings to the `$OUT_DIR/bindings.rs` and `$OUT_DIR/htif.rs` files.
     machine_bindings
         .write_to_file(out_path.join("bindings.rs"))
         .expect("Couldn't write machine bindings");
-    htif.write_to_file(out_path.join("htif.rs"))
-        .expect("Couldn't write htif defines");
-    pma.write_to_file(out_path.join("pma.rs"))
-        .expect("Couldn't write pma defines");
 
     // Setup reruns
     println!("cargo:rerun-if-changed=build.rs");
@@ -120,6 +90,7 @@ mod build_cm {
         // Get uarch
         cfg_if::cfg_if! {
             if #[cfg(feature = "build_uarch")] {
+                // requires docker
                 ()
             } else if #[cfg(feature = "copy_uarch")] {
                 let uarch_path = machine_dir_path.join("uarch");
@@ -131,7 +102,9 @@ mod build_cm {
             }
         }
 
+        //
         // Build and link emulator
+        //
 
         // build dependencies
         Command::new("make")
@@ -210,11 +183,12 @@ mod build_cm {
             process::{Command, Stdio},
         };
 
+        const VERSION_STRING: &str = "v0.19.0-test2";
+
         pub fn download(machine_dir_path: &Path) {
-            // apply git patche for 0.18.1
             let patch_file = machine_dir_path.join("add-generated-files.diff");
 
-            download_git_patch(&patch_file, "v0.18.1");
+            download_git_patch(&patch_file, VERSION_STRING);
             apply_git_patch(&patch_file, machine_dir_path);
         }
 
@@ -241,9 +215,9 @@ mod build_cm {
             // Open the patch file
             let mut patch = fs::File::open(patch_file).expect("fail to open patch file");
 
-            // Create a command to run `patch -Np0`
+            // Create a command to run `patch -Np1`
             let mut cmd = Command::new("patch")
-                .arg("-Np0")
+                .arg("-Np1")
                 .stdin(Stdio::piped())
                 .current_dir(target_dir)
                 .spawn()
@@ -282,7 +256,26 @@ mod build_cm {
     }
 }
 
-#[allow(dead_code)]
+mod feature_checks {
+    #[cfg(all(feature = "build_uarch", feature = "copy_uarch",))]
+    compile_error!("Features `build_uarch` and `copy_uarch` are mutually exclusive");
+
+    #[cfg(all(feature = "build_uarch", feature = "download_uarch"))]
+    compile_error!("Features `build_uarch` and `download_uarch` are mutually exclusive");
+
+    #[cfg(all(feature = "copy_uarch", feature = "download_uarch"))]
+    compile_error!("Features `copy_uarch`, and `download_uarch` are mutually exclusive");
+
+    #[cfg(not(any(
+        feature = "copy_uarch",
+        feature = "download_uarch",
+        feature = "build_uarch",
+        feature = "external_cartesi",
+    )))]
+    compile_error!("At least one of `build_uarch`, `copy_uarch`, `download_uarch`, and `external_cartesi` must be set");
+}
+
+#[allow(unused)]
 fn clean(path: &PathBuf) {
     // clean build artifacts
     Command::new("make")
diff --git a/machine/rust-bindings/cartesi-machine-sys/src/lib.rs b/machine/rust-bindings/cartesi-machine-sys/src/lib.rs
index 133a4417..04af9171 100644
--- a/machine/rust-bindings/cartesi-machine-sys/src/lib.rs
+++ b/machine/rust-bindings/cartesi-machine-sys/src/lib.rs
@@ -5,5 +5,5 @@
 
 extern crate link_cplusplus;
 include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
-include!(concat!(env!("OUT_DIR"), "/htif.rs"));
-include!(concat!(env!("OUT_DIR"), "/pma.rs"));
+// include!(concat!(env!("OUT_DIR"), "/htif.rs"));
+// include!(concat!(env!("OUT_DIR"), "/pma.rs"));
diff --git a/machine/rust-bindings/cartesi-machine/Cargo.toml b/machine/rust-bindings/cartesi-machine/Cargo.toml
index ad414c41..ae8286cd 100644
--- a/machine/rust-bindings/cartesi-machine/Cargo.toml
+++ b/machine/rust-bindings/cartesi-machine/Cargo.toml
@@ -17,5 +17,12 @@ remote_machine = ["cartesi-machine-sys/remote_machine"]
 [dependencies]
 cartesi-machine-sys = { path = "../cartesi-machine-sys" }
 
-hex = "0.4.3"
-thiserror = "1.0"
+base64 = "0.22"
+derive_builder = "0.20"
+hex = "0.4"
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
+thiserror = "2.0"
+
+[dev-dependencies]
+tempfile = "3.16"
diff --git a/machine/rust-bindings/cartesi-machine/src/config/machine.rs b/machine/rust-bindings/cartesi-machine/src/config/machine.rs
new file mode 100644
index 00000000..e599ed6f
--- /dev/null
+++ b/machine/rust-bindings/cartesi-machine/src/config/machine.rs
@@ -0,0 +1,426 @@
+// (c) Cartesi and individual authors (see AUTHORS)
+// SPDX-License-Identifier: Apache-2.0 (see LICENSE)
+
+use serde::{Deserialize, Serialize};
+use std::path::PathBuf;
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct MachineConfig {
+    pub processor: ProcessorConfig,
+    pub ram: RAMConfig,
+    pub dtb: DTBConfig,
+    pub flash_drive: FlashDriveConfigs,
+    pub tlb: TLBConfig,
+    pub clint: CLINTConfig,
+    pub plic: PLICConfig,
+    pub htif: HTIFConfig,
+    pub uarch: UarchConfig,
+    pub cmio: CmioConfig,
+    pub virtio: VirtIOConfigs,
+}
+
+impl MachineConfig {
+    pub fn new_with_ram(ram: RAMConfig) -> Self {
+        Self {
+            processor: ProcessorConfig::default(),
+            ram,
+            dtb: DTBConfig::default(),
+            flash_drive: FlashDriveConfigs::default(),
+            tlb: TLBConfig::default(),
+            clint: CLINTConfig::default(),
+            plic: PLICConfig::default(),
+            htif: HTIFConfig::default(),
+            uarch: UarchConfig::default(),
+            cmio: CmioConfig::default(),
+            virtio: VirtIOConfigs::default(),
+        }
+    }
+
+    pub fn processor(mut self, processor: ProcessorConfig) -> Self {
+        self.processor = processor;
+        self
+    }
+
+    pub fn dtb(mut self, dtb: DTBConfig) -> Self {
+        self.dtb = dtb;
+        self
+    }
+
+    pub fn add_flash_drive(mut self, flash_drive: MemoryRangeConfig) -> Self {
+        self.flash_drive.push(flash_drive);
+        self
+    }
+
+    pub fn tlb(mut self, tlb: TLBConfig) -> Self {
+        self.tlb = tlb;
+        self
+    }
+
+    pub fn clint(mut self, clint: CLINTConfig) -> Self {
+        self.clint = clint;
+        self
+    }
+
+    pub fn plic(mut self, plic: PLICConfig) -> Self {
+        self.plic = plic;
+        self
+    }
+
+    pub fn htif(mut self, htif: HTIFConfig) -> Self {
+        self.htif = htif;
+        self
+    }
+
+    pub fn uarch(mut self, uarch: UarchConfig) -> Self {
+        self.uarch = uarch;
+        self
+    }
+
+    pub fn cmio(mut self, cmio: CmioConfig) -> Self {
+        self.cmio = cmio;
+        self
+    }
+
+    pub fn add_virtio(mut self, virtio_config: VirtIODeviceConfig) -> Self {
+        self.virtio.push(virtio_config);
+        self
+    }
+}
+
+fn default_config() -> MachineConfig {
+    crate::machine::Machine::default_config().expect("failed to get default config")
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct ProcessorConfig {
+    pub x0: u64,
+    pub x1: u64,
+    pub x2: u64,
+    pub x3: u64,
+    pub x4: u64,
+    pub x5: u64,
+    pub x6: u64,
+    pub x7: u64,
+    pub x8: u64,
+    pub x9: u64,
+    pub x10: u64,
+    pub x11: u64,
+    pub x12: u64,
+    pub x13: u64,
+    pub x14: u64,
+    pub x15: u64,
+    pub x16: u64,
+    pub x17: u64,
+    pub x18: u64,
+    pub x19: u64,
+    pub x20: u64,
+    pub x21: u64,
+    pub x22: u64,
+    pub x23: u64,
+    pub x24: u64,
+    pub x25: u64,
+    pub x26: u64,
+    pub x27: u64,
+    pub x28: u64,
+    pub x29: u64,
+    pub x30: u64,
+    pub x31: u64,
+    pub f0: u64,
+    pub f1: u64,
+    pub f2: u64,
+    pub f3: u64,
+    pub f4: u64,
+    pub f5: u64,
+    pub f6: u64,
+    pub f7: u64,
+    pub f8: u64,
+    pub f9: u64,
+    pub f10: u64,
+    pub f11: u64,
+    pub f12: u64,
+    pub f13: u64,
+    pub f14: u64,
+    pub f15: u64,
+    pub f16: u64,
+    pub f17: u64,
+    pub f18: u64,
+    pub f19: u64,
+    pub f20: u64,
+    pub f21: u64,
+    pub f22: u64,
+    pub f23: u64,
+    pub f24: u64,
+    pub f25: u64,
+    pub f26: u64,
+    pub f27: u64,
+    pub f28: u64,
+    pub f29: u64,
+    pub f30: u64,
+    pub f31: u64,
+    pub pc: u64,
+    pub fcsr: u64,
+    pub mvendorid: u64,
+    pub marchid: u64,
+    pub mimpid: u64,
+    pub mcycle: u64,
+    pub icycleinstret: u64,
+    pub mstatus: u64,
+    pub mtvec: u64,
+    pub mscratch: u64,
+    pub mepc: u64,
+    pub mcause: u64,
+    pub mtval: u64,
+    pub misa: u64,
+    pub mie: u64,
+    pub mip: u64,
+    pub medeleg: u64,
+    pub mideleg: u64,
+    pub mcounteren: u64,
+    pub menvcfg: u64,
+    pub stvec: u64,
+    pub sscratch: u64,
+    pub sepc: u64,
+    pub scause: u64,
+    pub stval: u64,
+    pub satp: u64,
+    pub scounteren: u64,
+    pub senvcfg: u64,
+    pub ilrsc: u64,
+    pub iprv: u64,
+    #[serde(rename = "iflags_X")]
+    pub iflags_x: u64,
+    #[serde(rename = "iflags_Y")]
+    pub iflags_y: u64,
+    #[serde(rename = "iflags_H")]
+    pub iflags_h: u64,
+    pub iunrep: u64,
+}
+
+impl Default for ProcessorConfig {
+    fn default() -> Self {
+        default_config().processor
+    }
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct RAMConfig {
+    pub length: u64,
+    pub image_filename: PathBuf,
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct DTBConfig {
+    pub bootargs: String,
+    pub init: String,
+    pub entrypoint: String,
+    pub image_filename: PathBuf,
+}
+
+impl Default for DTBConfig {
+    fn default() -> Self {
+        default_config().dtb
+    }
+}
+
+#[derive(Clone, Debug, Default, Serialize, Deserialize)]
+pub struct MemoryRangeConfig {
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub start: Option<u64>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub length: Option<u64>,
+    pub image_filename: PathBuf,
+    pub shared: bool,
+}
+
+#[derive(Clone, Debug, Default, Serialize, Deserialize)]
+pub struct CmioBufferConfig {
+    pub image_filename: PathBuf,
+    pub shared: bool,
+}
+
+#[derive(Clone, Debug, Default, Serialize, Deserialize)]
+pub struct VirtIOHostfwd {
+    pub is_udp: bool,
+    pub host_ip: u64,
+    pub guest_ip: u64,
+    pub host_port: u64,
+    pub guest_port: u64,
+}
+
+pub type VirtIOHostfwdArray = Vec<VirtIOHostfwd>;
+
+#[derive(Clone, Debug, Default, Serialize, Deserialize)]
+#[serde(rename_all = "kebab-case")]
+pub enum VirtIODeviceType {
+    #[default]
+    Console,
+    P9fs,
+    #[serde(rename = "net-user")]
+    NetUser,
+    #[serde(rename = "net-tuntap")]
+    NetTuntap,
+}
+
+#[derive(Clone, Debug, Default, Serialize, Deserialize)]
+pub struct VirtIODeviceConfig {
+    pub r#type: VirtIODeviceType,
+    pub tag: String,
+    pub host_directory: String,
+    pub hostfwd: VirtIOHostfwdArray,
+    pub iface: String,
+}
+
+pub type FlashDriveConfigs = Vec<MemoryRangeConfig>;
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct TLBConfig {
+    pub image_filename: PathBuf,
+}
+
+impl Default for TLBConfig {
+    fn default() -> Self {
+        default_config().tlb
+    }
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct CLINTConfig {
+    pub mtimecmp: u64,
+}
+
+impl Default for CLINTConfig {
+    fn default() -> Self {
+        default_config().clint
+    }
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct PLICConfig {
+    pub girqpend: u64,
+    pub girqsrvd: u64,
+}
+
+impl Default for PLICConfig {
+    fn default() -> Self {
+        default_config().plic
+    }
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct HTIFConfig {
+    pub fromhost: u64,
+    pub tohost: u64,
+    pub console_getchar: bool,
+    pub yield_manual: bool,
+    pub yield_automatic: bool,
+}
+
+impl Default for HTIFConfig {
+    fn default() -> Self {
+        default_config().htif
+    }
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct UarchProcessorConfig {
+    pub x0: u64,
+    pub x1: u64,
+    pub x2: u64,
+    pub x3: u64,
+    pub x4: u64,
+    pub x5: u64,
+    pub x6: u64,
+    pub x7: u64,
+    pub x8: u64,
+    pub x9: u64,
+    pub x10: u64,
+    pub x11: u64,
+    pub x12: u64,
+    pub x13: u64,
+    pub x14: u64,
+    pub x15: u64,
+    pub x16: u64,
+    pub x17: u64,
+    pub x18: u64,
+    pub x19: u64,
+    pub x20: u64,
+    pub x21: u64,
+    pub x22: u64,
+    pub x23: u64,
+    pub x24: u64,
+    pub x25: u64,
+    pub x26: u64,
+    pub x27: u64,
+    pub x28: u64,
+    pub x29: u64,
+    pub x30: u64,
+    pub x31: u64,
+    pub pc: u64,
+    pub cycle: u64,
+    pub halt_flag: bool,
+}
+
+impl Default for UarchProcessorConfig {
+    fn default() -> Self {
+        default_config().uarch.processor
+    }
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct UarchRAMConfig {
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub length: Option<u64>,
+    pub image_filename: PathBuf,
+}
+
+impl Default for UarchRAMConfig {
+    fn default() -> Self {
+        default_config().uarch.ram
+    }
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct UarchConfig {
+    pub processor: UarchProcessorConfig,
+    pub ram: UarchRAMConfig,
+}
+
+impl Default for UarchConfig {
+    fn default() -> Self {
+        default_config().uarch
+    }
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct CmioConfig {
+    pub rx_buffer: CmioBufferConfig,
+    pub tx_buffer: CmioBufferConfig,
+}
+
+impl Default for CmioConfig {
+    fn default() -> Self {
+        default_config().cmio
+    }
+}
+
+pub type VirtIOConfigs = Vec<VirtIODeviceConfig>;
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_default_configs() {
+        default_config();
+        ProcessorConfig::default();
+        DTBConfig::default();
+        TLBConfig::default();
+        CLINTConfig::default();
+        PLICConfig::default();
+        HTIFConfig::default();
+        UarchProcessorConfig::default();
+        UarchRAMConfig::default();
+        UarchConfig::default();
+        CmioConfig::default();
+    }
+}
diff --git a/machine/rust-bindings/cartesi-machine/src/config/mod.rs b/machine/rust-bindings/cartesi-machine/src/config/mod.rs
new file mode 100644
index 00000000..89708d9f
--- /dev/null
+++ b/machine/rust-bindings/cartesi-machine/src/config/mod.rs
@@ -0,0 +1,5 @@
+// (c) Cartesi and individual authors (see AUTHORS)
+// SPDX-License-Identifier: Apache-2.0 (see LICENSE)
+
+pub mod machine;
+pub mod runtime;
diff --git a/machine/rust-bindings/cartesi-machine/src/config/runtime.rs b/machine/rust-bindings/cartesi-machine/src/config/runtime.rs
new file mode 100644
index 00000000..6f131d4a
--- /dev/null
+++ b/machine/rust-bindings/cartesi-machine/src/config/runtime.rs
@@ -0,0 +1,32 @@
+// (c) Cartesi and individual authors (see AUTHORS)
+// SPDX-License-Identifier: Apache-2.0 (see LICENSE)
+
+use serde::{Deserialize, Serialize};
+
+#[derive(Clone, Debug, Default, Serialize, Deserialize)]
+pub struct ConcurrencyRuntimeConfig {
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub update_merkle_tree: Option<u64>,
+}
+
+#[derive(Clone, Debug, Default, Serialize, Deserialize)]
+pub struct HTIFRuntimeConfig {
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub no_console_putchar: Option<bool>,
+}
+
+#[derive(Clone, Debug, Default, Serialize, Deserialize)]
+pub struct RuntimeConfig {
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub concurrency: Option<ConcurrencyRuntimeConfig>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub htif: Option<HTIFRuntimeConfig>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub skip_root_hash_check: Option<bool>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub skip_root_hash_store: Option<bool>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub skip_version_check: Option<bool>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub soft_yield: Option<bool>,
+}
diff --git a/machine/rust-bindings/cartesi-machine/src/configuration.rs b/machine/rust-bindings/cartesi-machine/src/configuration.rs
deleted file mode 100644
index 90e248ec..00000000
--- a/machine/rust-bindings/cartesi-machine/src/configuration.rs
+++ /dev/null
@@ -1,115 +0,0 @@
-// (c) Cartesi and individual authors (see AUTHORS)
-// SPDX-License-Identifier: Apache-2.0 (see LICENSE)
-
-pub type MemoryRangeConfig = cartesi_machine_sys::cm_memory_range_config;
-pub type CmIoConfig = cartesi_machine_sys::cm_cmio_config;
-pub type CmIoBufferConfig = cartesi_machine_sys::cm_cmio_buffer_config;
-pub type HtifConfig = cartesi_machine_sys::cm_htif_config;
-
-pub type MachineConfig = cartesi_machine_sys::cm_machine_config;
-
-#[derive(Debug)]
-pub struct MachineConfigRef {
-    config: *const MachineConfig,
-}
-
-impl Drop for MachineConfigRef {
-    fn drop(&mut self) {
-        unsafe {
-            cartesi_machine_sys::cm_delete_machine_config(self.config);
-        };
-    }
-}
-
-impl Default for MachineConfigRef {
-    fn default() -> Self {
-        let mut error_collector = crate::errors::ErrorCollector::new();
-        let mut config = std::ptr::null();
-
-        let result = unsafe {
-            cartesi_machine_sys::cm_get_default_config(&mut config, error_collector.as_mut_ptr())
-        };
-        error_collector
-            .collect(result)
-            .expect("cm_get_default_config should never fail");
-
-        Self { config }
-    }
-}
-
-impl MachineConfigRef {
-    pub fn try_new(
-        machine: &crate::Machine,
-    ) -> Result<MachineConfigRef, crate::errors::MachineError> {
-        let mut error_collector = crate::errors::ErrorCollector::new();
-        let mut config = std::ptr::null();
-
-        let result = unsafe {
-            cartesi_machine_sys::cm_get_initial_config(
-                machine.machine,
-                &mut config,
-                error_collector.as_mut_ptr(),
-            )
-        };
-        error_collector.collect(result)?;
-
-        Ok(Self { config })
-    }
-
-    pub fn inner(&self) -> &MachineConfig {
-        unsafe { self.config.as_ref().unwrap() }
-    }
-}
-
-type CmRuntimeConfig = cartesi_machine_sys::cm_machine_runtime_config;
-
-#[derive(Debug, Clone)]
-pub struct RuntimeConfig {
-    pub values: CmRuntimeConfig,
-}
-
-impl Default for RuntimeConfig {
-    fn default() -> Self {
-        Self {
-            values: CmRuntimeConfig {
-                concurrency: cartesi_machine_sys::cm_concurrency_runtime_config {
-                    update_merkle_tree: 0,
-                },
-                htif: cartesi_machine_sys::cm_htif_runtime_config {
-                    no_console_putchar: true,
-                },
-                skip_root_hash_check: false,
-                skip_version_check: false,
-                soft_yield: false,
-                skip_root_hash_store: false,
-            },
-        }
-    }
-}
-
-impl RuntimeConfig {
-    pub fn concurrency(mut self, update_merkle_tree: u64) -> Self {
-        self.values.concurrency.update_merkle_tree = update_merkle_tree;
-        self
-    }
-
-    pub fn no_console_putchar(mut self, no_console_putchar: bool) -> Self {
-        self.values.htif.no_console_putchar = no_console_putchar;
-        self
-    }
-
-    pub fn skip_root_hash_check(mut self, skip_root_hash_check: bool) -> Self {
-        self.values.skip_root_hash_check = skip_root_hash_check;
-        self
-    }
-
-    pub fn skip_version_check(mut self, skip_version_check: bool) -> Self {
-        self.values.skip_version_check = skip_version_check;
-        self
-    }
-
-    pub fn soft_yield(mut self, soft_yield: bool) -> Self {
-        self.values.soft_yield = soft_yield;
-        self
-    }
-}
diff --git a/machine/rust-bindings/cartesi-machine/src/constants.rs b/machine/rust-bindings/cartesi-machine/src/constants.rs
index 859e56cc..a835c100 100644
--- a/machine/rust-bindings/cartesi-machine/src/constants.rs
+++ b/machine/rust-bindings/cartesi-machine/src/constants.rs
@@ -3,132 +3,112 @@
 
 //! Constants definitions from Cartesi Machine
 
-///
+pub mod machine {
+    use cartesi_machine_sys::*;
+    pub const CYCLE_MAX: u64 = CM_MCYCLE_MAX as u64;
+    pub const HASH_SIZE: u32 = CM_HASH_SIZE;
+    pub const TREE_LOG2_WORD_SIZE: u32 = CM_TREE_LOG2_WORD_SIZE;
+    pub const TREE_LOG2_PAGE_SIZE: u32 = CM_TREE_LOG2_PAGE_SIZE;
+    pub const TREE_LOG2_ROOT_SIZE: u32 = CM_TREE_LOG2_ROOT_SIZE;
+}
+
+pub mod pma {
+    use cartesi_machine_sys::*;
+    pub const RX_START: u64 = CM_PMA_CMIO_RX_BUFFER_START as u64;
+    pub const RX_LOG2_SIZE: u64 = CM_PMA_CMIO_RX_BUFFER_LOG2_SIZE as u64;
+    pub const TX_START: u64 = CM_PMA_CMIO_TX_BUFFER_START as u64;
+    pub const TX_LOG2_SIZE: u64 = CM_PMA_CMIO_TX_BUFFER_LOG2_SIZE as u64;
+    pub const RAM_START: u64 = CM_PMA_RAM_START as u64;
+}
+
 pub mod break_reason {
     use cartesi_machine_sys::*;
-    pub const FAILED: u32 = CM_BREAK_REASON_CM_BREAK_REASON_FAILED;
-    pub const HALTED: u32 = CM_BREAK_REASON_CM_BREAK_REASON_HALTED;
-    pub const YIELDED_MANUALLY: u32 = CM_BREAK_REASON_CM_BREAK_REASON_YIELDED_MANUALLY;
-    pub const YIELDED_AUTOMATICALLY: u32 = CM_BREAK_REASON_CM_BREAK_REASON_YIELDED_AUTOMATICALLY;
-    pub const YIELDED_SOFTLY: u32 = CM_BREAK_REASON_CM_BREAK_REASON_YIELDED_SOFTLY;
-    pub const REACHED_TARGET_MCYCLE: u32 = CM_BREAK_REASON_CM_BREAK_REASON_REACHED_TARGET_MCYCLE;
+    pub const FAILED: u32 = CM_BREAK_REASON_FAILED;
+    pub const HALTED: u32 = CM_BREAK_REASON_HALTED;
+    pub const YIELDED_MANUALLY: u32 = CM_BREAK_REASON_YIELDED_MANUALLY;
+    pub const YIELDED_AUTOMATICALLY: u32 = CM_BREAK_REASON_YIELDED_AUTOMATICALLY;
+    pub const YIELDED_SOFTLY: u32 = CM_BREAK_REASON_YIELDED_SOFTLY;
+    pub const REACHED_TARGET_MCYCLE: u32 = CM_BREAK_REASON_REACHED_TARGET_MCYCLE;
 }
 
-///
 pub mod uarch_break_reason {
     use cartesi_machine_sys::*;
-    pub const REACHED_TARGET_CYCLE: u32 =
-        CM_UARCH_BREAK_REASON_CM_UARCH_BREAK_REASON_REACHED_TARGET_CYCLE;
-    pub const UARCH_HALTED: u32 = CM_UARCH_BREAK_REASON_CM_UARCH_BREAK_REASON_UARCH_HALTED;
+    pub const REACHED_TARGET_CYCLE: u32 = CM_UARCH_BREAK_REASON_REACHED_TARGET_CYCLE;
+    pub const UARCH_HALTED: u32 = CM_UARCH_BREAK_REASON_UARCH_HALTED;
 }
 
-///
-pub mod htif {
-    pub mod masks {
-        use cartesi_machine_sys::*;
-        pub const DEVICE_SHIFT: u32 = HTIF_DEV_SHIFT_DEF;
-        pub const COMMAND_SHIFT: u32 = HTIF_CMD_SHIFT_DEF;
-        pub const DATA_SHIFT: u32 = HTIF_DATA_SHIFT_DEF;
-        pub const DEVICE_MASK: u64 = HTIF_DEV_MASK_DEF as u64;
-        pub const COMMAND_MASK: u64 = HTIF_CMD_MASK_DEF as u64;
-        pub const DATA_MASK: u64 = HTIF_DATA_MASK_DEF as u64;
-    }
-
-    /// HTIF devices
-    pub mod devices {
-        use cartesi_machine_sys::*;
-        pub const HALT: u32 = HTIF_DEV_HALT_DEF;
-        pub const CONSOLE: u32 = HTIF_DEV_CONSOLE_DEF;
-        pub const YIELD: u32 = HTIF_DEV_YIELD_DEF;
-    }
+pub mod access_log_type {
+    use cartesi_machine_sys::*;
+    pub const ANNOTATIONS: u32 = CM_ACCESS_LOG_TYPE_ANNOTATIONS;
+    pub const LARGE_DATA: u32 = CM_ACCESS_LOG_TYPE_LARGE_DATA;
+}
 
-    /// HTIF commands
+pub mod cmio {
+    /// CMIO commands
     pub mod commands {
         use cartesi_machine_sys::*;
-        pub const HALT_HALT: u32 = HTIF_HALT_CMD_HALT_DEF;
-        pub const CONSOLE_GETCHAR: u32 = HTIF_CONSOLE_CMD_GETCHAR_DEF;
-        pub const CONSOLE_PUTCHAR: u32 = HTIF_CONSOLE_CMD_PUTCHAR_DEF;
-        pub const YIELD_AUTOMATIC: u32 = HTIF_YIELD_CMD_AUTOMATIC_DEF;
-        pub const YIELD_MANUAL: u32 = HTIF_YIELD_CMD_MANUAL_DEF;
+        pub const YIELD_AUTOMATIC: u8 = CM_CMIO_YIELD_COMMAND_AUTOMATIC as u8;
+        pub const YIELD_MANUAL: u8 = CM_CMIO_YIELD_COMMAND_MANUAL as u8;
     }
 
-    /// HTIF request
+    /// CMIO request
     pub mod tohost {
         pub mod automatic {
             use cartesi_machine_sys::*;
-            pub const PROGRESS: u32 = HTIF_YIELD_AUTOMATIC_REASON_PROGRESS_DEF;
-            pub const TX_OUTPUT: u32 = HTIF_YIELD_AUTOMATIC_REASON_TX_OUTPUT_DEF;
-            pub const TX_REPORT: u32 = HTIF_YIELD_AUTOMATIC_REASON_TX_REPORT_DEF;
+            pub const PROGRESS: u16 = CM_CMIO_YIELD_AUTOMATIC_REASON_PROGRESS as u16;
+            pub const TX_OUTPUT: u16 = CM_CMIO_YIELD_AUTOMATIC_REASON_TX_OUTPUT as u16;
+            pub const TX_REPORT: u16 = CM_CMIO_YIELD_AUTOMATIC_REASON_TX_REPORT as u16;
         }
 
         pub mod manual {
             use cartesi_machine_sys::*;
-            pub const RX_ACCEPTED: u32 = HTIF_YIELD_MANUAL_REASON_RX_ACCEPTED_DEF;
-            pub const RX_REJECTED: u32 = HTIF_YIELD_MANUAL_REASON_RX_REJECTED_DEF;
-            pub const TX_EXCEPTION: u32 = HTIF_YIELD_MANUAL_REASON_TX_EXCEPTION_DEF;
+            pub const RX_ACCEPTED: u16 = CM_CMIO_YIELD_MANUAL_REASON_RX_ACCEPTED as u16;
+            pub const RX_REJECTED: u16 = CM_CMIO_YIELD_MANUAL_REASON_RX_REJECTED as u16;
+            pub const TX_EXCEPTION: u16 = CM_CMIO_YIELD_MANUAL_REASON_TX_EXCEPTION as u16;
         }
     }
 
-    /// HTIF reply
+    /// CMIO response
     pub mod fromhost {
         use cartesi_machine_sys::*;
-        pub const ADVANCE_STATE: u16 = HTIF_YIELD_REASON_ADVANCE_STATE_DEF as u16;
-        pub const INSPECT_STATE: u16 = HTIF_YIELD_REASON_INSPECT_STATE_DEF as u16;
+        pub const ADVANCE_STATE: u16 = CM_CMIO_YIELD_REASON_ADVANCE_STATE as u16;
+        pub const INSPECT_STATE: u16 = CM_CMIO_YIELD_REASON_INSPECT_STATE as u16;
     }
 }
 
-///
-pub mod pma {
-    use cartesi_machine_sys::*;
-    pub const CMIO_RX_BUFFER_START: u64 = PMA_CMIO_RX_BUFFER_START_DEF as u64;
-    pub const CMIO_RX_BUFFER_LOG2_SIZE: u64 = PMA_CMIO_RX_BUFFER_LOG2_SIZE_DEF as u64;
-    pub const CMIO_TX_BUFFER_START: u64 = PMA_CMIO_TX_BUFFER_START_DEF as u64;
-    pub const CMIO_TX_BUFFER_LOG2_SIZE: u64 = PMA_CMIO_TX_BUFFER_LOG2_SIZE_DEF as u64;
-}
-
-pub mod error {
+pub mod error_code {
     use cartesi_machine_sys::*;
 
-    pub const OK: u32 = CM_ERROR_CM_ERROR_OK;
+    pub const OK: i32 = CM_ERROR_OK;
 
     // Logic errors
-    pub const INVALID_ARGUMENT: u32 = CM_ERROR_CM_ERROR_INVALID_ARGUMENT;
-    pub const DOMAIN_ERROR: u32 = CM_ERROR_CM_ERROR_DOMAIN_ERROR;
-    pub const LENGTH_ERROR: u32 = CM_ERROR_CM_ERROR_LENGTH_ERROR;
-    pub const OUT_OF_RANGE: u32 = CM_ERROR_CM_ERROR_OUT_OF_RANGE;
-    pub const LOGIC_ERROR: u32 = CM_ERROR_CM_ERROR_LOGIC_ERROR;
-    pub const LOGIC_ERROR_END: u32 = CM_ERROR_CM_LOGIC_ERROR_END;
-
-    // Bad optional access error
-    pub const BAD_OPTIONAL_ACCESS: u32 = CM_ERROR_CM_ERROR_BAD_OPTIONAL_ACCESS;
+    pub const INVALID_ARGUMENT: i32 = CM_ERROR_INVALID_ARGUMENT;
+    pub const DOMAIN_ERROR: i32 = CM_ERROR_DOMAIN_ERROR;
+    pub const LENGTH_ERROR: i32 = CM_ERROR_LENGTH_ERROR;
+    pub const OUT_OF_RANGE: i32 = CM_ERROR_OUT_OF_RANGE;
+    pub const LOGIC_ERROR: i32 = CM_ERROR_LOGIC_ERROR;
 
     // Runtime errors
-    pub const RUNTIME_ERROR: u32 = CM_ERROR_CM_ERROR_RUNTIME_ERROR;
-    pub const RANGE_ERROR: u32 = CM_ERROR_CM_ERROR_RANGE_ERROR;
-    pub const OVERFLOW_ERROR: u32 = CM_ERROR_CM_ERROR_OVERFLOW_ERROR;
-    pub const UNDERFLOW_ERROR: u32 = CM_ERROR_CM_ERROR_UNDERFLOW_ERROR;
-    pub const REGEX_ERROR: u32 = CM_ERROR_CM_ERROR_REGEX_ERROR;
-    pub const SYSTEM_IOS_BASE_FAILURE: u32 = CM_ERROR_CM_ERROR_SYSTEM_IOS_BASE_FAILURE;
-    pub const FILESYSTEM_ERROR: u32 = CM_ERROR_CM_ERROR_FILESYSTEM_ERROR;
-    pub const ATOMIC_TX_ERROR: u32 = CM_ERROR_CM_ERROR_ATOMIC_TX_ERROR;
-    pub const NONEXISTING_LOCAL_TIME: u32 = CM_ERROR_CM_ERROR_NONEXISTING_LOCAL_TIME;
-    pub const AMBIGOUS_LOCAL_TIME: u32 = CM_ERROR_CM_ERROR_AMBIGUOUS_LOCAL_TIME;
-    pub const FORMAT_ERROR: u32 = CM_ERROR_CM_ERROR_FORMAT_ERROR;
-    pub const RUNTIME_ERROR_END: u32 = CM_ERROR_CM_RUNTIME_ERROR_END;
+    pub const RUNTIME_ERROR: i32 = CM_ERROR_RUNTIME_ERROR;
+    pub const RANGE_ERROR: i32 = CM_ERROR_RANGE_ERROR;
+    pub const OVERFLOW_ERROR: i32 = CM_ERROR_OVERFLOW_ERROR;
+    pub const UNDERFLOW_ERROR: i32 = CM_ERROR_UNDERFLOW_ERROR;
+    pub const REGEX_ERROR: i32 = CM_ERROR_REGEX_ERROR;
+    pub const SYSTEM_ERROR: i32 = CM_ERROR_SYSTEM_ERROR;
 
     // Other errors
-    pub const BAD_TYPEID: u32 = CM_ERROR_CM_ERROR_BAD_TYPEID;
-    pub const BAD_CAST: u32 = CM_ERROR_CM_ERROR_BAD_CAST;
-    pub const BAD_ANY_CAST: u32 = CM_ERROR_CM_ERROR_BAD_ANY_CAST;
-    pub const BAD_WEAK_PTR: u32 = CM_ERROR_CM_ERROR_BAD_WEAK_PTR;
-    pub const BAD_FUNCTION_CALL: u32 = CM_ERROR_CM_ERROR_BAD_FUNCTION_CALL;
-    pub const BAD_ALLOC: u32 = CM_ERROR_CM_ERROR_BAD_ALLOC;
-    pub const BAD_ARRAY_NEW_LENGTH: u32 = CM_ERROR_CM_ERROR_BAD_ARRAY_NEW_LENGTH;
-    pub const BAD_EXCEPTION: u32 = CM_ERROR_CM_ERROR_BAD_EXCEPTION;
-    pub const BAD_VARIANT_ACCESS: u32 = CM_ERROR_CM_ERROR_BAD_VARIANT_ACCESS;
-    pub const EXCEPTION: u32 = CM_ERROR_CM_ERROR_EXCEPTION;
-    pub const OTHER_ERROR_END: u32 = CM_ERROR_CM_OTHER_ERROR_END;
+    pub const BAD_TYPEID: i32 = CM_ERROR_BAD_TYPEID;
+    pub const BAD_CAST: i32 = CM_ERROR_BAD_CAST;
+    pub const BAD_ANY_CAST: i32 = CM_ERROR_BAD_ANY_CAST;
+    pub const BAD_OPTIONAL_ACCESS: i32 = CM_ERROR_BAD_OPTIONAL_ACCESS;
+    pub const BAD_WEAK_PTR: i32 = CM_ERROR_BAD_WEAK_PTR;
+    pub const BAD_FUNCTION_CALL: i32 = CM_ERROR_BAD_FUNCTION_CALL;
+    pub const BAD_ALLOC: i32 = CM_ERROR_BAD_ALLOC;
+    pub const BAD_ARRAY_NEW_LENGTH: i32 = CM_ERROR_BAD_ARRAY_NEW_LENGTH;
+    pub const BAD_EXCEPTION: i32 = CM_ERROR_BAD_EXCEPTION;
+    pub const BAD_VARIANT_ACCESS: i32 = CM_ERROR_BAD_VARIANT_ACCESS;
+    pub const EXCEPTION: i32 = CM_ERROR_EXCEPTION;
 
     // C API Errors
-    pub const UNKNOWN: u32 = CM_ERROR_CM_ERROR_UNKNOWN;
+    pub const UNKNOWN: i32 = CM_ERROR_UNKNOWN;
 }
diff --git a/machine/rust-bindings/cartesi-machine/src/error.rs b/machine/rust-bindings/cartesi-machine/src/error.rs
new file mode 100644
index 00000000..6438b6bb
--- /dev/null
+++ b/machine/rust-bindings/cartesi-machine/src/error.rs
@@ -0,0 +1,21 @@
+// (c) Cartesi and individual authors (see AUTHORS)
+// SPDX-License-Identifier: Apache-2.0 (see LICENSE)
+
+//! Error handling for the machine emulator
+
+use std::fmt::Display;
+
+pub type MachineResult<T> = Result<T, MachineError>;
+
+/// Error returned from machine emulator C API
+#[derive(Debug, Clone, thiserror::Error)]
+pub struct MachineError {
+    pub code: i32,
+    pub message: String,
+}
+
+impl Display for MachineError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "Cartesi Machine error {}: {}", self.code, self.message)
+    }
+}
diff --git a/machine/rust-bindings/cartesi-machine/src/errors.rs b/machine/rust-bindings/cartesi-machine/src/errors.rs
deleted file mode 100644
index c816aa94..00000000
--- a/machine/rust-bindings/cartesi-machine/src/errors.rs
+++ /dev/null
@@ -1,58 +0,0 @@
-// (c) Cartesi and individual authors (see AUTHORS)
-// SPDX-License-Identifier: Apache-2.0 (see LICENSE)
-
-//! Error handling for the machine emulator
-
-use crate::utils::from_cstr;
-use std::{ffi::c_char, fmt::Display, mem::MaybeUninit};
-
-/// Error returned from machine emulator C API
-#[derive(Debug, Clone, thiserror::Error)]
-pub struct MachineError {
-    code: i32,
-    message: Option<String>,
-}
-
-impl Display for MachineError {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(
-            f,
-            "Error with code {}: {}",
-            self.code as u8,
-            self.message.clone().unwrap_or_default()
-        )
-    }
-}
-
-/// Collects an error and cleans the memory. It's used to avoid accidental memory leaks when
-/// handling errors using the ownership of [ErrorCollector], the inability to clone it and the
-/// [cm_delete_cstring] function.
-pub(crate) struct ErrorCollector {
-    ptr: MaybeUninit<*mut c_char>,
-}
-
-impl ErrorCollector {
-    /// Creates a new error collector
-    pub fn new() -> Self {
-        Self {
-            ptr: MaybeUninit::<*mut c_char>::uninit(),
-        }
-    }
-
-    /// Gets the pointer to the error message
-    pub fn as_mut_ptr(&mut self) -> *mut *mut c_char {
-        self.ptr.as_mut_ptr()
-    }
-
-    /// Collect error from C API
-    pub fn collect(self, code: i32) -> Result<(), MachineError> {
-        if code == cartesi_machine_sys::CM_ERROR_CM_ERROR_OK as i32 {
-            Ok(())
-        } else {
-            let message = unsafe { from_cstr(*self.ptr.as_ptr()) };
-            unsafe { cartesi_machine_sys::cm_delete_cstring(*self.ptr.as_ptr()) };
-
-            Err(MachineError { code, message })
-        }
-    }
-}
diff --git a/machine/rust-bindings/cartesi-machine/src/hash.rs b/machine/rust-bindings/cartesi-machine/src/hash.rs
deleted file mode 100644
index 15aff8d8..00000000
--- a/machine/rust-bindings/cartesi-machine/src/hash.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-// (c) Cartesi and individual authors (see AUTHORS)
-// SPDX-License-Identifier: Apache-2.0 (see LICENSE)
-
-//! Defines the [struct@Hash] type.
-
-use std::fmt::Display;
-
-type Digest = [u8; 32];
-
-/// Digest generated by a hash function.
-#[derive(Clone, Debug, PartialEq, Eq, Hash)]
-pub struct Hash(Digest);
-
-impl Display for Hash {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "0x{}", hex::encode(self.0))
-    }
-}
-
-impl Hash {
-    pub fn as_ptr(&self) -> *const Digest {
-        &self.0
-    }
-
-    /// Create a new hash from a byte array.
-    pub fn new(hash: Digest) -> Self {
-        Self(hash)
-    }
-
-    /// Get the byte array representation of this hash.
-    pub fn as_bytes(&self) -> &[u8] {
-        &self.0
-    }
-}
diff --git a/machine/rust-bindings/cartesi-machine/src/lib.rs b/machine/rust-bindings/cartesi-machine/src/lib.rs
index 39848d0a..06c0222a 100644
--- a/machine/rust-bindings/cartesi-machine/src/lib.rs
+++ b/machine/rust-bindings/cartesi-machine/src/lib.rs
@@ -3,20 +3,12 @@
 
 #![doc = include_str!("../README.md")]
 
-pub mod configuration;
-pub mod errors;
-
-pub mod hash;
+pub mod config;
+pub mod constants;
+pub mod error;
 pub mod machine;
+pub mod types;
 
-// TODO: review
-pub mod log;
-pub mod proof;
-
-mod constants;
-mod utils;
-
-pub use constants::*;
 pub use machine::Machine;
 
 // Reexport inner cartesi-machine-sys
diff --git a/machine/rust-bindings/cartesi-machine/src/log.rs b/machine/rust-bindings/cartesi-machine/src/log.rs
deleted file mode 100644
index 60d895a4..00000000
--- a/machine/rust-bindings/cartesi-machine/src/log.rs
+++ /dev/null
@@ -1,184 +0,0 @@
-//! Logging utilities for Cartesi Machine.
-
-use crate::{hash::Hash, utils};
-
-/// Type of state access
-#[derive(Debug, PartialEq, Eq)]
-pub enum AccessType {
-    /// Read operation
-    Read = 0,
-    /// Write operation
-    Write,
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-#[repr(C)]
-/// Type of access log
-pub struct AccessLogType {
-    /// Includes proofs
-    pub proofs: bool,
-    /// Includes annotations
-    pub annotations: bool,
-    /// Includes data bigger than 8 bytes
-    pub large_data: bool,
-}
-
-impl From<AccessLogType> for cartesi_machine_sys::cm_access_log_type {
-    fn from(log_type: AccessLogType) -> Self {
-        unsafe { std::mem::transmute(log_type) }
-    }
-}
-
-impl From<cartesi_machine_sys::cm_access_log_type> for AccessLogType {
-    fn from(log_type: cartesi_machine_sys::cm_access_log_type) -> Self {
-        unsafe { std::mem::transmute(log_type) }
-    }
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-#[repr(C)]
-/// Bracket type
-pub enum BracketType {
-    /// Start of scope
-    Begin = 0,
-    /// End of scope
-    End,
-}
-
-/// Bracket Note
-pub struct BracketNote<'a> {
-    ptr: *const cartesi_machine_sys::cm_bracket_note,
-    phantom: std::marker::PhantomData<&'a ()>,
-}
-
-impl<'a> BracketNote<'a> {
-    fn new(ptr: *const cartesi_machine_sys::cm_bracket_note) -> Self {
-        Self {
-            ptr,
-            phantom: std::marker::PhantomData,
-        }
-    }
-
-    pub fn kind(&self) -> BracketType {
-        unsafe { std::mem::transmute((*self.ptr).type_) }
-    }
-
-    pub fn r#where(&self) -> u64 {
-        unsafe { (*self.ptr).where_ }
-    }
-
-    pub fn text(&self) -> String {
-        unsafe { utils::from_cstr((*self.ptr).text) }.unwrap()
-    }
-}
-
-/// Record of an access to the machine state
-pub struct Access<'a> {
-    ptr: *const cartesi_machine_sys::cm_access,
-    phantom: std::marker::PhantomData<&'a ()>,
-}
-
-impl<'a> Access<'a> {
-    fn new(ptr: *const cartesi_machine_sys::cm_access) -> Self {
-        Self {
-            ptr,
-            phantom: std::marker::PhantomData,
-        }
-    }
-
-    /// Type of access
-    pub fn access_type(&self) -> AccessType {
-        unsafe { std::mem::transmute((*self.ptr).type_ as u8) }
-    }
-
-    /// Address of access
-    pub fn address(&self) -> u64 {
-        unsafe { (*self.ptr).address }
-    }
-
-    /// Log2 of size of access
-    pub fn log2_size(&self) -> i32 {
-        unsafe { (*self.ptr).log2_size }
-    }
-
-    /// Hash of data before access
-    pub fn read_hash(&self) -> Hash {
-        Hash::new(unsafe { (*self.ptr).read_hash })
-    }
-
-    /// Data before access
-    pub fn read_data(&self) -> &[u8] {
-        unsafe { std::slice::from_raw_parts((*self.ptr).read_data, (*self.ptr).read_data_size) }
-    }
-
-    /// Hash of data after access (if writing)
-    pub fn written_hash(&self) -> Hash {
-        Hash::new(unsafe { (*self.ptr).written_hash })
-    }
-
-    /// Data after access (if writing)
-    pub fn written_data(&self) -> &[u8] {
-        unsafe {
-            std::slice::from_raw_parts((*self.ptr).written_data, (*self.ptr).written_data_size)
-        }
-    }
-
-    /// Sibling hashes towards root
-    pub fn sibling_hashes(&self) -> Vec<Hash> {
-        let sibling_hashes = unsafe { *(*self.ptr).sibling_hashes };
-        let sibling_hashes =
-            unsafe { std::slice::from_raw_parts(sibling_hashes.entry, sibling_hashes.count) };
-
-        sibling_hashes.iter().map(|hash| Hash::new(*hash)).collect()
-    }
-}
-
-/// Log of state accesses
-pub struct AccessLog(*mut cartesi_machine_sys::cm_access_log);
-
-impl Drop for AccessLog {
-    fn drop(&mut self) {
-        unsafe { cartesi_machine_sys::cm_delete_access_log(self.0) };
-    }
-}
-
-impl AccessLog {
-    pub(crate) fn new(ptr: *mut cartesi_machine_sys::cm_access_log) -> Self {
-        Self(ptr)
-    }
-
-    pub(crate) fn as_ptr(&self) -> &cartesi_machine_sys::cm_access_log {
-        unsafe { &*self.0 }
-    }
-
-    pub fn accesses(&self) -> Vec<Access> {
-        let accesses = unsafe { (*self.0).accesses };
-        let accesses = unsafe { std::slice::from_raw_parts(accesses.entry, accesses.count) };
-
-        accesses.iter().map(|access| Access::new(access)).collect()
-    }
-
-    pub fn brackets(&self) -> Vec<BracketNote> {
-        let brackets = unsafe { (*self.0).brackets };
-        let brackets = unsafe { std::slice::from_raw_parts(brackets.entry, brackets.count) };
-
-        brackets
-            .iter()
-            .map(|bracket| BracketNote::new(bracket))
-            .collect()
-    }
-
-    pub fn notes(&self) -> Vec<String> {
-        let notes = unsafe { (*self.0).notes };
-        let notes = unsafe { std::slice::from_raw_parts(notes.entry, notes.count) };
-
-        notes
-            .iter()
-            .map(|note| utils::from_cstr(*note).unwrap())
-            .collect()
-    }
-
-    pub fn log_type(&self) -> AccessLogType {
-        unsafe { std::mem::transmute((*self.0).log_type) }
-    }
-}
diff --git a/machine/rust-bindings/cartesi-machine/src/machine.rs b/machine/rust-bindings/cartesi-machine/src/machine.rs
index ee72c391..de5e3d91 100644
--- a/machine/rust-bindings/cartesi-machine/src/machine.rs
+++ b/machine/rust-bindings/cartesi-machine/src/machine.rs
@@ -1,772 +1,944 @@
-use std::path::Path;
-
-use crate::configuration::*;
-use crate::errors::*;
-use crate::hash;
-use crate::log;
-use crate::proof;
+// (c) Cartesi and individual authors (see AUTHORS)
+// SPDX-License-Identifier: Apache-2.0 (see LICENSE)
+
+use std::{
+    ffi::{c_char, CStr, CString},
+    path::Path,
+    ptr,
+};
+
+use crate::{
+    config::{self, machine::MachineConfig, runtime::RuntimeConfig},
+    constants,
+    error::{MachineError, MachineResult as Result},
+    types::{
+        access_proof::AccessLog,
+        cmio::{CmioRequest, CmioResponseReason},
+        memory_proof::Proof,
+        memory_range::MemoryRangeDescriptions,
+        BreakReason, Hash, LogType, Register, UArchBreakReason,
+    },
+};
 
 /// Machine instance handle
 pub struct Machine {
     pub machine: *mut cartesi_machine_sys::cm_machine,
-    pub runtime_config: RuntimeConfig,
 }
 
 impl Drop for Machine {
     fn drop(&mut self) {
         unsafe {
-            cartesi_machine_sys::cm_delete_machine(self.machine);
+            cartesi_machine_sys::cm_delete(self.machine);
         }
     }
 }
 
-impl Machine {
-    /// Create machine instance from previously serialized directory
-    pub fn load(path: &Path, runtime_config: RuntimeConfig) -> Result<Self, MachineError> {
-        let mut machine = std::ptr::null_mut();
-        {
-            let mut error_collector = ErrorCollector::new();
-            let path = {
-                let p = path.to_str().unwrap();
-                std::ffi::CString::new(p).unwrap()
-            };
-
-            let result = unsafe {
-                cartesi_machine_sys::cm_load_machine(
-                    path.as_ptr(),
-                    &runtime_config.values,
-                    &mut machine,
-                    error_collector.as_mut_ptr(),
-                )
-            };
-            error_collector.collect(result)?;
+macro_rules! check_err {
+    ($err_code:expr) => {
+        if $err_code != constants::error_code::OK {
+            Err(Machine::last_error($err_code))
+        } else {
+            Ok(())
         }
+    };
+}
 
-        let mut initial_config_ptr = std::ptr::null();
-        {
-            let mut error_collector = ErrorCollector::new();
-
-            let result = unsafe {
-                cartesi_machine_sys::cm_get_initial_config(
-                    machine,
-                    &mut initial_config_ptr,
-                    error_collector.as_mut_ptr(),
-                )
-            };
-            error_collector.collect(result)?;
-        }
+macro_rules! serialize_to_json {
+    ($src:expr) => {
+        CString::new(serde_json::to_string($src).expect("failed serializing to json"))
+            .expect("CString::new failed")
+    };
+}
 
-        let machine = Machine {
-            machine,
-            runtime_config,
+macro_rules! parse_json_from_cstring {
+    ($src:expr) => {{
+        let cstr = unsafe { CStr::from_ptr($src) };
+        let json = cstr.to_string_lossy();
+        serde_json::from_str(&json).expect("could not parse json")
+    }};
+}
+
+impl Machine {
+    // -----------------------------------------------------------------------------
+    // API functions
+    // -----------------------------------------------------------------------------
+
+    /// Returns the default machine config.
+    pub fn default_config() -> Result<MachineConfig> {
+        let mut config_ptr: *const c_char = ptr::null();
+        let err_code =
+            unsafe { cartesi_machine_sys::cm_get_default_config(ptr::null(), &mut config_ptr) };
+        check_err!(err_code)?;
+
+        let config = parse_json_from_cstring!(config_ptr);
+
+        Ok(config)
+    }
+
+    /// Gets the address of any x, f, or control state register.
+    pub fn reg_address(reg: Register) -> Result<u64> {
+        let mut val: u64 = 0;
+        let err_code =
+            unsafe { cartesi_machine_sys::cm_get_reg_address(ptr::null(), reg, &mut val) };
+        check_err!(err_code)?;
+        Ok(val)
+    }
+
+    // -----------------------------------------------------------------------------
+    // Machine API functions
+    // -----------------------------------------------------------------------------
+
+    /// Creates a new machine instance from configuration.
+    pub fn create(config: &MachineConfig, runtime_config: &RuntimeConfig) -> Result<Self> {
+        let config_json = serialize_to_json!(&config);
+        let runtime_config_json = serialize_to_json!(&runtime_config);
+
+        let mut machine: *mut cartesi_machine_sys::cm_machine = ptr::null_mut();
+        let err_code = unsafe {
+            cartesi_machine_sys::cm_create_new(
+                config_json.as_ptr(),
+                runtime_config_json.as_ptr(),
+                &mut machine,
+            )
         };
+        check_err!(err_code)?;
 
-        Ok(machine)
+        Ok(Self { machine })
     }
 
-    /// Serialize entire state to directory
-    pub fn store(&self, path: &Path) -> Result<(), MachineError> {
-        let mut error_collector = ErrorCollector::new();
+    /// Loads a new machine instance from a previously stored directory.
+    pub fn load(dir: &Path, runtime_config: &RuntimeConfig) -> Result<Self> {
+        let dir_cstr = path_to_cstring(dir);
+        let runtime_config_json = serialize_to_json!(&runtime_config);
 
-        let path = {
-            let p = path.to_str().unwrap();
-            std::ffi::CString::new(p).unwrap()
+        let mut machine: *mut cartesi_machine_sys::cm_machine = ptr::null_mut();
+        let err_code = unsafe {
+            cartesi_machine_sys::cm_load_new(
+                dir_cstr.as_ptr(),
+                runtime_config_json.as_ptr(),
+                &mut machine,
+            )
         };
+        check_err!(err_code)?;
 
-        let result = unsafe {
-            cartesi_machine_sys::cm_store(self.machine, path.as_ptr(), error_collector.as_mut_ptr())
-        };
-        error_collector.collect(result)?;
+        Ok(Self { machine })
+    }
+
+    /// Stores a machine instance to a directory, serializing its entire state.
+    pub fn store(&mut self, dir: &Path) -> Result<()> {
+        // CM_API cm_error cm_store(const cm_machine *m, const char *dir);
+        // todo!()
+        let dir_cstr = path_to_cstring(dir);
+        let err_code = unsafe { cartesi_machine_sys::cm_store(self.machine, dir_cstr.as_ptr()) };
+        check_err!(err_code)?;
 
         Ok(())
     }
 
-    /// Runs the machine until mcycle reaches mcycle_end or the machine halts.
-    pub fn run(&mut self, mcycle_end: u64) -> Result<u32, MachineError> {
-        let mut error_collector = ErrorCollector::new();
-        let mut break_reason = 0;
-
-        let result = unsafe {
-            cartesi_machine_sys::cm_machine_run(
-                self.machine,
-                mcycle_end,
-                &mut break_reason,
-                error_collector.as_mut_ptr(),
-            )
+    /// Changes the machine runtime configuration.
+    pub fn set_runtime_config(&mut self, runtime_config: &RuntimeConfig) -> Result<()> {
+        let runtime_config_json = serialize_to_json!(&runtime_config);
+        let err_code = unsafe {
+            cartesi_machine_sys::cm_set_runtime_config(self.machine, runtime_config_json.as_ptr())
         };
+        check_err!(err_code)?;
 
-        error_collector.collect(result)?;
+        Ok(())
+    }
 
-        Ok(break_reason)
+    /// Gets the machine runtime config.
+    pub fn runtime_config(&mut self) -> Result<RuntimeConfig> {
+        let mut rc_ptr: *const c_char = ptr::null();
+        let err_code =
+            unsafe { cartesi_machine_sys::cm_get_runtime_config(self.machine, &mut rc_ptr) };
+        check_err!(err_code)?;
+
+        let runtime_config = parse_json_from_cstring!(rc_ptr);
+
+        Ok(runtime_config)
     }
 
-    /// Runs the machine until ucycle reaches ucycle_end or the machine halts.
-    pub fn run_uarch(&mut self, ucycle_end: u64) -> Result<u32, MachineError> {
-        let mut error_collector = ErrorCollector::new();
-        let mut break_reason = 0;
+    /// Replaces a memory range.
+    pub fn replace_memory_range(
+        &mut self,
+        start: u64,
+        length: u64,
+        shared: bool,
+        image_path: Option<&Path>,
+    ) -> Result<()> {
+        let image_cstr = match image_path {
+            Some(path) => path_to_cstring(path),
+            None => CString::new("").unwrap(),
+        };
+
+        let image_ptr = if image_path.is_some() {
+            image_cstr.as_ptr()
+        } else {
+            ptr::null()
+        };
 
-        let result = unsafe {
-            cartesi_machine_sys::cm_machine_run_uarch(
+        let err_code = unsafe {
+            cartesi_machine_sys::cm_replace_memory_range(
                 self.machine,
-                ucycle_end,
-                &mut break_reason,
-                error_collector.as_mut_ptr(),
+                start,
+                length,
+                shared,
+                image_ptr,
             )
         };
+        check_err!(err_code)?;
 
-        error_collector.collect(result)?;
-        Ok(break_reason)
+        Ok(())
     }
 
-    /// Write a CMIO response
-    pub fn send_cmio_response(&mut self, reason: u16, data: &[u8]) -> Result<(), MachineError> {
-        let mut error_collector = ErrorCollector::new();
+    /// Returns the machine config used to initialize the machine.
+    pub fn initial_config(&mut self) -> Result<config::machine::MachineConfig> {
+        let mut config_ptr: *const c_char = ptr::null();
+        let err_code =
+            unsafe { cartesi_machine_sys::cm_get_initial_config(self.machine, &mut config_ptr) };
+        check_err!(err_code)?;
 
-        let result = unsafe {
-            cartesi_machine_sys::cm_send_cmio_response(
-                self.machine,
-                reason,
-                data.as_ptr(),
-                data.len(),
-                error_collector.as_mut_ptr(),
-            )
-        };
+        let config = parse_json_from_cstring!(config_ptr);
 
-        error_collector.collect(result)?;
+        Ok(config)
+    }
 
-        Ok(())
+    /// Returns a list with all memory ranges in the machine.
+    pub fn memory_ranges(&mut self) -> Result<MemoryRangeDescriptions> {
+        let mut ranges_ptr: *const c_char = ptr::null();
+        let err_code =
+            unsafe { cartesi_machine_sys::cm_get_memory_ranges(self.machine, &mut ranges_ptr) };
+        check_err!(err_code)?;
+
+        let ranges = parse_json_from_cstring!(ranges_ptr);
+
+        Ok(ranges)
     }
 
-    /// Write a CMIO response logging all accesses to the state.
-    pub fn log_send_cmio_response(
-        &mut self,
-        reason: u16,
-        data: &[u8],
-        log_type: log::AccessLogType,
-        one_based: bool,
-    ) -> Result<log::AccessLog, MachineError> {
-        let mut error_collector = ErrorCollector::new();
-        let mut access_log = std::ptr::null_mut();
+    /// Obtains the root hash of the Merkle tree.
+    pub fn root_hash(&mut self) -> Result<Hash> {
+        let mut hash = Hash::default();
+        let err_code = unsafe { cartesi_machine_sys::cm_get_root_hash(self.machine, &mut hash) };
+        check_err!(err_code)?;
 
-        let result = unsafe {
-            cartesi_machine_sys::cm_log_send_cmio_response(
+        Ok(hash)
+    }
+
+    /// Obtains the proof for a node in the machine state Merkle tree.
+    pub fn proof(&mut self, address: u64, log2_size: u32) -> Result<Proof> {
+        let mut proof_ptr: *const c_char = ptr::null();
+        let err_code = unsafe {
+            cartesi_machine_sys::cm_get_proof(
                 self.machine,
-                reason,
-                data.as_ptr(),
-                data.len(),
-                log_type.into(),
-                one_based,
-                &mut access_log,
-                error_collector.as_mut_ptr(),
+                address,
+                log2_size as i32,
+                &mut proof_ptr,
             )
         };
+        check_err!(err_code)?;
 
-        error_collector.collect(result)?;
+        let proof = parse_json_from_cstring!(proof_ptr);
 
-        Ok(log::AccessLog::new(access_log))
+        Ok(proof)
     }
 
-    /// Runs the machine for one micro cycle logging all accesses to the state.
-    pub fn log_uarch_step(
-        &mut self,
-        log_type: log::AccessLogType,
-        one_based: bool,
-    ) -> Result<log::AccessLog, MachineError> {
-        let mut error_collector = ErrorCollector::new();
-        let mut access_log = std::ptr::null_mut();
-
-        let result = unsafe {
-            cartesi_machine_sys::cm_log_uarch_step(
-                self.machine,
-                log_type.into(),
-                one_based,
-                &mut access_log,
-                error_collector.as_mut_ptr(),
-            )
-        };
-        error_collector.collect(result)?;
+    // ------------------------------------
+    // Reading and writing
+    // ------------------------------------
+
+    /// Reads the value of a word in the machine state, by its physical address.
+    pub fn read_word(&mut self, address: u64) -> Result<u64> {
+        let mut value: u64 = 0;
+        let err_code =
+            unsafe { cartesi_machine_sys::cm_read_word(self.machine, address, &mut value) };
+        check_err!(err_code)?;
 
-        Ok(log::AccessLog::new(access_log))
+        Ok(value)
     }
 
-    /// Checks the internal consistency of an access log produced by cm_log_uarch_step
-    pub fn verify_uarch_step_log(
-        &mut self,
-        log: &log::AccessLog,
-        one_based: bool,
-    ) -> Result<(), MachineError> {
-        let mut error_collector = ErrorCollector::new();
-
-        let result = unsafe {
-            cartesi_machine_sys::cm_verify_uarch_step_log(
-                log.as_ptr(),
-                &self.runtime_config.values,
-                one_based,
-                error_collector.as_mut_ptr(),
-            )
-        };
-        error_collector.collect(result)?;
+    /// Reads the value of a register.
+    pub fn read_reg(&mut self, reg: Register) -> Result<u64> {
+        let mut value: u64 = 0;
+        let err_code = unsafe { cartesi_machine_sys::cm_read_reg(self.machine, reg, &mut value) };
+        check_err!(err_code)?;
 
-        Ok(())
+        Ok(value)
     }
 
-    /// Checks the validity of a state transition
-    pub fn verify_uarch_step_state_transition(
-        &mut self,
-        root_hash_before: &hash::Hash,
-        log: &log::AccessLog,
-        root_hash_after: &hash::Hash,
-        one_based: bool,
-    ) -> Result<(), MachineError> {
-        let mut error_collector = ErrorCollector::new();
-
-        let result = unsafe {
-            cartesi_machine_sys::cm_verify_uarch_step_state_transition(
-                root_hash_before.as_ptr(),
-                log.as_ptr(),
-                root_hash_after.as_ptr(),
-                &self.runtime_config.values,
-                one_based,
-                error_collector.as_mut_ptr(),
-            )
-        };
-        error_collector.collect(result)?;
+    /// Writes the value of a register.
+    pub fn write_reg(&mut self, reg: Register, val: u64) -> Result<()> {
+        let err_code = unsafe { cartesi_machine_sys::cm_write_reg(self.machine, reg, val) };
+        check_err!(err_code)?;
 
         Ok(())
     }
 
-    /// Checks the validity of a state transition caused by a uarch state reset
-    pub fn verify_uarch_reset_state_transition(
-        &mut self,
-        root_hash_before: &hash::Hash,
-        log: &log::AccessLog,
-        root_hash_after: &hash::Hash,
-        one_based: bool,
-    ) -> Result<(), MachineError> {
-        let mut error_collector = ErrorCollector::new();
-
-        let result = unsafe {
-            cartesi_machine_sys::cm_verify_uarch_reset_state_transition(
-                root_hash_before.as_ptr(),
-                log.as_ptr(),
-                root_hash_after.as_ptr(),
-                &self.runtime_config.values,
-                one_based,
-                error_collector.as_mut_ptr(),
-            )
+    /// Reads a chunk of data from a machine memory range, by its physical address.
+    pub fn read_memory(&mut self, address: u64, size: u64) -> Result<Vec<u8>> {
+        let mut buffer = vec![0u8; size as usize];
+        let err_code = unsafe {
+            cartesi_machine_sys::cm_read_memory(self.machine, address, buffer.as_mut_ptr(), size)
         };
-        error_collector.collect(result)?;
+        check_err!(err_code)?;
 
-        Ok(())
+        Ok(buffer)
     }
 
-    /// Checks the internal consistency of an access log produced by cm_log_uarch_reset
-    pub fn verify_uarch_reset_log(
-        &mut self,
-        log: &log::AccessLog,
-        one_based: bool,
-    ) -> Result<(), MachineError> {
-        let mut error_collector = ErrorCollector::new();
-
-        let result = unsafe {
-            cartesi_machine_sys::cm_verify_uarch_reset_log(
-                log.as_ptr(),
-                &self.runtime_config.values,
-                one_based,
-                error_collector.as_mut_ptr(),
+    /// Writes a chunk of data to a machine memory range, by its physical address.
+    pub fn write_memory(&mut self, address: u64, data: &[u8]) -> Result<()> {
+        let err_code = unsafe {
+            cartesi_machine_sys::cm_write_memory(
+                self.machine,
+                address,
+                data.as_ptr(),
+                data.len() as u64,
             )
         };
-        error_collector.collect(result)?;
+        check_err!(err_code)?;
 
         Ok(())
     }
 
-    /// Obtains the proof for a node in the Merkle tree
-    pub fn get_proof(
-        &mut self,
-        address: u64,
-        log2_size: i32,
-    ) -> Result<proof::MerkleTreeProof, MachineError> {
-        let mut error_collector = ErrorCollector::new();
-        let mut proof = std::ptr::null_mut();
-
-        let result = unsafe {
-            cartesi_machine_sys::cm_get_proof(
+    /// Reads a chunk of data from a machine memory range, by its virtual memory.
+    pub fn read_virtual_memory(&mut self, address: u64, size: u64) -> Result<Vec<u8>> {
+        let mut buffer = vec![0u8; size as usize];
+        let err_code = unsafe {
+            cartesi_machine_sys::cm_read_virtual_memory(
                 self.machine,
                 address,
-                log2_size,
-                &mut proof,
-                error_collector.as_mut_ptr(),
+                buffer.as_mut_ptr(),
+                size,
             )
         };
-        error_collector.collect(result)?;
+        check_err!(err_code)?;
 
-        Ok(proof::MerkleTreeProof::new(proof))
+        Ok(buffer)
     }
 
-    /// Obtains the root hash of the Merkle tree
-    pub fn get_root_hash(&mut self) -> Result<hash::Hash, MachineError> {
-        let mut error_collector = ErrorCollector::new();
-        let mut hash = [0; 32];
-
-        let result = unsafe {
-            cartesi_machine_sys::cm_get_root_hash(
+    /// Writes a chunk of data to a machine memory range, by its virtual address.
+    pub fn write_virtual_memory(&mut self, address: u64, data: &[u8]) -> Result<()> {
+        let err_code = unsafe {
+            cartesi_machine_sys::cm_write_virtual_memory(
                 self.machine,
-                &mut hash,
-                error_collector.as_mut_ptr(),
+                address,
+                data.as_ptr(),
+                data.len() as u64,
             )
         };
-        error_collector.collect(result)?;
+        check_err!(err_code)?;
 
-        Ok(hash::Hash::new(hash))
+        Ok(())
     }
 
-    /// Verifies integrity of Merkle tree.
-    pub fn verify_merkle_tree(&mut self) -> Result<bool, MachineError> {
-        let mut error_collector = ErrorCollector::new();
-        let mut ret = false;
-
-        let result = unsafe {
-            cartesi_machine_sys::cm_verify_merkle_tree(
+    /// Translates a virtual memory address to its corresponding physical memory address.
+    pub fn translate_virtual_address(&mut self, virtual_address: u64) -> Result<u64> {
+        let mut paddr: u64 = 0;
+        let err_code = unsafe {
+            cartesi_machine_sys::cm_translate_virtual_address(
                 self.machine,
-                &mut ret,
-                error_collector.as_mut_ptr(),
+                virtual_address,
+                &mut paddr,
             )
         };
-        error_collector.collect(result)?;
+        check_err!(err_code)?;
 
-        Ok(ret)
+        Ok(paddr)
     }
 
-    /// Write the value of any CSR
-    pub fn write_csr(&mut self, csr: u32, value: u64) -> Result<(), MachineError> {
-        let mut error_collector = ErrorCollector::new();
+    // ------------------------------------
+    // Running
+    // ------------------------------------
 
-        let result = unsafe {
-            cartesi_machine_sys::cm_write_csr(
-                self.machine,
-                csr as u32,
-                value,
-                error_collector.as_mut_ptr(),
-            )
-        };
-        error_collector.collect(result)?;
+    /// Returns machine CM_REG_MCYCLE
+    pub fn mcycle(&mut self) -> Result<u64> {
+        self.read_reg(cartesi_machine_sys::CM_REG_MCYCLE)
+    }
 
-        Ok(())
+    /// Returns machine CM_REG_IFLAGS_Y
+    pub fn iflags_y(&mut self) -> Result<bool> {
+        Ok(self.read_reg(cartesi_machine_sys::CM_REG_IFLAGS_Y)? != 0)
     }
 
-    /// Read the value of any CSR
-    pub fn read_csr(&mut self, csr: u32) -> Result<u64, MachineError> {
-        let mut error_collector = ErrorCollector::new();
-        let mut value = 0;
+    /// Returns machine CM_REG_IFLAGS_H
+    pub fn iflags_h(&mut self) -> Result<bool> {
+        Ok(self.read_reg(cartesi_machine_sys::CM_REG_IFLAGS_H)? != 0)
+    }
 
-        let result = unsafe {
-            cartesi_machine_sys::cm_read_csr(
-                self.machine,
-                csr as u32,
-                &mut value,
-                error_collector.as_mut_ptr(),
-            )
+    /// Returns machine CM_REG_UARCH_CYCLE
+    pub fn ucycle(&mut self) -> Result<u64> {
+        self.read_reg(cartesi_machine_sys::CM_REG_UARCH_CYCLE)
+    }
+
+    /// Returns machine CM_REG_UARCH_HALT_FLAG
+    pub fn uarch_halt_flag(&mut self) -> Result<bool> {
+        Ok(self.read_reg(cartesi_machine_sys::CM_REG_UARCH_HALT_FLAG)? != 0)
+    }
+
+    /// Runs the machine until CM_REG_MCYCLE reaches mcycle_end, machine yields, or halts.
+    pub fn run(&mut self, mcycle_end: u64) -> Result<BreakReason> {
+        let mut break_reason = BreakReason::default();
+        let cm_error =
+            unsafe { cartesi_machine_sys::cm_run(self.machine, mcycle_end, &mut break_reason) };
+        check_err!(cm_error)?;
+
+        Ok(break_reason)
+    }
+
+    /// Runs the machine microarchitecture until CM_REG_UARCH_CYCLE reaches uarch_cycle_end or it halts.
+    pub fn run_uarch(&mut self, uarch_cycle_end: u64) -> Result<UArchBreakReason> {
+        let mut break_reason = UArchBreakReason::default();
+        let err_code = unsafe {
+            cartesi_machine_sys::cm_run_uarch(self.machine, uarch_cycle_end, &mut break_reason)
         };
-        error_collector.collect(result)?;
+        check_err!(err_code)?;
 
-        Ok(value)
+        Ok(break_reason)
     }
 
-    /// Gets the address of any CSR
-    pub fn get_csr_address(&mut self, csr: u32) -> u64 {
-        unsafe { cartesi_machine_sys::cm_get_csr_address(csr as u32) }
+    /// Resets the entire microarchitecture state to pristine values.
+    pub fn reset_uarch(&mut self) -> Result<()> {
+        let err_code = unsafe { cartesi_machine_sys::cm_reset_uarch(self.machine) };
+        check_err!(err_code)?;
+
+        Ok(())
     }
 
-    /// Read the value of a word in the machine state.
-    pub fn read_word(&mut self, word_address: u64) -> Result<u64, MachineError> {
-        let mut error_collector = ErrorCollector::new();
-        let mut word_value = 0;
+    /// Receives a cmio request.
+    pub fn receive_cmio_request(&mut self) -> Result<CmioRequest> {
+        let mut cmd: u8 = 0;
+        let mut reason: u16 = 0;
+        let mut length: u64 = 0;
 
-        let result = unsafe {
-            cartesi_machine_sys::cm_read_word(
+        // if data is NULL, length will still be set without reading any data.
+        let err_code = unsafe {
+            cartesi_machine_sys::cm_receive_cmio_request(
                 self.machine,
-                word_address,
-                &mut word_value,
-                error_collector.as_mut_ptr(),
+                &mut cmd,
+                &mut reason,
+                ptr::null_mut(),
+                &mut length,
             )
         };
-        error_collector.collect(result)?;
-
-        Ok(word_value)
-    }
+        check_err!(err_code)?;
 
-    /// Read a chunk of data from the machine memory.
-    pub fn read_memory(&mut self, address: u64, length: u64) -> Result<Vec<u8>, MachineError> {
-        let mut error_collector = ErrorCollector::new();
-        let mut data = vec![0; length as usize];
+        let mut buffer = vec![0u8; length as usize];
 
-        let result = unsafe {
-            cartesi_machine_sys::cm_read_memory(
+        let err_code = unsafe {
+            cartesi_machine_sys::cm_receive_cmio_request(
                 self.machine,
-                address,
-                data.as_mut_ptr(),
-                length,
-                error_collector.as_mut_ptr(),
+                &mut cmd,
+                &mut reason,
+                buffer.as_mut_ptr(),
+                &mut length,
             )
         };
-        error_collector.collect(result)?;
+        check_err!(err_code)?;
 
-        Ok(data)
+        Ok(CmioRequest::new(cmd, reason, buffer))
     }
 
-    /// Write a chunk of data to the machine memory.
-    pub fn write_memory(&mut self, address: u64, data: &[u8]) -> Result<(), MachineError> {
-        let mut error_collector = ErrorCollector::new();
-
-        let result = unsafe {
-            cartesi_machine_sys::cm_write_memory(
+    /// Sends a cmio response.
+    pub fn send_cmio_response(&mut self, reason: CmioResponseReason, data: &[u8]) -> Result<()> {
+        let err_code = unsafe {
+            cartesi_machine_sys::cm_send_cmio_response(
                 self.machine,
-                address,
+                reason as u16,
                 data.as_ptr(),
-                data.len(),
-                error_collector.as_mut_ptr(),
+                data.len() as u64,
             )
         };
-        error_collector.collect(result)?;
+        check_err!(err_code)?;
 
         Ok(())
     }
 
-    /// Reads a chunk of data from the machine virtual memory.
-    pub fn read_virtual_memory(
-        &mut self,
-        address: u64,
-        length: u64,
-    ) -> Result<Vec<u8>, MachineError> {
-        let mut error_collector = ErrorCollector::new();
-        let mut data = vec![0; length as usize];
+    // ------------------------------------
+    // Logging
+    // ------------------------------------
 
-        let result = unsafe {
-            cartesi_machine_sys::cm_read_virtual_memory(
+    /// Runs the machine for the given mcycle count and generates a log of accessed pages and proof data.
+    pub fn log_step(&mut self, mcycle_count: u64, log_filename: &Path) -> Result<BreakReason> {
+        let mut break_reason = BreakReason::default();
+        let log_filename_c = path_to_cstring(log_filename);
+
+        let err_code = unsafe {
+            cartesi_machine_sys::cm_log_step(
                 self.machine,
-                address,
-                data.as_mut_ptr(),
-                length,
-                error_collector.as_mut_ptr(),
+                mcycle_count,
+                log_filename_c.as_ptr(),
+                &mut break_reason,
             )
         };
-        error_collector.collect(result)?;
+        check_err!(err_code)?;
 
-        Ok(data)
+        Ok(break_reason)
     }
 
-    /// Writes a chunk of data to the machine virtual memory.
-    pub fn write_virtual_memory(&mut self, address: u64, data: &[u8]) -> Result<(), MachineError> {
-        let mut error_collector = ErrorCollector::new();
+    /// Runs the machine in the microarchitecture for one micro cycle logging all accesses to the state.
+    pub fn log_step_uarch(&mut self, log_type: LogType) -> Result<AccessLog> {
+        let mut log_ptr: *const c_char = ptr::null();
+        let err_code = unsafe {
+            cartesi_machine_sys::cm_log_step_uarch(self.machine, log_type as i32, &mut log_ptr)
+        };
+        check_err!(err_code)?;
 
-        let result = unsafe {
-            cartesi_machine_sys::cm_write_virtual_memory(
+        let access_log = parse_json_from_cstring!(log_ptr);
+
+        Ok(access_log)
+    }
+
+    /// Resets the entire microarchitecture state to pristine values logging all accesses to the state.
+    pub fn log_reset_uarch(&mut self, log_type: LogType) -> Result<AccessLog> {
+        let mut log_ptr: *const c_char = ptr::null();
+        let err_code = unsafe {
+            cartesi_machine_sys::cm_log_reset_uarch(self.machine, log_type as i32, &mut log_ptr)
+        };
+        check_err!(err_code)?;
+
+        let access_log = parse_json_from_cstring!(log_ptr);
+
+        Ok(access_log)
+    }
+
+    /// Sends a cmio response logging all accesses to the state.
+    pub fn log_send_cmio_response(
+        &mut self,
+        reason: CmioResponseReason,
+        data: &[u8],
+        log_type: LogType,
+    ) -> Result<AccessLog> {
+        let mut log_ptr: *const c_char = ptr::null();
+        let err_code = unsafe {
+            cartesi_machine_sys::cm_log_send_cmio_response(
                 self.machine,
-                address,
+                reason as u16,
                 data.as_ptr(),
-                data.len(),
-                error_collector.as_mut_ptr(),
+                data.len() as u64,
+                log_type as i32,
+                &mut log_ptr,
+            )
+        };
+        check_err!(err_code)?;
+
+        let access_log = parse_json_from_cstring!(log_ptr);
+
+        Ok(access_log)
+    }
+
+    // ------------------------------------
+    // Verifying
+    // ------------------------------------
+
+    /// Checks the validity of a step log file.
+    pub fn verify_step(
+        root_hash_before: &Hash,
+        log_filename: &Path,
+        mcycle_count: u64,
+        root_hash_after: &Hash,
+    ) -> Result<BreakReason> {
+        let log_filename_c = path_to_cstring(log_filename);
+
+        let mut break_reason = BreakReason::default();
+        let err_code = unsafe {
+            cartesi_machine_sys::cm_verify_step(
+                ptr::null(),
+                root_hash_before,
+                log_filename_c.as_ptr(),
+                mcycle_count,
+                root_hash_after,
+                &mut break_reason,
             )
         };
-        error_collector.collect(result)?;
+        check_err!(err_code)?;
 
-        Ok(())
+        Ok(break_reason)
     }
 
-    /// Reads the value of a general-purpose microarchitecture register.
-    pub fn read_x(&mut self, i: u32) -> Result<u64, MachineError> {
-        let mut error_collector = ErrorCollector::new();
-        let mut value = 0;
-
-        let result = unsafe {
-            cartesi_machine_sys::cm_read_x(
-                self.machine,
-                i as i32,
-                &mut value,
-                error_collector.as_mut_ptr(),
+    /// Checks the validity of a state transition produced by cm_log_step_uarch.
+    pub fn verify_step_uarch(
+        root_hash_before: &Hash,
+        log: &AccessLog,
+        root_hash_after: &Hash,
+    ) -> Result<()> {
+        let log_cstr = serialize_to_json!(&log);
+
+        let err_code = unsafe {
+            cartesi_machine_sys::cm_verify_step_uarch(
+                ptr::null(),
+                root_hash_before,
+                log_cstr.as_ptr(),
+                root_hash_after,
             )
         };
-        error_collector.collect(result)?;
+        check_err!(err_code)?;
 
-        Ok(value)
+        Ok(())
     }
 
-    /// Writes the value of a general-purpose microarchitecture register.
-    pub fn write_x(&mut self, i: u32, value: u64) -> Result<(), MachineError> {
-        let mut error_collector = ErrorCollector::new();
-
-        let result = unsafe {
-            cartesi_machine_sys::cm_write_x(
-                self.machine,
-                i as i32,
-                value,
-                error_collector.as_mut_ptr(),
+    /// Checks the validity of a state transition produced by cm_log_verify_reset_uarch.
+    pub fn verify_reset_uarch(
+        root_hash_before: &Hash,
+        log: &AccessLog,
+        root_hash_after: &Hash,
+    ) -> Result<()> {
+        let log_cstr = serialize_to_json!(&log);
+        let err_code = unsafe {
+            cartesi_machine_sys::cm_verify_reset_uarch(
+                ptr::null(),
+                root_hash_before,
+                log_cstr.as_ptr(),
+                root_hash_after,
             )
         };
-        error_collector.collect(result)?;
+        check_err!(err_code)?;
 
         Ok(())
     }
 
-    /// Gets the address of a general-purpose register.
-    pub fn get_x_address(&mut self, i: u32) -> u64 {
-        unsafe { cartesi_machine_sys::cm_get_x_address(i as i32) }
+    /// Checks the validity of a state transition produced by cm_log_send_cmio_response.
+    pub fn verify_send_cmio_response(
+        reason: CmioResponseReason,
+        data: &[u8],
+        root_hash_before: &Hash,
+        log: &AccessLog,
+        root_hash_after: &Hash,
+    ) -> Result<()> {
+        let log_cstr = serialize_to_json!(&log);
+
+        let err_code = unsafe {
+            cartesi_machine_sys::cm_verify_send_cmio_response(
+                ptr::null(),
+                reason as u16,
+                data.as_ptr(),
+                data.len() as u64,
+                root_hash_before,
+                log_cstr.as_ptr(),
+                root_hash_after,
+            )
+        };
+        check_err!(err_code)?;
+
+        Ok(())
     }
+}
 
-    /// Gets the address of a general-purpose microarchitecture register.
-    pub fn get_uarch_x_address(&mut self, i: u32) -> u64 {
-        unsafe { cartesi_machine_sys::cm_get_uarch_x_address(i as i32) }
+impl Machine {
+    fn last_error(code: i32) -> MachineError {
+        assert!(code != constants::error_code::OK);
+
+        let msg_p = unsafe { cartesi_machine_sys::cm_get_last_error_message() };
+        let cstr = unsafe { std::ffi::CStr::from_ptr(msg_p) };
+        let message = String::from_utf8_lossy(cstr.to_bytes()).to_string();
+
+        MachineError { code, message }
     }
+}
 
-    /// Reads the value of a floating-point register.
-    pub fn read_f(&mut self, i: u32) -> Result<u64, MachineError> {
-        let mut error_collector = ErrorCollector::new();
-        let mut value = 0;
+fn path_to_cstring(path: &Path) -> CString {
+    CString::new(path.to_string_lossy().as_bytes()).expect("CString::new failed")
+}
 
-        let result = unsafe {
-            cartesi_machine_sys::cm_read_f(
-                self.machine,
-                i as i32,
-                &mut value,
-                error_collector.as_mut_ptr(),
-            )
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use crate::{
+        config::{
+            machine::{DTBConfig, MachineConfig, MemoryRangeConfig, RAMConfig},
+            runtime::RuntimeConfig,
+        },
+        constants,
+        error::MachineResult as Result,
+        types::cmio::ManualReason,
+    };
+
+    fn make_basic_machine_config() -> MachineConfig {
+        MachineConfig::new_with_ram(RAMConfig {
+            length: 134217728,
+            image_filename: "../../../test/programs/linux.bin".into(),
+        })
+        .dtb(DTBConfig {
+            entrypoint: "echo Hello from inside!".to_string(),
+            ..Default::default()
+        })
+        .add_flash_drive(MemoryRangeConfig {
+            image_filename: "../../../test/programs/rootfs.ext2".into(),
+            ..Default::default()
+        })
+    }
+
+    fn make_cmio_machine_config() -> MachineConfig {
+        MachineConfig::new_with_ram(RAMConfig {
+            length: 134217728,
+            image_filename: "../../../test/programs/linux.bin".into(),
+        })
+        .dtb(DTBConfig {
+            entrypoint:
+                "echo '{\"domain\":16,\"id\":\"'$(echo -n Hello from inside! | hex --encode)'\"}' \
+                     | rollup gio | grep -Eo '0x[0-9a-f]+' | tr -d '\\n' | hex --decode; echo"
+                    .to_string(),
+            ..Default::default()
+        })
+        .add_flash_drive(MemoryRangeConfig {
+            image_filename: "../../../test/programs/rootfs.ext2".into(),
+            ..Default::default()
+        })
+    }
+
+    fn create_machine(config: &MachineConfig) -> Result<Machine> {
+        let runtime_config = RuntimeConfig {
+            htif: Some(config::runtime::HTIFRuntimeConfig {
+                no_console_putchar: Some(true),
+            }),
+            ..Default::default()
         };
-        error_collector.collect(result)?;
+        Machine::create(config, &runtime_config)
+    }
 
-        Ok(value)
+    #[test]
+    fn test_machine_run_halt_root_hash() -> Result<()> {
+        let config = make_basic_machine_config();
+        let mut machine = create_machine(&config)?;
+
+        let break_reason = machine.run(u64::MAX)?;
+        assert_eq!(break_reason, constants::break_reason::HALTED);
+
+        let mcycle_after_halt = machine.mcycle()?;
+        let root_hash_before = machine.root_hash()?;
+
+        let break_reason = machine.run(u64::MAX)?;
+        assert_eq!(break_reason, constants::break_reason::HALTED);
+
+        let root_hash_after = machine.root_hash()?;
+        assert_eq!(root_hash_before, root_hash_after,);
+
+        let mcycle_after_second_run = machine.mcycle()?;
+        assert_eq!(mcycle_after_halt, mcycle_after_second_run,);
+
+        Ok(())
     }
 
-    /// Writes the value of a floating-point register.
-    pub fn write_f(&mut self, i: u32, value: u64) -> Result<(), MachineError> {
-        let mut error_collector = ErrorCollector::new();
+    #[test]
+    fn test_machine_uarch_reset() -> Result<()> {
+        let config = make_basic_machine_config();
+        let mut machine = create_machine(&config)?;
 
-        let result = unsafe {
-            cartesi_machine_sys::cm_write_f(
-                self.machine,
-                i as i32,
-                value,
-                error_collector.as_mut_ptr(),
-            )
-        };
-        error_collector.collect(result)?;
+        machine.run(1)?;
+        let reference_hash = machine.root_hash()?;
+        let initial_config = machine.initial_config()?;
+        drop(machine);
+
+        let mut machine = create_machine(&initial_config)?;
+
+        let uarch_break_reason = machine.run_uarch(u64::MAX)?;
+        assert_eq!(
+            uarch_break_reason,
+            constants::uarch_break_reason::UARCH_HALTED
+        );
+
+        machine.reset_uarch()?;
+
+        let final_hash = machine.root_hash()?;
+        assert_eq!(reference_hash, final_hash,);
+
+        let ucycle = machine.ucycle()?;
+        assert_eq!(ucycle, 0);
 
         Ok(())
     }
 
-    /// Gets the address of a floating-point register.
-    pub fn get_f_address(&mut self, i: u32) -> u64 {
-        unsafe { cartesi_machine_sys::cm_get_f_address(i as i32) }
+    #[test]
+    fn test_runtime_config_round_trip() -> Result<()> {
+        let config = make_basic_machine_config();
+        let mut machine = create_machine(&config)?;
+
+        let original_config = machine.runtime_config()?;
+        machine.set_runtime_config(&original_config)?;
+
+        Ok(())
     }
 
-    /// Returns copy of initialization config.
-    pub fn initial_config(&self) -> Result<MachineConfigRef, MachineError> {
-        MachineConfigRef::try_new(self)
+    #[test]
+    fn test_log_step() -> Result<()> {
+        let tmp_dir = tempfile::tempdir().expect("failed creating a temp dir");
+        let log_path = tmp_dir.path().join("machine_step.log");
+
+        let config = make_basic_machine_config();
+        let mut machine = create_machine(&config)?;
+
+        let root_hash_before = machine.root_hash()?;
+
+        machine.log_step(50, &log_path)?;
+        let root_hash_after = machine.root_hash()?;
+
+        let verified_break_reason =
+            Machine::verify_step(&root_hash_before, &log_path, 50, &root_hash_after)?;
+        assert_ne!(verified_break_reason, constants::break_reason::FAILED);
+
+        Ok(())
     }
 
-    /// Returns copy of default system config.
-    pub fn default_config() -> MachineConfigRef {
-        MachineConfigRef::default()
+    #[test]
+    fn test_log_step_uarch() -> Result<()> {
+        let config = make_basic_machine_config();
+        let mut machine = create_machine(&config)?;
+
+        let root_hash_before = machine.root_hash()?;
+        let access_log: AccessLog = machine.log_step_uarch(LogType::LargeData)?;
+        let root_hash_after = machine.root_hash()?;
+
+        Machine::verify_step_uarch(&root_hash_before, &access_log, &root_hash_after)?;
+
+        Ok(())
     }
 
-    /// Replaces a memory range
-    pub fn replace_memory_range(
-        &mut self,
-        new_range: &MemoryRangeConfig,
-    ) -> Result<(), MachineError> {
-        let mut error_collector = ErrorCollector::new();
+    #[test]
+    fn test_log_reset_uarch() -> Result<()> {
+        let config = make_basic_machine_config();
+        let mut machine = create_machine(&config)?;
 
-        let result = unsafe {
-            cartesi_machine_sys::cm_replace_memory_range(
-                self.machine,
-                new_range,
-                error_collector.as_mut_ptr(),
-            )
-        };
-        error_collector.collect(result)?;
+        let root_hash_before = machine.root_hash()?;
+        let reset_log = machine.log_reset_uarch(LogType::Annotations)?;
+        let root_hash_after = machine.root_hash()?;
+
+        Machine::verify_reset_uarch(&root_hash_before, &reset_log, &root_hash_after)?;
 
         Ok(())
     }
 
-    /// Verify if dirty page maps are consistent.
-    pub fn verify_dirty_page_maps(&mut self) -> Result<bool, MachineError> {
-        let mut error_collector = ErrorCollector::new();
-        let mut ret = false;
+    #[test]
+    fn test_log_send_cmio_response() -> Result<()> {
+        let config = make_cmio_machine_config();
+        let mut machine = create_machine(&config)?;
 
-        let result = unsafe {
-            cartesi_machine_sys::cm_verify_dirty_page_maps(
-                self.machine,
-                &mut ret,
-                error_collector.as_mut_ptr(),
-            )
-        };
-        error_collector.collect(result)?;
+        let break_reason = machine.run(u64::MAX)?;
+        assert_eq!(break_reason, constants::break_reason::YIELDED_MANUALLY);
+
+        let root_hash_before = machine.root_hash()?;
+
+        let request = machine.receive_cmio_request()?;
+        assert!(matches!(
+            request,
+            CmioRequest::Manual(ManualReason::GIO { domain: 16, ref data })
+            if data == b"Hello from inside!"
+        ));
+        let response_data = b"Hello from outside!";
+        let access_log: AccessLog = machine.log_send_cmio_response(
+            CmioResponseReason::Advance,
+            response_data,
+            LogType::LargeData,
+        )?;
 
-        Ok(ret)
+        let root_hash_after = machine.root_hash()?;
+
+        Machine::verify_send_cmio_response(
+            CmioResponseReason::Advance,
+            response_data,
+            &root_hash_before,
+            &access_log,
+            &root_hash_after,
+        )?;
+
+        Ok(())
     }
 
-    /// Resets the value of the microarchitecture halt flag.
-    pub fn log_uarch_reset(
-        &mut self,
-        log_type: log::AccessLogType,
-        one_based: bool,
-    ) -> Result<log::AccessLog, MachineError> {
-        let mut error_collector = ErrorCollector::new();
-        let mut access_log = std::ptr::null_mut();
-
-        let result = unsafe {
-            cartesi_machine_sys::cm_log_uarch_reset(
-                self.machine,
-                log_type.into(),
-                one_based,
-                &mut access_log,
-                error_collector.as_mut_ptr(),
-            )
-        };
-        error_collector.collect(result)?;
+    #[test]
+    fn test_machine_cmio() -> Result<()> {
+        let cmio_config = make_cmio_machine_config();
+        let mut machine = create_machine(&cmio_config)?;
+
+        let break_reason = machine.run(u64::MAX)?;
+        assert_eq!(break_reason, constants::break_reason::YIELDED_MANUALLY);
+
+        let request = machine.receive_cmio_request()?;
+        assert!(matches!(
+            request,
+            CmioRequest::Manual(ManualReason::GIO { domain: 16, ref data })
+            if data == b"Hello from inside!"
+        ));
+
+        let response = b"Hello from outside!";
+        machine.send_cmio_response(CmioResponseReason::Advance, response)?;
+
+        let break_reason = machine.run(u64::MAX)?;
+        assert_eq!(break_reason, constants::break_reason::HALTED);
 
-        Ok(log::AccessLog::new(access_log))
+        Ok(())
     }
-}
 
-macro_rules! read_csr {
-    ($typ: ty, $name: ident, $flag: ident) => {
-        pub fn $name(&self) -> Result<$typ, MachineError> {
-            let mut error_collector = ErrorCollector::new();
-            let mut value: $typ = Default::default();
+    #[test]
+    fn test_store_and_load_machine() -> Result<()> {
+        let config = make_basic_machine_config();
+        let mut machine = create_machine(&config)?;
 
-            let result = unsafe {
-                cartesi_machine_sys::$flag(self.machine, &mut value, error_collector.as_mut_ptr())
-            };
-            error_collector.collect(result)?;
+        let break_reason = machine.run(1000)?;
+        assert_eq!(break_reason, constants::break_reason::REACHED_TARGET_MCYCLE);
+        let root_hash_before_store = machine.root_hash()?;
 
-            Ok(value)
-        }
-    };
-}
+        let tmp_dir = tempfile::tempdir().expect("failed creating a temp dir");
+        let store_path = tmp_dir.path().join("image");
+        machine.store(&store_path)?;
 
-macro_rules! write_csr {
-    ($typ: ty, $name: ident, $flag: ident) => {
-        pub fn $name(&mut self, value: $typ) -> Result<(), MachineError> {
-            let mut error_collector = ErrorCollector::new();
+        let runtime_config = RuntimeConfig::default();
+        let mut machine = Machine::load(&store_path, &runtime_config)?;
 
-            let result = unsafe {
-                cartesi_machine_sys::$flag(self.machine, value, error_collector.as_mut_ptr())
-            };
-            error_collector.collect(result)?;
+        let root_hash_after_load = machine.root_hash()?;
+        assert_eq!(root_hash_before_store, root_hash_after_load,);
 
-            Ok(())
-        }
-    };
-}
+        Ok(())
+    }
 
-macro_rules! iflags {
-    ($name: ident, $flag: ident) => {
-        pub fn $name(&mut self) -> Result<(), MachineError> {
-            let mut error_collector = ErrorCollector::new();
+    #[test]
+    fn test_memory_range() -> Result<()> {
+        let config = make_basic_machine_config();
+        let mut machine = create_machine(&config)?;
 
-            let result =
-                unsafe { cartesi_machine_sys::$flag(self.machine, error_collector.as_mut_ptr()) };
-            error_collector.collect(result)?;
+        let ranges_before = machine.memory_ranges()?;
+        assert!(!ranges_before.is_empty());
 
-            Ok(())
-        }
-    };
-}
+        let range = ranges_before.last().unwrap();
 
-impl Machine {
-    iflags!(set_iflags_x, cm_set_iflags_X);
-    iflags!(reset_iflags_x, cm_reset_iflags_X);
-    iflags!(set_iflags_y, cm_set_iflags_Y);
-    iflags!(reset_iflags_y, cm_reset_iflags_Y);
-    iflags!(set_iflags_h, cm_set_iflags_H);
-    iflags!(set_uarch_halt_flag, cm_set_uarch_halt_flag);
-    iflags!(reset_uarch, cm_reset_uarch);
-
-    read_csr!(u64, read_pc, cm_read_pc);
-    read_csr!(u64, read_fcsr, cm_read_fcsr);
-    read_csr!(u64, read_mvendorid, cm_read_mvendorid);
-    read_csr!(u64, read_marchid, cm_read_marchid);
-    read_csr!(u64, read_mimpid, cm_read_mimpid);
-    read_csr!(u64, read_mcycle, cm_read_mcycle);
-    read_csr!(u64, read_icycleinstret, cm_read_icycleinstret);
-    read_csr!(u64, read_mstatus, cm_read_mstatus);
-    read_csr!(u64, read_menvcfg, cm_read_menvcfg);
-    read_csr!(u64, read_mtvec, cm_read_mtvec);
-    read_csr!(u64, read_mscratch, cm_read_mscratch);
-    read_csr!(u64, read_mepc, cm_read_mepc);
-    read_csr!(u64, read_mcause, cm_read_mcause);
-    read_csr!(u64, read_mtval, cm_read_mtval);
-    read_csr!(u64, read_misa, cm_read_misa);
-    read_csr!(u64, read_mie, cm_read_mie);
-    read_csr!(u64, read_mip, cm_read_mip);
-    read_csr!(u64, read_medeleg, cm_read_medeleg);
-    read_csr!(u64, read_mideleg, cm_read_mideleg);
-    read_csr!(u64, read_mcounteren, cm_read_mcounteren);
-    read_csr!(u64, read_stvec, cm_read_stvec);
-    read_csr!(u64, read_sscratch, cm_read_sscratch);
-    read_csr!(u64, read_sepc, cm_read_sepc);
-    read_csr!(u64, read_scause, cm_read_scause);
-    read_csr!(u64, read_stval, cm_read_stval);
-    read_csr!(u64, read_satp, cm_read_satp);
-    read_csr!(u64, read_scounteren, cm_read_scounteren);
-    read_csr!(u64, read_senvcfg, cm_read_senvcfg);
-    read_csr!(u64, read_ilrsc, cm_read_ilrsc);
-    read_csr!(u64, read_iflags, cm_read_iflags);
-    read_csr!(u64, read_htif_tohost, cm_read_htif_tohost);
-    read_csr!(u64, read_htif_tohost_dev, cm_read_htif_tohost_dev);
-    read_csr!(u64, read_htif_tohost_cmd, cm_read_htif_tohost_cmd);
-    read_csr!(u64, read_htif_tohost_data, cm_read_htif_tohost_data);
-    read_csr!(u64, read_htif_fromhost, cm_read_htif_fromhost);
-    read_csr!(u64, read_htif_ihalt, cm_read_htif_ihalt);
-    read_csr!(u64, read_htif_iconsole, cm_read_htif_iconsole);
-    read_csr!(u64, read_htif_iyield, cm_read_htif_iyield);
-    read_csr!(u64, read_clint_mtimecmp, cm_read_clint_mtimecmp);
-    read_csr!(bool, read_iflags_x, cm_read_iflags_X);
-    read_csr!(bool, read_iflags_y, cm_read_iflags_Y);
-    read_csr!(bool, read_iflags_h, cm_read_iflags_H);
-    read_csr!(u64, read_uarch_pc, cm_read_uarch_pc);
-    read_csr!(u64, read_uarch_cycle, cm_read_uarch_cycle);
-    read_csr!(bool, read_uarch_halt_flag, cm_read_uarch_halt_flag);
-
-    write_csr!(u64, write_pc, cm_write_pc);
-    write_csr!(u64, write_fcsr, cm_write_fcsr);
-    write_csr!(u64, write_mcycle, cm_write_mcycle);
-    write_csr!(u64, write_icycleinstret, cm_write_icycleinstret);
-    write_csr!(u64, write_mstatus, cm_write_mstatus);
-    write_csr!(u64, write_menvcfg, cm_write_menvcfg);
-    write_csr!(u64, write_mtvec, cm_write_mtvec);
-    write_csr!(u64, write_mscratch, cm_write_mscratch);
-    write_csr!(u64, write_mepc, cm_write_mepc);
-    write_csr!(u64, write_mcause, cm_write_mcause);
-    write_csr!(u64, write_mtval, cm_write_mtval);
-    write_csr!(u64, write_misa, cm_write_misa);
-    write_csr!(u64, write_mie, cm_write_mie);
-    write_csr!(u64, write_mip, cm_write_mip);
-    write_csr!(u64, write_medeleg, cm_write_medeleg);
-    write_csr!(u64, write_mideleg, cm_write_mideleg);
-    write_csr!(u64, write_mcounteren, cm_write_mcounteren);
-    write_csr!(u64, write_stvec, cm_write_stvec);
-    write_csr!(u64, write_sscratch, cm_write_sscratch);
-    write_csr!(u64, write_sepc, cm_write_sepc);
-    write_csr!(u64, write_scause, cm_write_scause);
-    write_csr!(u64, write_stval, cm_write_stval);
-    write_csr!(u64, write_satp, cm_write_satp);
-    write_csr!(u64, write_scounteren, cm_write_scounteren);
-    write_csr!(u64, write_senvcfg, cm_write_senvcfg);
-    write_csr!(u64, write_ilrsc, cm_write_ilrsc);
-    write_csr!(u64, write_iflags, cm_write_iflags);
-    write_csr!(u64, write_htif_tohost, cm_write_htif_tohost);
-    write_csr!(u64, write_htif_fromhost, cm_write_htif_fromhost);
-    write_csr!(u64, write_htif_ihalt, cm_write_htif_ihalt);
-    write_csr!(u64, write_htif_iconsole, cm_write_htif_iconsole);
-    write_csr!(u64, write_htif_iyield, cm_write_htif_iyield);
-    write_csr!(u64, write_clint_mtimecmp, cm_write_clint_mtimecmp);
-    write_csr!(u64, write_uarch_pc, cm_write_uarch_pc);
-    write_csr!(u64, write_uarch_cycle, cm_write_uarch_cycle);
-}
+        let mem = machine.read_memory(range.start, range.length)?;
+        assert!(mem.iter().any(|x| *x != 0));
+
+        // writes zeroes in range
+        machine.replace_memory_range(range.start, range.length, false, None)?;
+        let mem = machine.read_memory(range.start, range.length)?;
+        assert!(!mem.iter().any(|x| *x != 0));
+
+        // write ones in range
+        machine.write_memory(range.start, &vec![1; range.length as usize])?;
+        let mem = machine.read_memory(range.start, range.length)?;
+        assert!(mem.iter().all(|x| *x == 1));
+
+        let log2_size = u64::BITS - range.length.leading_zeros();
+        let proof: Proof = machine.proof(range.start, u64::BITS - range.length.leading_zeros())?;
+        assert_eq!(proof.target_address, range.start);
+        assert_eq!(proof.log2_target_size, log2_size as u64);
+
+        Ok(())
+    }
 
-/// Returns packed iflags from its component fields.
-pub fn packed_iflags(prv: i32, x: i32, y: i32, h: i32) -> u64 {
-    unsafe { cartesi_machine_sys::cm_packed_iflags(prv, x, y, h) }
+    #[test]
+    fn test_memory_ops() -> Result<()> {
+        let config = make_basic_machine_config();
+        let mut machine = create_machine(&config)?;
+
+        let reg = cartesi_machine_sys::CM_REG_X1;
+        let reg_phys_addr = Machine::reg_address(reg)?;
+
+        let val_via_read_word = machine.read_word(reg_phys_addr)?;
+        let val_via_read_reg = machine.read_reg(reg)?;
+        assert_eq!(val_via_read_word, val_via_read_reg);
+
+        let new_reg_value = 0x1234_5678_9ABC_DEF0;
+        machine.write_reg(reg, new_reg_value)?;
+        let val_via_read_word2 = machine.read_word(reg_phys_addr)?;
+        assert_eq!(val_via_read_word2, new_reg_value);
+
+        let val_via_read_reg2 = machine.read_reg(reg)?;
+        assert_eq!(val_via_read_reg2, new_reg_value);
+
+        Ok(())
+    }
 }
diff --git a/machine/rust-bindings/cartesi-machine/src/proof.rs b/machine/rust-bindings/cartesi-machine/src/proof.rs
deleted file mode 100644
index 38e93254..00000000
--- a/machine/rust-bindings/cartesi-machine/src/proof.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-//! Structures for merkle proofs
-
-use crate::hash::Hash;
-
-/// Merkle tree proof structure
-pub struct MerkleTreeProof(*mut cartesi_machine_sys::cm_merkle_tree_proof);
-
-impl Drop for MerkleTreeProof {
-    fn drop(&mut self) {
-        unsafe { cartesi_machine_sys::cm_delete_merkle_tree_proof(self.0) };
-    }
-}
-
-impl MerkleTreeProof {
-    pub(crate) fn new(ptr: *mut cartesi_machine_sys::cm_merkle_tree_proof) -> Self {
-        Self(ptr)
-    }
-
-    /// Address of the target node
-    pub fn target_address(&self) -> u64 {
-        unsafe { (*self.0).target_address }
-    }
-
-    /// Log2 of size of target node
-    pub fn log2_target_size(&self) -> usize {
-        unsafe { (*self.0).log2_target_size }
-    }
-
-    /// Hash of target node
-    pub fn target_hash(&self) -> Hash {
-        Hash::new(unsafe { (*self.0).target_hash })
-    }
-
-    /// Log2 of size of root node
-    pub fn log2_root_size(&self) -> usize {
-        unsafe { (*self.0).log2_root_size }
-    }
-
-    /// Hash of root node
-    pub fn root_hash(&self) -> Hash {
-        Hash::new(unsafe { (*self.0).root_hash })
-    }
-
-    /// Sibling hashes towards root
-    pub fn sibling_hashes(&self) -> Vec<Hash> {
-        let sibling_hashes = unsafe { (*self.0).sibling_hashes };
-        let sibling_hashes =
-            unsafe { std::slice::from_raw_parts(sibling_hashes.entry, sibling_hashes.count) };
-
-        sibling_hashes.iter().map(|hash| Hash::new(*hash)).collect()
-    }
-}
diff --git a/machine/rust-bindings/cartesi-machine/src/types/access_proof.rs b/machine/rust-bindings/cartesi-machine/src/types/access_proof.rs
new file mode 100644
index 00000000..6a4c5f3c
--- /dev/null
+++ b/machine/rust-bindings/cartesi-machine/src/types/access_proof.rs
@@ -0,0 +1,91 @@
+// (c) Cartesi and individual authors (see AUTHORS)
+// SPDX-License-Identifier: Apache-2.0 (see LICENSE)
+
+use crate::types::{base64_decode::*, Hash};
+use serde::{Deserialize, Serialize};
+
+pub type NoteArray = Vec<String>;
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+#[serde(rename_all = "snake_case")]
+pub enum BracketType {
+    Begin,
+    End,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+#[serde(rename_all = "snake_case")]
+pub enum AccessType {
+    Read,
+    Write,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct Bracket {
+    pub r#type: BracketType,
+    pub r#where: u64,
+    pub text: String,
+}
+
+pub type BracketArray = Vec<Bracket>;
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct Access {
+    pub r#type: AccessType,
+    pub address: u64,
+    pub log2_size: u64,
+
+    #[serde(
+        deserialize_with = "deserialize_base64_32",
+        serialize_with = "serialize_base64_32"
+    )]
+    pub read_hash: Hash,
+
+    #[serde(
+        deserialize_with = "deserialize_base64_vec_opt",
+        serialize_with = "serialize_base64_vec_opt",
+        default,
+        skip_serializing_if = "Option::is_none"
+    )]
+    pub read: Option<Vec<u8>>,
+
+    #[serde(
+        deserialize_with = "deserialize_base64_32_opt",
+        serialize_with = "serialize_base64_32_opt",
+        default,
+        skip_serializing_if = "Option::is_none"
+    )]
+    pub written_hash: Option<Hash>,
+
+    #[serde(
+        deserialize_with = "deserialize_base64_vec_opt",
+        serialize_with = "serialize_base64_vec_opt",
+        default,
+        skip_serializing_if = "Option::is_none"
+    )]
+    pub written: Option<Vec<u8>>,
+
+    #[serde(
+        deserialize_with = "deserialize_base64_32_array_opt",
+        serialize_with = "serialize_base64_32_array_opt",
+        default,
+        skip_serializing_if = "Option::is_none"
+    )]
+    pub sibling_hashes: Option<Vec<Hash>>,
+}
+
+pub type AccessArray = Vec<Access>;
+
+#[derive(Clone, Debug, Default, Deserialize, Serialize)]
+pub struct AccessLogType {
+    pub has_annotations: bool,
+    pub has_large_data: bool,
+}
+
+#[derive(Clone, Debug, Default, Deserialize, Serialize)]
+pub struct AccessLog {
+    pub log_type: AccessLogType,
+    pub accesses: AccessArray,
+    pub notes: Option<NoteArray>,
+    pub brackets: Option<BracketArray>,
+}
diff --git a/machine/rust-bindings/cartesi-machine/src/types/base64_decode.rs b/machine/rust-bindings/cartesi-machine/src/types/base64_decode.rs
new file mode 100644
index 00000000..ee9a874e
--- /dev/null
+++ b/machine/rust-bindings/cartesi-machine/src/types/base64_decode.rs
@@ -0,0 +1,299 @@
+// (c) Cartesi and individual authors (see AUTHORS)
+// SPDX-License-Identifier: Apache-2.0 (see LICENSE)
+
+use crate::types::Hash;
+use base64::{prelude::BASE64_STANDARD, Engine};
+use serde::{Deserialize, Deserializer, Serialize, Serializer};
+
+macro_rules! decode_base64_str {
+    ($base64_string:expr) => {
+        BASE64_STANDARD
+            .decode($base64_string)
+            .map_err(|err| serde::de::Error::custom(format!("base64 decode error: {err}")))
+    };
+}
+
+macro_rules! encode_to_base64_str {
+    ($bytes:expr) => {
+        BASE64_STANDARD.encode($bytes)
+    };
+}
+
+macro_rules! unwrap_ref {
+    ($val:expr) => {
+        $val.as_ref().expect("serde should skip when None")
+    };
+}
+
+macro_rules! decode_base64_hash {
+    ($base64_string:expr) => {{
+        let bytes = decode_base64_str!($base64_string)?;
+        if bytes.len() != 32 {
+            Err(serde::de::Error::invalid_length(bytes.len(), &"32"))
+        } else {
+            let mut hash = Hash::default();
+            hash.copy_from_slice(&bytes);
+            Ok(hash)
+        }
+    }};
+}
+
+#[allow(dead_code)]
+pub fn deserialize_base64_vec<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    let base64_string = String::deserialize(deserializer)?;
+    decode_base64_str!(&base64_string)
+}
+
+#[allow(dead_code)]
+pub fn serialize_base64_vec<S>(val: &Vec<u8>, serializer: S) -> Result<S::Ok, S::Error>
+where
+    S: Serializer,
+{
+    let encoded = encode_to_base64_str!(&val);
+    serializer.serialize_str(&encoded)
+}
+
+pub fn deserialize_base64_vec_opt<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    let opt_str = Option::<String>::deserialize(deserializer)?;
+
+    match opt_str {
+        None => Ok(None),
+        Some(base64_string) => Ok(Some(decode_base64_str!(&base64_string)?)),
+    }
+}
+
+pub fn serialize_base64_vec_opt<S>(val: &Option<Vec<u8>>, serializer: S) -> Result<S::Ok, S::Error>
+where
+    S: Serializer,
+{
+    let encoded = encode_to_base64_str!(unwrap_ref!(&val));
+    serializer.serialize_str(&encoded)
+}
+
+pub fn deserialize_base64_32<'de, D>(deserializer: D) -> Result<Hash, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    let base64_string = String::deserialize(deserializer)?;
+    decode_base64_hash!(&base64_string)
+}
+
+pub fn serialize_base64_32<S>(val: &Hash, serializer: S) -> Result<S::Ok, S::Error>
+where
+    S: Serializer,
+{
+    let encoded = encode_to_base64_str!(&val);
+    serializer.serialize_str(&encoded)
+}
+
+pub fn deserialize_base64_32_opt<'de, D>(deserializer: D) -> Result<Option<Hash>, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    let opt_str = Option::<String>::deserialize(deserializer)?;
+    match opt_str {
+        None => Ok(None),
+        Some(base64_string) => Ok(Some(decode_base64_hash!(&base64_string)?)),
+    }
+}
+
+pub fn serialize_base64_32_opt<S>(val: &Option<Hash>, serializer: S) -> Result<S::Ok, S::Error>
+where
+    S: Serializer,
+{
+    let encoded = encode_to_base64_str!(unwrap_ref!(&val));
+    serializer.serialize_str(&encoded)
+}
+
+pub fn deserialize_base64_32_array<'de, D>(deserializer: D) -> Result<Vec<Hash>, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    let strings = Vec::<String>::deserialize(deserializer)?;
+    let mut out = Vec::with_capacity(strings.len());
+
+    for base64_string in strings {
+        out.push(decode_base64_hash!(&base64_string)?);
+    }
+
+    Ok(out)
+}
+
+pub fn serialize_base64_32_array<S>(val: &[Hash], serializer: S) -> Result<S::Ok, S::Error>
+where
+    S: Serializer,
+{
+    let encoded: Vec<String> = val.iter().map(|arr| encode_to_base64_str!(&arr)).collect();
+
+    encoded.serialize(serializer)
+}
+
+pub fn deserialize_base64_32_array_opt<'de, D>(
+    deserializer: D,
+) -> Result<Option<Vec<Hash>>, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    let opt_strings = Option::<Vec<String>>::deserialize(deserializer)?;
+
+    match opt_strings {
+        None => Ok(None),
+        Some(strings) => {
+            let mut out = Vec::with_capacity(strings.len());
+            for base64_string in strings {
+                out.push(decode_base64_hash!(&base64_string)?);
+            }
+            Ok(Some(out))
+        }
+    }
+}
+
+pub fn serialize_base64_32_array_opt<S>(
+    val: &Option<Vec<Hash>>,
+    serializer: S,
+) -> Result<S::Ok, S::Error>
+where
+    S: Serializer,
+{
+    let hashes = unwrap_ref!(&val);
+
+    // Map each Hash to a base64 string
+    let encoded: Vec<String> = hashes
+        .iter()
+        .map(|arr| encode_to_base64_str!(&arr))
+        .collect();
+
+    encoded.serialize(serializer)
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use serde_json::from_str;
+
+    use serde::Deserialize;
+
+    #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
+    pub struct Foo {
+        #[serde(
+            deserialize_with = "deserialize_base64_vec",
+            serialize_with = "serialize_base64_vec"
+        )]
+        pub bytes: Vec<u8>,
+
+        #[serde(
+            deserialize_with = "deserialize_base64_vec_opt",
+            serialize_with = "serialize_base64_vec_opt",
+            default,
+            skip_serializing_if = "Option::is_none"
+        )]
+        pub bytes_opt: Option<Vec<u8>>,
+
+        #[serde(
+            deserialize_with = "deserialize_base64_32",
+            serialize_with = "serialize_base64_32"
+        )]
+        pub hash: Hash,
+
+        #[serde(
+            deserialize_with = "deserialize_base64_32_opt",
+            serialize_with = "serialize_base64_32_opt",
+            default,
+            skip_serializing_if = "Option::is_none"
+        )]
+        pub hash_opt: Option<Hash>,
+
+        #[serde(
+            deserialize_with = "deserialize_base64_32_array",
+            serialize_with = "serialize_base64_32_array"
+        )]
+        pub hashes: Vec<Hash>,
+
+        #[serde(
+            deserialize_with = "deserialize_base64_32_array_opt",
+            serialize_with = "serialize_base64_32_array_opt",
+            default,
+            skip_serializing_if = "Option::is_none"
+        )]
+        pub hashes_opt: Option<Vec<Hash>>,
+    }
+
+    #[test]
+    fn test_deserialize_base64() {
+        let json_data = r#"
+        {
+          "bytes": "SGVsbG8gV29ybGQh",
+          "hash":  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
+          "hashes": [
+            "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
+            "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE="
+          ]
+        }
+        "#;
+
+        let proof: Foo = from_str(json_data).expect("should deserialize fine");
+
+        // bytes -> "Hello World!"
+        assert_eq!(proof.bytes, b"Hello World!");
+
+        // hash -> 32 bytes of zeros
+        assert_eq!(proof.hash, [0u8; 32]);
+
+        // hashes -> first is 32 zeros, second is 32 ones
+        assert_eq!(proof.hashes.len(), 2);
+        assert_eq!(proof.hashes[0], [0u8; 32]);
+        assert_eq!(proof.hashes[1], [1u8; 32]);
+
+        assert!(proof.bytes_opt.is_none());
+        assert!(proof.hash_opt.is_none());
+        assert!(proof.hashes_opt.is_none());
+
+        assert_eq!(
+            proof,
+            from_str(&serde_json::to_string(&proof).unwrap()).unwrap()
+        );
+    }
+
+    #[test]
+    fn test_deserialize_proof_with_all_fields() {
+        let json_data = r#"
+        {
+          "bytes_opt": "SGVsbG8gV29ybGQh",
+          "bytes": "SGVsbG8gV29ybGQh",
+
+          "hash":  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
+          "hash_opt":  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
+
+          "hashes": [
+            "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
+            "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE="
+          ],
+          "hashes_opt": [
+            "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
+            "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE="
+          ]
+        }
+        "#;
+
+        let proof: Foo = from_str(json_data).expect("should deserialize fine");
+        assert_eq!(proof.bytes, b"Hello World!".to_vec());
+        assert_eq!(proof.bytes_opt, Some(b"Hello World!".to_vec()));
+        assert_eq!(proof.hash, [0u8; 32]);
+        assert_eq!(proof.hash_opt, Some([0u8; 32]));
+        assert_eq!(proof.hashes.len(), 2);
+        assert_eq!(proof.hashes[1], [1u8; 32]);
+        assert_eq!(proof.hashes_opt.as_ref().unwrap().len(), 2);
+        assert_eq!(proof.hashes_opt.as_ref().unwrap()[1], [1u8; 32]);
+
+        assert_eq!(
+            proof,
+            from_str(&serde_json::to_string(&proof).unwrap()).unwrap()
+        );
+    }
+}
diff --git a/machine/rust-bindings/cartesi-machine/src/types/cmio.rs b/machine/rust-bindings/cartesi-machine/src/types/cmio.rs
new file mode 100644
index 00000000..141356ab
--- /dev/null
+++ b/machine/rust-bindings/cartesi-machine/src/types/cmio.rs
@@ -0,0 +1,155 @@
+// (c) Cartesi and individual authors (see AUTHORS)
+// SPDX-License-Identifier: Apache-2.0 (see LICENSE)
+
+use crate::{constants, types::Hash};
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum CmioRequest {
+    Automatic(AutomaticReason),
+    Manual(ManualReason),
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum AutomaticReason {
+    Progress { mille_progress: u32 },
+    TxOutput { data: Vec<u8> },
+    TxReport { data: Vec<u8> },
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum ManualReason {
+    RxAccepted { output_hashes_root_hash: Hash },
+    RxRejected,
+    TxException { message: String },
+    GIO { domain: u16, data: Vec<u8> },
+}
+
+impl CmioRequest {
+    pub fn new(cmd: u8, reason: u16, data: Vec<u8>) -> Self {
+        use constants::cmio::{commands, tohost::automatic, tohost::manual};
+        match cmd {
+            commands::YIELD_AUTOMATIC => Self::Automatic(match reason {
+                automatic::PROGRESS => AutomaticReason::Progress {
+                    mille_progress: u32::from_le_bytes(
+                        data.try_into().expect("malformed progress integer"),
+                    ),
+                },
+                automatic::TX_OUTPUT => AutomaticReason::TxOutput { data },
+                automatic::TX_REPORT => AutomaticReason::TxReport { data },
+                _ => panic!("Unknown automatic yield reason {}", reason),
+            }),
+
+            commands::YIELD_MANUAL => Self::Manual(match reason {
+                manual::RX_ACCEPTED => ManualReason::RxAccepted {
+                    output_hashes_root_hash: data
+                        .try_into()
+                        .expect("malformed `output_hashes_root_hash`"),
+                },
+                manual::RX_REJECTED => ManualReason::RxRejected,
+                manual::TX_EXCEPTION => ManualReason::TxException {
+                    message: String::from_utf8_lossy(&data).into_owned(),
+                },
+                domain => ManualReason::GIO { domain, data },
+            }),
+            _ => panic!("Unknown cmio command {}", cmd),
+        }
+    }
+
+    pub fn cmd_and_reason(&self) -> (u8, u16) {
+        use constants::cmio::{commands, tohost::automatic, tohost::manual};
+        match self {
+            CmioRequest::Automatic(automatic_reason) => (
+                commands::YIELD_AUTOMATIC,
+                match automatic_reason {
+                    AutomaticReason::Progress { .. } => automatic::PROGRESS,
+                    AutomaticReason::TxOutput { .. } => automatic::TX_OUTPUT,
+                    AutomaticReason::TxReport { .. } => automatic::TX_REPORT,
+                },
+            ),
+            CmioRequest::Manual(manual_reason) => (
+                commands::YIELD_MANUAL,
+                match manual_reason {
+                    ManualReason::RxAccepted { .. } => manual::RX_ACCEPTED,
+                    ManualReason::RxRejected => manual::RX_REJECTED,
+                    ManualReason::TxException { .. } => manual::TX_EXCEPTION,
+                    ManualReason::GIO { domain, .. } => *domain,
+                },
+            ),
+        }
+    }
+
+    pub fn cmd(&self) -> u8 {
+        self.cmd_and_reason().0
+    }
+
+    pub fn reason(&self) -> u16 {
+        self.cmd_and_reason().1
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum CmioResponseReason {
+    Advance = cartesi_machine_sys::CM_CMIO_YIELD_REASON_ADVANCE_STATE as isize,
+    Inspect = cartesi_machine_sys::CM_CMIO_YIELD_REASON_INSPECT_STATE as isize,
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use constants::cmio;
+
+    macro_rules! test_req {
+        ($cmd:expr, $reason:expr, $data:expr, $expected:expr) => {{
+            let r = CmioRequest::new($cmd, $reason, $data);
+            assert_eq!(r, $expected);
+            assert_eq!(r.cmd_and_reason(), ($cmd, $reason));
+            assert_eq!(r.cmd(), $cmd);
+            assert_eq!(r.reason(), $reason);
+        }};
+    }
+
+    #[test]
+    fn test_cmio_contructor() {
+        test_req!(
+            cmio::commands::YIELD_AUTOMATIC,
+            cmio::tohost::automatic::TX_REPORT,
+            vec![],
+            CmioRequest::Automatic(AutomaticReason::TxReport { data: vec![] })
+        );
+        test_req!(
+            cmio::commands::YIELD_AUTOMATIC,
+            cmio::tohost::automatic::TX_OUTPUT,
+            vec![],
+            CmioRequest::Automatic(AutomaticReason::TxOutput { data: vec![] })
+        );
+        test_req!(
+            cmio::commands::YIELD_AUTOMATIC,
+            cmio::tohost::automatic::PROGRESS,
+            42u32.to_ne_bytes().to_vec(),
+            CmioRequest::Automatic(AutomaticReason::Progress { mille_progress: 42 })
+        );
+
+        test_req!(
+            cmio::commands::YIELD_MANUAL,
+            cmio::tohost::manual::RX_ACCEPTED,
+            vec![0; 32],
+            CmioRequest::Manual(ManualReason::RxAccepted {
+                output_hashes_root_hash: Hash::default()
+            })
+        );
+        test_req!(
+            cmio::commands::YIELD_MANUAL,
+            cmio::tohost::manual::RX_REJECTED,
+            vec![],
+            CmioRequest::Manual(ManualReason::RxRejected)
+        );
+        test_req!(
+            cmio::commands::YIELD_MANUAL,
+            cmio::tohost::manual::TX_EXCEPTION,
+            vec![],
+            CmioRequest::Manual(ManualReason::TxException {
+                message: "".to_owned()
+            })
+        );
+    }
+}
diff --git a/machine/rust-bindings/cartesi-machine/src/types/memory_proof.rs b/machine/rust-bindings/cartesi-machine/src/types/memory_proof.rs
new file mode 100644
index 00000000..8b0986f3
--- /dev/null
+++ b/machine/rust-bindings/cartesi-machine/src/types/memory_proof.rs
@@ -0,0 +1,31 @@
+// (c) Cartesi and individual authors (see AUTHORS)
+// SPDX-License-Identifier: Apache-2.0 (see LICENSE)
+
+use crate::types::{base64_decode::*, Hash};
+use serde::{Deserialize, Serialize};
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct Proof {
+    pub target_address: u64,
+    pub log2_target_size: u64,
+
+    #[serde(
+        deserialize_with = "deserialize_base64_32",
+        serialize_with = "serialize_base64_32"
+    )]
+    pub target_hash: Hash,
+
+    pub log2_root_size: u64,
+
+    #[serde(
+        deserialize_with = "deserialize_base64_32",
+        serialize_with = "serialize_base64_32"
+    )]
+    pub root_hash: Hash,
+
+    #[serde(
+        deserialize_with = "deserialize_base64_32_array",
+        serialize_with = "serialize_base64_32_array"
+    )]
+    pub sibling_hashes: Vec<Hash>,
+}
diff --git a/machine/rust-bindings/cartesi-machine/src/types/memory_range.rs b/machine/rust-bindings/cartesi-machine/src/types/memory_range.rs
new file mode 100644
index 00000000..630c9944
--- /dev/null
+++ b/machine/rust-bindings/cartesi-machine/src/types/memory_range.rs
@@ -0,0 +1,13 @@
+// (c) Cartesi and individual authors (see AUTHORS)
+// SPDX-License-Identifier: Apache-2.0 (see LICENSE)
+
+use serde::Deserialize;
+
+#[derive(Clone, Debug, Default, Deserialize)]
+pub struct MemoryRangeDescription {
+    pub start: u64,
+    pub length: u64,
+    pub description: Option<String>,
+}
+
+pub type MemoryRangeDescriptions = Vec<MemoryRangeDescription>;
diff --git a/machine/rust-bindings/cartesi-machine/src/types/mod.rs b/machine/rust-bindings/cartesi-machine/src/types/mod.rs
new file mode 100644
index 00000000..bf8b712d
--- /dev/null
+++ b/machine/rust-bindings/cartesi-machine/src/types/mod.rs
@@ -0,0 +1,20 @@
+// (c) Cartesi and individual authors (see AUTHORS)
+// SPDX-License-Identifier: Apache-2.0 (see LICENSE)
+
+pub mod access_proof;
+pub mod cmio;
+pub mod memory_proof;
+pub mod memory_range;
+
+pub type Hash = cartesi_machine_sys::cm_hash;
+pub type Register = cartesi_machine_sys::cm_reg;
+pub type BreakReason = cartesi_machine_sys::cm_break_reason;
+pub type UArchBreakReason = cartesi_machine_sys::cm_uarch_break_reason;
+
+#[derive(Clone, Debug)]
+pub enum LogType {
+    Annotations = crate::constants::access_log_type::ANNOTATIONS as isize,
+    LargeData = crate::constants::access_log_type::LARGE_DATA as isize,
+}
+
+mod base64_decode;
diff --git a/machine/rust-bindings/cartesi-machine/src/utils.rs b/machine/rust-bindings/cartesi-machine/src/utils.rs
deleted file mode 100644
index bf1e546e..00000000
--- a/machine/rust-bindings/cartesi-machine/src/utils.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-// (c) Cartesi and individual authors (see AUTHORS)
-// SPDX-License-Identifier: Apache-2.0 (see LICENSE)
-
-//! This module contains functions for converting between Rust and C strings.
-
-use std::ffi::{c_char, CStr};
-
-pub(crate) fn from_cstr(cstr: *const c_char) -> Option<String> {
-    if cstr.is_null() {
-        None
-    } else {
-        unsafe { Some(CStr::from_ptr(cstr).to_string_lossy().into_owned()) }
-    }
-}
diff --git a/prt/client-rs/core/src/machine/instance.rs b/prt/client-rs/core/src/machine/instance.rs
index e341463b..9c945c7f 100644
--- a/prt/client-rs/core/src/machine/instance.rs
+++ b/prt/client-rs/core/src/machine/instance.rs
@@ -2,11 +2,10 @@ use crate::db::compute_state_access::{ComputeStateAccess, Input};
 use crate::machine::constants;
 use cartesi_dave_arithmetic as arithmetic;
 use cartesi_dave_merkle::Digest;
+use cartesi_machine::types::cmio::CmioResponseReason;
+use cartesi_machine::types::LogType;
 use cartesi_machine::{
-    configuration::RuntimeConfig,
-    htif,
-    log::{AccessLog, AccessLogType},
-    machine::Machine,
+    config::runtime::RuntimeConfig, machine::Machine, types::access_proof::AccessLog,
 };
 use log::{debug, trace};
 
@@ -48,19 +47,19 @@ pub struct MachineInstance {
 
 impl MachineInstance {
     pub fn new(snapshot_path: &str) -> Result<Self> {
-        let mut machine = Machine::load(&Path::new(snapshot_path), RuntimeConfig::default())?;
+        let mut machine = Machine::load(Path::new(snapshot_path), &RuntimeConfig::default())?;
 
-        let root_hash = machine.get_root_hash()?;
-        let start_cycle = machine.read_mcycle()?;
+        let root_hash = machine.root_hash()?;
+        let start_cycle = machine.mcycle()?;
 
         // Machine can never be advanced on the micro arch.
         // Validators must verify this first
-        assert_eq!(machine.read_uarch_cycle()?, 0);
+        assert_eq!(machine.ucycle()?, 0);
 
         Ok(MachineInstance {
             machine,
             start_cycle,
-            root_hash: Digest::from_digest(root_hash.as_bytes())?,
+            root_hash: Digest::from_digest(&root_hash)?,
             cycle: 0,
             ucycle: 0,
         })
@@ -84,15 +83,15 @@ impl MachineInstance {
 
     // load inner machine with snapshot, update cycle, keep everything else the same
     pub fn load_snapshot(&mut self, snapshot_path: &Path, snapshot_cycle: u64) -> Result<()> {
-        let machine = Machine::load(&Path::new(snapshot_path), RuntimeConfig::default())?;
+        let mut machine = Machine::load(Path::new(snapshot_path), &RuntimeConfig::default())?;
 
-        let cycle = machine.read_mcycle()?;
+        let cycle = machine.mcycle()?;
 
         // Machine can not go backward behind the initial machine
         assert!(cycle >= self.start_cycle);
         self.cycle = snapshot_cycle;
 
-        assert_eq!(machine.read_uarch_cycle()?, 0);
+        assert_eq!(machine.ucycle()?, 0);
 
         self.machine = machine;
 
@@ -112,12 +111,6 @@ impl MachineInstance {
         ucycle: u64,
         db: &ComputeStateAccess,
     ) -> Result<MachineProof> {
-        let log_type = AccessLogType {
-            annotations: true,
-            proofs: true,
-            large_data: false,
-        };
-
         let mut logs = Vec::new();
         let mut encode_input = None;
         if db.handle_rollups {
@@ -139,24 +132,21 @@ impl MachineInstance {
                     // need to process input
                     if ucycle == 0 {
                         let cmio_logs = self.machine.log_send_cmio_response(
-                            htif::fromhost::ADVANCE_STATE,
-                            &data,
-                            log_type,
-                            false,
+                            CmioResponseReason::Advance,
+                            data,
+                            LogType::LargeData,
                         )?;
                         // append step logs to cmio logs
-                        let step_logs = self.machine.log_uarch_step(log_type, false)?;
+                        let step_logs = self.machine.log_step_uarch(LogType::LargeData)?;
                         logs.push(&cmio_logs);
                         logs.push(&step_logs);
-                        return Ok(encode_access_logs(logs, Some(Input { 0: data.clone() })));
+                        return Ok(encode_access_logs(logs, Some(Input(data.clone()))));
                     } else {
                         self.machine
-                            .send_cmio_response(htif::fromhost::ADVANCE_STATE, &data)?;
-                    }
-                } else {
-                    if ucycle == 0 {
-                        encode_input = Some(Input { 0: Vec::new() });
+                            .send_cmio_response(CmioResponseReason::Advance, data)?;
                     }
+                } else if ucycle == 0 {
+                    encode_input = Some(Input(Vec::new()));
                 }
             }
         } else {
@@ -166,11 +156,11 @@ impl MachineInstance {
 
         self.run_uarch(ucycle)?;
         if ucycle == constants::UARCH_SPAN {
-            let reset_logs = self.machine.log_uarch_reset(log_type, false)?;
+            let reset_logs = self.machine.log_reset_uarch(LogType::LargeData)?;
             logs.push(&reset_logs);
             Ok(encode_access_logs(logs, encode_input))
         } else {
-            let step_logs = self.machine.log_uarch_step(log_type, false)?;
+            let step_logs = self.machine.log_step_uarch(LogType::LargeData)?;
             logs.push(&step_logs);
             Ok(encode_access_logs(logs, encode_input))
         }
@@ -180,25 +170,25 @@ impl MachineInstance {
     pub fn run(&mut self, cycle: u64) -> Result<MachineState> {
         assert!(self.cycle <= cycle);
 
-        let mcycle = self.machine.read_mcycle()?;
+        let mcycle = self.machine.mcycle()?;
 
         let physical_cycle = arithmetic::add_and_clamp(mcycle, cycle - self.cycle);
         trace!("physical cycle {}", physical_cycle);
 
         loop {
-            let halted = self.machine.read_iflags_h()?;
+            let halted = self.machine.iflags_h()?;
             if halted {
                 trace!("run break with halt");
                 break;
             }
 
-            let yielded = self.machine.read_iflags_y()?;
+            let yielded = self.machine.iflags_y()?;
             if yielded {
                 trace!("run break with yield");
                 break;
             }
 
-            if self.machine.read_mcycle()? == physical_cycle {
+            if self.machine.mcycle()? == physical_cycle {
                 trace!("run break with meeting physical cycle");
                 break;
             }
@@ -208,7 +198,7 @@ impl MachineInstance {
 
         self.cycle = cycle;
 
-        Ok(self.machine_state()?)
+        self.machine_state()
     }
 
     pub fn run_uarch(&mut self, ucycle: u64) -> Result<()> {
@@ -255,7 +245,7 @@ impl MachineInstance {
             trace!("run to next input cycle: {}", next_input_cycle);
             machine_state_without_input = self.run(next_input_cycle)?;
             if next_input_cycle == cycle {
-                self.take_snapshot(next_input_cycle, &db)?;
+                self.take_snapshot(next_input_cycle, db)?;
             }
 
             let input = inputs.get(next_input_index as usize);
@@ -267,7 +257,7 @@ impl MachineInstance {
                 trace!("input: 0x{}", data.encode_hex());
 
                 self.machine
-                    .send_cmio_response(htif::fromhost::ADVANCE_STATE, data)?;
+                    .send_cmio_response(CmioResponseReason::Advance, data)?;
 
                 trace!(
                     "after input, machine state: {}",
@@ -280,7 +270,7 @@ impl MachineInstance {
         }
         if cycle > self.cycle {
             machine_state_without_input = self.run(cycle)?;
-            self.take_snapshot(cycle, &db)?;
+            self.take_snapshot(cycle, db)?;
         }
         Ok(machine_state_without_input)
     }
@@ -288,24 +278,24 @@ impl MachineInstance {
     pub fn increment_uarch(&mut self) -> Result<MachineState> {
         self.machine.run_uarch(self.ucycle + 1)?;
         self.ucycle += 1;
-        Ok(self.machine_state()?)
+        self.machine_state()
     }
 
     pub fn ureset(&mut self) -> Result<MachineState> {
         self.machine.reset_uarch()?;
         self.cycle += 1;
         self.ucycle = 0;
-        Ok(self.machine_state()?)
+        self.machine_state()
     }
 
     pub fn machine_state(&mut self) -> Result<MachineState> {
-        let root_hash = self.machine.get_root_hash()?;
-        let halted = self.machine.read_iflags_h()?;
-        let yielded = self.machine.read_iflags_y()?;
-        let uhalted = self.machine.read_uarch_halt_flag()?;
+        let root_hash = self.machine.root_hash()?;
+        let halted = self.machine.iflags_h()?;
+        let yielded = self.machine.iflags_y()?;
+        let uhalted = self.machine.uarch_halt_flag()?;
 
         Ok(MachineState {
-            root_hash: Digest::from_digest(root_hash.as_bytes())?,
+            root_hash: Digest::from_digest(&root_hash)?,
             halted,
             yielded,
             uhalted,
@@ -318,8 +308,8 @@ impl MachineInstance {
         Ok(())
     }
 
-    pub fn position(&self) -> Result<(u64, u64, u64)> {
-        Ok((self.cycle, self.ucycle, self.machine.read_mcycle()?))
+    pub fn position(&mut self) -> Result<(u64, u64, u64)> {
+        Ok((self.cycle, self.ucycle, self.machine.mcycle()?))
     }
 }
 
@@ -328,23 +318,25 @@ fn encode_access_logs(logs: Vec<&AccessLog>, encode_input: Option<Input>) -> Vec
 
     if let Some(i) = encode_input {
         encoded.push(U256::from(i.0.len()).to_be_bytes_vec());
-        if i.0.len() > 0 {
+        if !i.0.is_empty() {
             encoded.push(i.0);
         }
     }
 
     for log in logs.iter() {
-        for a in log.accesses().iter() {
-            if a.log2_size() == 3 {
-                encoded.push(a.read_data().to_vec());
+        for a in log.accesses.iter() {
+            if a.log2_size == 3 {
+                encoded.push(a.read.clone().unwrap());
             } else {
-                encoded.push(a.read_hash().as_bytes().to_vec());
+                encoded.push(a.read_hash.to_vec());
             }
 
             let decoded_siblings: Vec<Vec<u8>> = a
-                .sibling_hashes()
+                .sibling_hashes
+                .clone()
+                .unwrap()
                 .iter()
-                .map(|h| h.as_bytes().to_vec())
+                .map(|h| h.to_vec())
                 .collect();
             encoded.extend_from_slice(&decoded_siblings);
         }
diff --git a/test/Dockerfile b/test/Dockerfile
index 7c0ca60e..6ddb0f20 100644
--- a/test/Dockerfile
+++ b/test/Dockerfile
@@ -2,7 +2,7 @@
 
 # to work wiht an unreleased verion, build machine docker image locally
 # (i.e. cartesi/machine-emulator:latest), and change tag.
-FROM cartesi/machine-emulator:0.18.1 AS machine
+FROM cartesi/machine-emulator:0.19.0-test2 AS machine
 
 FROM rust:1.83
 
@@ -38,7 +38,7 @@ RUN \
 # Install cartesi machine
 COPY \
   --from=machine \
-  /usr/lib/libcartesi-0.18.so /usr/lib/libcartesi.a \
+  /usr/lib/libcartesi-0.19.so /usr/lib/libcartesi.a \
   /usr/lib/
 COPY \
   --from=machine \