Skip to content
Closed
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
13 changes: 12 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ endef

# Fast bootstrap.
fast: release-image barretenberg boxes playground docs aztec-up \
bb-tests l1-contracts-tests yarn-project-tests boxes-tests playground-tests aztec-up-tests docs-tests noir-protocol-circuits-tests release-image-tests spartan claude-tests
bb-tests l1-contracts-tests yarn-project-tests boxes-tests playground-tests aztec-up-tests docs-tests noir-protocol-circuits-tests release-image-tests spartan claude-tests ipc-codegen-tests

# Full bootstrap.
full: fast bb-full-tests bb-cpp-full yarn-project-benches
Expand Down Expand Up @@ -269,6 +269,17 @@ bb-tests: bb-cpp-native-tests bb-acir-tests bb-ts-tests bb-sol-tests bb-bbup-tes

bb-full-tests: bb-cpp-wasm-threads-tests bb-cpp-asan-tests bb-cpp-smt-tests

#==============================================================================
# IPC Codegen
#==============================================================================

.PHONY: ipc-codegen ipc-codegen-tests
ipc-codegen:
$(call build,$@,ipc-codegen)

ipc-codegen-tests: ipc-codegen
$(call test,$@,ipc-codegen)

#==============================================================================
# .claude tooling
#==============================================================================
Expand Down
5 changes: 3 additions & 2 deletions barretenberg/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ cmake-build-debug
*_opt.pil
bench-out

# Generated code from msgpack schema (run `yarn generate` in ts/)
# Generated code (produced by ipc-codegen, not committed)
**/generated/
# Legacy generated paths from the cbind pipeline (kept for any leftover from migration)
rust/barretenberg-rs/src/generated_types.rs
rust/barretenberg-rs/src/api.rs
ts/src/cbind/generated/
11 changes: 11 additions & 0 deletions ipc-codegen/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Local build artifacts produced by the echo wire-compat examples.
examples/cpp/*/echo_client
examples/cpp/*/echo_server
examples/rust/*/target/
examples/zig/*/.zig-cache/
examples/zig/*/zig-out/
examples/ts/*/node_modules/

# All files under any generated/ directory are produced by bootstrap.sh build
# (either codegen output or one-time-copied templates) and are never committed.
**/generated/
5 changes: 5 additions & 0 deletions ipc-codegen/.rebuild_patterns
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
^ipc-codegen/src/.*\.ts$
^ipc-codegen/templates/
^ipc-codegen/examples/
^ipc-codegen/package\.json$
^ipc-codegen/bootstrap\.sh$
166 changes: 166 additions & 0 deletions ipc-codegen/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# ipc-codegen

Schema-driven IPC code generator for **C++**, **TypeScript**, **Rust**, and **Zig**.

Given a JSON schema describing a service's commands and responses, emits matching
type definitions plus a client and/or server in the target language. All wire I/O
is msgpack-over-UDS; languages talk to each other byte-compatibly.

## Quick start

```sh
cd ipc-codegen
./bootstrap.sh build # generate echo example bindings, compile all 4 languages
./bootstrap.sh test # run the 18-test cross-language wire-compat matrix
```

## Layout

```
ipc-codegen/
bootstrap.sh # build / test / update_goldens / hash
src/ # generator (TypeScript, runs under Node 22+)
generate.ts # CLI entry point
schema_visitor.ts # JSON schema -> CompiledSchema IR
cpp_codegen.ts # IR -> C++ output
typescript_codegen.ts # IR -> TypeScript output
rust_codegen.ts # IR -> Rust output
zig_codegen.ts # IR -> Zig output
naming.ts # snake_case / PascalCase helpers
templates/ # static templates copied alongside generated code
cpp/{ipc_client,ipc_server,msgpack_struct_map_impl}.hpp
ts/{ipc_client,ipc_server}.ts
rust/{backend,error,ipc_client,ipc_server,uds_backend,ffi_backend}.rs
zig/{backend,uds_backend,ffi_backend,ipc_client,ipc_server,ffi_client}.zig
examples/ # 4-language echo service (test harness, see below)
SCHEMA_SPEC.md # wire protocol and schema-format reference
```

The package contains no service schemas of its own. Each consumer owns and commits its schema next to the C++ server that defines the wire format, and invokes `generate.ts` with that local path.

## CLI: `src/generate.ts`

Invoked once per (schema, language) pair. Run directly with `node --experimental-strip-types`, or via `bootstrap.sh`.

```
node --experimental-strip-types --experimental-transform-types --no-warnings \
src/generate.ts --schema <file> --lang <ts|cpp|rust|zig> --out <dir> [flags]
```

### Required flags

| Flag | Purpose |
|---|---|
| `--schema <file>` | Path to the JSON schema. |
| `--lang <ts\|cpp\|rust\|zig>` | Target language. |
| `--out <dir>` | Output directory. Files are (re)written every run; static templates are copied alongside (and re-copied only if missing for one-time scaffolding files). |

### Role flags

| Flag | Purpose |
|---|---|
| `--server` | Emit server dispatch (matches request name to handler, deserializes, calls handler, serializes response). |
| `--client` | Emit a typed client class/struct with one method per command. |
| `--uds` | Include the Unix-domain-socket backend (Rust/Zig). |
| `--ffi` | Include the FFI backend (Rust/Zig). |

### Naming flags

| Flag | Purpose |
|---|---|
| `--prefix <Str>` | Type prefix applied to generated type names (`<Prefix>CircuitProve`, etc.). Auto-detected from the schema if omitted. |
| `--strip-method-prefix` | TS only. Drops the prefix from client *method* names: `bbCircuitProve()` → `circuitProve()`. Types keep the prefix. |

### C++-specific flags

| Flag | Purpose |
|---|---|
| `--cpp-namespace <ns>` | C++ namespace, e.g. `bb::wsdb`. Default: lowercased prefix. |
| `--cpp-wire-namespace <ns>` | Inner namespace for wire types, default `wire`. |
| `--cpp-include-dir <path>` | Include-path prefix for cross-includes between generated files, e.g. `myservice/generated`. Leave unset when generated files are in the same directory as their consumer. |

### Other

| Flag | Purpose |
|---|---|
| `--curve-constants` | TS only. Also emit `curve_constants.ts` with bn254/grumpkin/secp moduli & generators (currently only used by bb). |
| `--skeleton <dir>` | One-shot scaffolding: writes a `<service>_handlers.{ts,rs,zig,cpp}` stub, `main`, and a build file into `<dir>` if they don't already exist. Skipped on subsequent runs. |

## Worked examples

Paths below are illustrative — consumers commit their own schema next to the C++ server that owns the wire format and supply absolute or relative paths on the command line.

### TypeScript client + server, with curve constants

```sh
src/generate.ts \
--schema /path/to/myservice_schema.json \
--lang ts \
--out /path/to/output/generated \
--server --client \
--prefix MyService --strip-method-prefix --curve-constants
```

Produces `api_types.ts`, `async.ts`, `sync.ts`, `server.ts`, `ipc_client.ts` (template), `ipc_server.ts` (template), `curve_constants.ts`.

### C++ server + client, under a project sub-include path

```sh
src/generate.ts \
--schema /path/to/myservice_schema.json \
--lang cpp \
--out /path/to/myservice/generated \
--server --client \
--cpp-namespace my::ns --prefix MyService \
--cpp-include-dir myservice/generated
```

Produces `myservice_types.hpp`, `myservice_ipc_client.{hpp,cpp}`, `myservice_ipc_server.hpp`, plus the `ipc_client.hpp` / `ipc_server.hpp` / `msgpack_struct_map_impl.hpp` templates. Cross-includes use the supplied `--cpp-include-dir` prefix (`#include "myservice/generated/myservice_types.hpp"`).

### Rust UDS + FFI client

```sh
src/generate.ts \
--schema /path/to/myservice_schema.json \
--lang rust \
--out /path/to/crate/src/generated \
--server --client --uds --ffi \
--prefix MyService \
--skeleton /path/to/crate/src
```

Produces `myservice_types.rs`, `myservice_client.rs`, `myservice_server.rs`, `ipc_server.rs` (template), plus the backend templates (`backend.rs`, `error.rs`, `uds_backend.rs`, `ffi_backend.rs`). The skeleton flag also writes a one-time `myservice_handlers.rs`, `main.rs`, and `Cargo.toml` into the skeleton dir so a new service crate is buildable on first run.

### Zig client + server

```sh
src/generate.ts \
--schema /path/to/myservice_schema.json \
--lang zig \
--out /path/to/output/generated \
--server --client --uds --ffi \
--prefix MyService
```

Produces `myservice_types.zig`, `myservice_client.zig`, `myservice_server.zig`, plus backend templates.

## Adding a new service

1. **Define the C++ command structs** in your service's `.hpp`, each with `MSGPACK_SCHEMA_NAME` and `SERIALIZATION_FIELDS(...)`. Group them into a single `Command` and `Response` `NamedUnion`.
2. **Snapshot the schema.** Build the service binary and run `<binary> msgpack schema` to dump the JSON. Commit it next to the C++ source that defines it (e.g. alongside the `Command` / `Response` headers). This is the wire-format source of truth.
3. **Wire your consumer's build to invoke `src/generate.ts`** with the flags above, passing the absolute path to the committed schema and the desired output directory. Generated files go under a `generated/` directory which is gitignored by convention.
4. **Run `./bootstrap.sh test`** in `ipc-codegen/` to confirm the codegen and cross-language wire compat tests still pass.

## Schemas are the source of truth

The JSON schema is the wire contract between client and server. Consumers commit it next to the C++ server that defines the underlying `SERIALIZATION_FIELDS`, so the file lives close to what it describes and tracks with that code. Whenever a server-side command changes, refresh the JSON snapshot by running `<binary> msgpack schema` against the rebuilt binary and committing the diff. Diverged schemas are a CI failure (each consumer is responsible for guarding its own snapshot).

Each generated file embeds a `SCHEMA_HASH` so callers can detect at connection time that their bindings predate the server.

## Wire-format contract

`examples/echo-schema/golden/*.msgpack` is a frozen set of byte-level fixtures covering every relevant msgpack encoding boundary (variable-width ints, fixstr/str8/str16, bin8/bin16, optional `Some`/`None`, empty containers, multi-byte UTF-8). The per-language golden tests (`examples/{rust,ts}/echo/...`) both decode the fixtures and re-encode round-trip — pinning down canonical msgpack output across implementations.

If you intentionally change the wire format, run `./bootstrap.sh update_goldens` and review the diff. Any byte-level change is a breaking change for external implementations of the schema.

See `SCHEMA_SPEC.md` for the wire protocol details.
Loading
Loading