2025-10-02 17:59:57 +02:00
# Contributor's Guide
2026-05-14 15:03:27 +02:00
## Development Setup
### Prerequisites
2026-05-27 16:17:19 +02:00
#### Sys development
2026-05-27 16:41:00 +02:00
- [Rust ](https://rustup.rs/ ) — the pinned toolchain version is in `rust-toolchain.toml` and will be installed
automatically by `rustup` .
- Compiled circuit libraries (`.a` files and `witness_generator.dat` ) — see [rust/README.md ](rust/README.md ) for how to
provide them.
2026-05-27 16:17:19 +02:00
#### Building circuits
2026-05-27 16:41:00 +02:00
- `llvm-objcopy` — required for symbol isolation when building circuit static libraries. On macOS, install via `brew
install llvm` (LLVM 20+ required).
2026-05-27 16:17:19 +02:00
### Pre-Commit
2026-05-27 16:41:00 +02:00
[pre-commit ](https://pre-commit.com/ ) covers most of the lints required by CI. It's not mandatory — you can run checks
however you like — but it's the easiest way to catch issues before pushing.
2026-05-14 15:03:27 +02:00
2026-05-27 16:17:19 +02:00
#### Installation
2026-05-14 15:03:27 +02:00
```bash
pre-commit install
```
2026-05-27 16:17:19 +02:00
After this, they will be run automatically on every commit.
2026-05-14 15:03:27 +02:00
2026-05-27 16:17:19 +02:00
#### Running Manually
2026-05-14 15:03:27 +02:00
2026-05-27 16:17:19 +02:00
To run the checks manually against all files:
2026-05-14 15:03:27 +02:00
```bash
pre-commit run --all-files
```
### Maintenance
#### Rust Toolchain
2026-05-27 16:17:19 +02:00
When bumping the stable toolchain, update `channel` in `rust-toolchain.toml` .
The comment there lists every other place that must be updated in sync (nightly version, CI workflows, pre-commit hooks).
2026-05-14 15:03:27 +02:00
#### Tool Versions
`taplo` , `cargo-deny` , and `cargo-machete` are pinned in two places that must stay in sync:
2026-05-27 16:41:00 +02:00
2026-05-14 15:03:27 +02:00
- `.pre-commit-config.yaml` (hook `rev` )
- `.github/workflows/lint.yml` (`cargo install --version` )
---
2026-05-27 16:17:19 +02:00
## Building
2026-05-19 14:48:58 +02:00
2026-05-27 16:17:19 +02:00
For a full walkthrough of the CI build steps, from `.circom` source to release artifacts, see
[docs/build-pipeline.md ](docs/build-pipeline.md ).
2026-05-19 14:48:58 +02:00
2026-05-27 16:17:19 +02:00
### Symbol Isolation in Circuit Libraries
2026-05-19 14:48:58 +02:00
2026-05-27 16:17:19 +02:00
#### The Problem
Each circuit (PoQ, PoL, PoC, Signature) is compiled into a static archive (`libpoq.a` , `libpol.a` , etc.).
All archives share the same symbols, compiled from the same source files but with **different
constant values per circuit** (e.g. `get_size_of_witness()` returns 18149 for PoQ and 20531 for PoL).
2026-05-27 16:41:00 +02:00
When two or more circuit libraries are linked into the same binary, the linker silently picks the first definition it
2026-05-27 16:17:19 +02:00
encounters for each symbol and discards the rest without any sort of error or warning.
2026-05-27 16:41:00 +02:00
The result is that one circuit's constants end up hardwired into functions shared by both circuits, corrupting witness
parsing.
In practice: the wrong `get_size_of_witness()` value causes `loadCircuit` to compute an incorrect buffer size, `pu32`
2026-05-19 14:48:58 +02:00
walks off the end of the buffer, reads garbage as a length field, and the subsequent `memcpy` reads past the stack guard
page, which results in a **SIGSEGV** .
2026-05-27 16:17:19 +02:00
#### The Fix
2026-05-19 14:48:58 +02:00
2026-05-27 16:17:19 +02:00
The Makefile uses a two-step process to hide all circuit-specific symbols before archiving:
2026-05-19 14:48:58 +02:00
2026-05-27 16:17:19 +02:00
1. **Partial link** (`ld -r` ): merges all circuit-specific `.o` files into a single relocatable
object. `fr.o` is excluded — it contains only field arithmetic with no circuit-specific calls
and is safe to deduplicate across circuits.
2. **Symbol localization** : demotes every global symbol to local except the two public FFI entry
points (`$(PROJECT)_generate_witness` and `$(PROJECT)_generate_witness_from_files` ). Local
symbols are invisible to the final linker, so each archive retains a private copy.
2026-05-19 14:48:58 +02:00
2026-05-27 16:17:19 +02:00
**`llvm-objcopy` vs GNU `objcopy` **
2026-05-19 14:48:58 +02:00
2026-05-27 16:17:19 +02:00
`llvm-objcopy` is required on Linux. GNU `objcopy` only changes the binding of COMDAT signature
symbols to local, confusing the linker's deduplication logic and causing "relocation refers to
symbol in discarded section" errors. `llvm-objcopy` additionally clears the `GRP_COMDAT` flag,
turning affected sections into regular non-COMDAT sections. Slightly larger binary, no linker errors.
2026-05-19 14:48:58 +02:00
2026-05-27 16:41:00 +02:00
##### macOS
2026-05-19 14:48:58 +02:00
2026-05-27 16:17:19 +02:00
Uses `llvm-objcopy` (from `brew install llvm` , LLVM 20+).
Mach-O prepends `_` to every C symbol, so `--keep-global-symbol` arguments must include the
leading `_` . The Makefile's `SYM_PREFIX` variable handles this automatically.
2026-05-27 16:41:00 +02:00
##### Windows
2026-05-27 16:17:19 +02:00
Uses GNU's `objcopy` (from MinGW binutils).
GNU's `objcopy` works correctly on COFF, mapping local binding to storage class `C_STAT` .
The ELF `GRP_COMDAT` problem doesn't apply: COFF COMDAT is per-section rather than group-based.
2026-05-27 16:41:00 +02:00
#### FFI Maintenance
2026-05-19 14:48:58 +02:00
`PUBLIC_SYMS` is hardcoded to `$(PROJECT)_generate_witness` and `$(PROJECT)_generate_witness_from_files` in the
2026-05-27 16:41:00 +02:00
Makefile. If the public FFI API ever changes — entry points renamed or new ones added — update that variable, otherwise
2026-05-27 16:17:19 +02:00
the affected symbols will be localized and linking will fail.
2026-05-19 14:48:58 +02:00
---
2026-05-27 16:17:19 +02:00
## Releasing
### Triggering a Release Build
2025-10-02 17:59:57 +02:00
To trigger a release build:
2026-05-27 16:17:19 +02:00
1. Create and push a tag in the format `vX.Y.Z` :
2026-05-27 16:41:00 +02:00
2026-05-27 16:17:19 +02:00
```bash
git tag v1.2.3 -m "Release v1.2.3"
git push --tags
```
2026-05-27 16:41:00 +02:00
2025-10-02 17:59:57 +02:00
2. This will automatically trigger the `.github/workflows/build_circuits.yml` workflow.
3. Once the workflow finishes, the generated artifacts will be attached to a new release.
2026-05-27 16:41:00 +02:00
> Pull Requests will also generate artifacts, which may be found on the job's page, but won't generate a new release.
2025-10-02 17:59:57 +02:00
2026-05-27 16:17:19 +02:00
#### Generated artifacts
2025-10-30 15:22:19 +04:00
2026-05-27 16:41:00 +02:00
For each supported platform (Linux x86_64, Linux aarch64, macOS aarch64, Windows x86_64), a release artifact is
generated:
2025-10-30 15:22:19 +04:00
2026-05-27 16:41:00 +02:00
**`logos-blockchain-circuits-{version}-{os}-{arch}.tar.gz` ** — a complete bundle containing all components needed to
generate and verify proofs for all circuits.
2025-10-30 15:22:19 +04:00
2025-11-03 16:47:52 +04:00
**Bundle Structure:**
2025-10-30 15:22:19 +04:00
2026-05-27 16:41:00 +02:00
```text
2026-01-19 14:54:26 +01:00
logos-blockchain-circuits-{version}-{os}-{arch}/
2026-05-27 16:17:19 +02:00
├── lib/
│ └── libgmp.a
├── {circuit}/ (poc/, pol/, poq/, signature/)
2026-05-14 15:03:27 +02:00
│ ├── include/
2026-05-27 16:17:19 +02:00
│ ├── lib{circuit}.a
2025-11-03 16:47:52 +04:00
│ ├── proving_key.zkey
2026-05-27 16:17:19 +02:00
│ ├── verification_key.json
│ └── witness_generator.dat
├── prover[.exe]
├── verifier[.exe]
└── VERSION
2025-11-03 16:47:52 +04:00
```
2025-10-30 15:22:19 +04:00
2026-05-27 16:17:19 +02:00
> On Windows, static libraries use the `.lib` extension instead of `.a` (e.g. `pol.lib`, `gmp.lib`).
2025-11-04 15:21:55 +04:00
2026-05-27 16:17:19 +02:00
The proving keys are generated using the Hermez Powers of Tau ceremony — see [docs/build-pipeline.md § Step 2 ](docs/build-pipeline.md#step-2--proving-key-generation ).
2025-10-30 15:22:19 +04:00
2026-05-27 16:17:19 +02:00
### Publishing
2025-10-02 17:59:57 +02:00
2026-05-27 16:41:00 +02:00
Releases are marked as **Draft** and **Pre-Release** to ensure the changelog and pre-release steps are manually reviewed
before going public. Before publishing:
2025-10-02 17:59:57 +02:00
1. **Review the changelog**
Ensure that all relevant changes are clearly listed and properly formatted.
2. **Confirm the pre-release checklist**
Verify that all required steps have been completed, then remove the checklist from the release notes.
3. **Mark the release as published**
- Uncheck ** “This is a pre-release.”**
- Publish the release (removing the Draft state).