Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 112 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ on:
pull_request:

jobs:
native-tests:
# Default backend: pure-Rust MOCK ledger. Needs no C++ toolchain, no network, no
# submodule source beyond the headers — the fast, always-on gate.
mock-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -18,8 +20,75 @@ jobs:
- name: Install libclang for bindgen
run: sudo apt-get update && sudo apt-get install -y clang libclang-dev

- name: Run mocked native tests
run: cargo test --features native
- name: Run mocked tests
run: cargo test --features mock

# Formatting, lints, and docs. All three steps run even if an earlier one fails
# (`if: ${{ !cancelled() }}`) so each reports independently.
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy

- name: Install libclang for bindgen
run: sudo apt-get update && sudo apt-get install -y clang libclang-dev

# NOTE: this is a CHECK only — it never rewrites source. At the time this job was
# added the tree was NOT fully rustfmt-clean (pre-existing formatting in build.rs
# and src/*.rs owned by another workstream), so this step will stay RED until a
# one-time `cargo fmt --all` pass lands. That pass is a tracked follow-up.
- name: rustfmt (check only)
if: ${{ !cancelled() }}
run: cargo fmt --all --check

# WARNING-ONLY for now: there are pre-existing clippy lints in src/ that are owned
# by another workstream and must not be fixed here. Once they are cleared, tighten
# this to `-- -D warnings` so new lints fail CI.
# TODO(clippy-deny): append `-- -D warnings` after the src/ clippy cleanup lands.
- name: clippy (mock)
if: ${{ !cancelled() }}
run: cargo clippy --features mock --all-targets

- name: cargo doc (mock)
if: ${{ !cancelled() }}
run: cargo doc --no-deps --features mock

# REAL libcma compiled + linked for the host (x86_64). This is the backend that must
# produce byte-identical records to the riscv64 build, so it exercises the real C++
# ledger and the records-layout / reproducibility tests. Needs a g++ >= 14 toolchain,
# bindgen's libclang, and network access (Boost + nlohmann/json are fetched by build.rs).
host-real-tests:
runs-on: ubuntu-latest
env:
# build.rs's host path honours these (default g++/gcc, which on the runner is < 14).
CMA_HOST_CXX: g++-14
CMA_HOST_CC: gcc-14
# bindgen's builtin-header fallback shells out to $CC; keep it on the same major.
CC: gcc-14
CXX: g++-14
steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- uses: dtolnay/rust-toolchain@stable

- name: Install C++ toolchain + bindgen/build deps
run: |
sudo apt-get update
sudo apt-get install -y \
g++-14 gcc-14 build-essential \
clang libclang-dev \
wget make cmake

- name: Build + run host-real tests (real libcma linked for the host)
run: cargo test --no-default-features --features host-real -- --nocapture

riscv-link-check:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -59,3 +128,43 @@ jobs:
ar p "$lib" "$member" > "$obj"
file "$obj" | grep -F 'RISC-V'
rm -f "$obj"

# ===========================================================================
# TODO(cross-arch-differential): the ULTIMATE host<->machine reproducibility
# invariant — not yet implemented (heavy: needs a riscv64 build AND running it
# under QEMU user emulation, then a byte-for-byte image diff).
#
# tests/host_real_records_layout.rs already pins the 32-byte record layout and
# asserts host determinism, and riscv-link-check proves the riscv64 archive
# builds. The missing piece is proving the two backends emit the SAME records
# image for the SAME credits, byte for byte — the property that makes off-chain
# host prediction of the on-chain ledger sound.
#
# Intended shape of the job:
# cross-arch-differential:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v4
# with: { submodules: recursive }
# - uses: dtolnay/rust-toolchain@stable
# - uses: docker/setup-qemu-action@v3 # register binfmt for riscv64 user emu
# - name: Install host + riscv64 GCC 14 toolchains, libclang, qemu-user
# run: |
# sudo apt-get update
# sudo apt-get install -y \
# g++-14 gcc-14 g++-14-riscv64-linux-gnu gcc-14-riscv64-linux-gnu \
# build-essential clang libclang-dev wget make cmake \
# qemu-user qemu-user-static libc6-riscv64-cross libstdc++6-riscv64-cross
# # 1. Build a tiny harness that credits a FIXED (address, balance) set into a
# # buffer-backed single-asset ledger and writes the 128 KiB records prefix
# # to a file. Build it twice from the same source:
# # a) host-real -> native x86_64 binary -> host.records
# # b) riscv64 -> riscv64 binary, run via `qemu-riscv64` -> riscv.records
# # (The riscv64 crate currently builds a static archive; a small #[no_mangle]
# # entrypoint or an integration test cross-compiled to riscv64-unknown-linux-gnu
# # and executed under qemu is the mechanism to add.)
# # 2. Assert byte-for-byte equality:
# # cmp host.records riscv.records
# # A mismatch means the host can NOT soundly predict the machine ledger and
# # must fail CI hard.
# ===========================================================================
68 changes: 68 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Changelog

All notable changes to this project are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

While the crate is pre-1.0 (`0.0.x`), any release may contain breaking changes.

## [Unreleased]

### Added
- **Single-asset ledger API.** New bindings and `Ledger` wrapper support for the
single-asset `cma` ledger.
- **`host-real` feature.** Builds and links the real C++ `libcma` for the host
(x86_64) instead of the mock, for off-chain use (e.g. a sequencer predicting
the machine's ledger). Complements the existing `riscv64` cross-build path.
- Packaging metadata for crates.io / docs.rs: `LICENSE` (MIT), `rust-version`
(MSRV `1.74`), a `documentation` link, and `[package.metadata.docs.rs]` (an
offline `mock`-only docs build). Added `CHANGELOG.md`, `CONTRIBUTING.md`,
`SECURITY.md`, `deny.toml`, `rust-toolchain.toml`, and `rustfmt.toml`.

### Changed
- **Vendored `libcma` bumped to the uint96 single-asset format (drive format v2;
machine-asset-tools `e4bfc24`).** The single-asset drive record widened its
balance from `uint64` to `uint96`, consuming the former 4-byte pad: the 32-byte
record is now `balance_lo (u64 LE) | balance_hi (u32 LE) | owner (20B)`, with the
owner moved from offset 8 to **offset 12**. Total supply and virtual (internal
account-id) balances widened to full 256-bit. The public C API (and therefore the
Rust wrapper surface) is UNCHANGED — deposits/withdrawals/balances already used
256-bit `cma_amount_t` at the boundary; only code that parses the raw 32-byte
records must adopt the new offsets. **The on-drive format is not backward
compatible** (`MemoryFooter::VERSION` 1 → 2): a v1 drive would be silently
misread. Downstream that reads the records image directly (e.g. a sequencer's
`create_dump` / snapshot parser and the emergency-withdrawal output builder) MUST
be updated to the offset-12 owner and uint96 balance.
- **BREAKING: reshaped the ledger API around a single-asset ledger.** Removed
`LedgerMemoryMode` and reshaped `LedgerFileConfig`. Code that constructed a
ledger via the old memory-mode / file-config shape must be updated.
- **BREAKING: renamed the default mock feature `native` → `mock`.** The default
backend is now `mock`. Update any `--features native` usage accordingly. The
three mutually-exclusive backends are now `mock` (default), `host-real`, and
`riscv64`.

### Fixed
- **Relocation safety for `cma_ledger_t`.** The self-referential C++ ledger is
now boxed so it is not moved after construction, preventing dangling internal
self-pointers.

### Security / hardening
- Mutual-exclusion guards: enabling more than one backend feature now fails at
compile time (`compile_error!`) instead of silently letting the mock win in a
real build.
- `build.rs` verifies a checksum of the vendored/built libcma source.

### Changed (breaking)
- **Migrated off the EOL `ethers-rs` onto `alloy`** (`alloy-primitives` +
`alloy-dyn-abi`). The public `Address` / `U256` types are now
`alloy_primitives::{Address, U256}` — a breaking change for downstream code
that used the ethers-typed API (hence the `0.0.1` → `0.1.0` bump). The ABI
parser (`parser.rs`) was reimplemented over `alloy-dyn-abi`; byte-for-byte
equivalence is pinned by the existing parser test vectors (all pass).

### Known issues / tech debt
- **`json` (0.12)** — largely unmaintained (RUSTSEC-2022-0081). Planned
migration to `serde_json`. Surfaced by `deny.toml`.

[Unreleased]: https://github.com/Mugen-Builders/libcma_binding_rust/commits/main
78 changes: 78 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Contributing

Thanks for your interest in improving `libcma_binding_rust`.

## Prerequisites

Clone with submodules — the C headers live under `third_party/`:

```bash
git clone --recurse-submodules https://github.com/Mugen-Builders/libcma_binding_rust
# or, if already cloned:
git submodule update --init --recursive
```

(`build.rs` will auto-init the submodules if they are missing, but doing it
yourself is more predictable.)

## Building

The default build uses the pure-Rust **`mock`** backend — no network or C++
toolchain required:

```bash
cargo build
```

A **real** libcma build links the compiled C++ library instead of the mock:

```bash
# host (x86_64), off-chain use:
cargo build --no-default-features --features host-real

# Cartesi machine target (riscv64):
cargo build --no-default-features --features riscv64
```

Real builds additionally require **g++ ≥ 14**, GNU `make`, and **network access**
(`build.rs` fetches / compiles the archive from source). For `riscv64` you also
need the RISC-V GCC 14 cross toolchain (`g++-14-riscv64-linux-gnu`).

### Feature rule: exactly one backend

`mock`, `host-real`, and `riscv64` are **mutually exclusive** — exactly one must
be enabled, and a `compile_error!` guard enforces it. Because `mock` is a default
feature, selecting a real backend means also disabling defaults:

```bash
cargo build --no-default-features --features host-real
```

Enabling a real backend without `--no-default-features` leaves `mock` on, which
the guard rejects.

## Testing

```bash
cargo test
```

Tests run against the `mock` backend by default.

## Formatting and linting

Before opening a pull request:

```bash
cargo fmt --all
cargo clippy --all-targets
```

The repo pins `stable` via `rust-toolchain.toml` and ships a `rustfmt.toml`, so
formatting stays consistent across contributors.

## Pull requests

- Keep changes focused, and call out breaking changes clearly (the crate is
pre-1.0, so breaking changes are allowed but should be documented).
- Update `CHANGELOG.md` under the `## [Unreleased]` section.
Loading
Loading