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
30 changes: 20 additions & 10 deletions .github/workflows/dusk_ci.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
on: [pull_request]
on:
pull_request:
push:
branches:
- main

name: Continuous integration

jobs:
code_analysis:
name: Code Analysis
uses: dusk-network/.github/.github/workflows/code-analysis.yml@main

dusk_analysis:
name: Dusk Analyzer
uses: dusk-network/.github/.github/workflows/dusk-analysis.yml@main
code-quality:
name: Code quality
uses: dusk-network/.github/.github/workflows/run-make.yml@main
with:
make_target: cq

test:
name: Tests
uses: dusk-network/.github/.github/workflows/run-tests.yml@main
name: Run tests
uses: dusk-network/.github/.github/workflows/run-make.yml@main
with:
make_target: test

no-std:
name: Run no-std tests
uses: dusk-network/.github/.github/workflows/run-make.yml@main
with:
make_target: no-std
90 changes: 90 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# dusk-bytes

Serialization traits using const generics for fixed-size types. Workspace with two `no_std` crates: core serialization traits and a companion proc-macro for hex formatting. This is a foundational dependency for most Dusk repos — changes here ripple widely.

## Repository Map

```
bytes/
├── dusk-bytes/ # dusk-bytes — Serializable<N>, DeserializableSlice, Read/Write, hex parsing
│ ├── src/
│ │ ├── lib.rs # Re-exports, crate attributes
│ │ ├── serialize.rs # Serializable<N>, DeserializableSlice, Read, Write traits
│ │ ├── parse.rs # ParseHexStr trait, const hex() function
│ │ ├── errors.rs # Error enum, BadLength/InvalidChar traits
│ │ └── primitive.rs # Serializable impls for integer primitives (u8..u128, i8..i128)
│ └── tests/
├── derive-hex/ # derive-hex — #[derive(Hex)] and #[derive(HexDebug)] proc macros
│ ├── src/
│ │ └── lib.rs # LowerHex, UpperHex, Debug derive implementations
│ └── tests/
├── Makefile # Build targets (run `make help`)
└── rustfmt.toml
```

## Commands

Run `make help` to see all available targets.

## Architecture

### Core Traits (`dusk-bytes`)

- **`Serializable<const N: usize>`** — defines `from_bytes(&[u8; N])` and `to_bytes() -> [u8; N]` for fixed-size serialization. The const generic `N` is the wire size.
- **`DeserializableSlice<N>`** — auto-implemented for all `Serializable<N>` types. Adds `from_slice(&[u8])` (with length check) and `from_reader<R: Read>()` (streaming).
- **`Read` / `Write`** — minimal byte-oriented IO traits (not `std::io`). `Read` is implemented for `&[u8]` (advances the slice), `Write` for `&mut [u8]`.
- **`ParseHexStr<N>`** — auto-implemented for all `Serializable<N>` types. Parses hex strings into the target type.
- **`hex::<N, M>()`** — const function for compile-time hex-to-bytes conversion.

### Proc Macros (`derive-hex`)

- **`#[derive(Hex)]`** — generates `LowerHex` and `UpperHex` implementations using the type's `to_bytes()` method.
- **`#[derive(HexDebug)]`** — generates `Hex` plus a `Debug` implementation that delegates to hex formatting.

### Key Design Points

- All serialization is little-endian (see primitive impls).
- `derive-hex` is a proc-macro crate — it cannot be built for `no_std` targets like `thumbv6m-none-eabi`. The `no-std` Makefile target only builds `dusk-bytes`.
- No feature flags — both crates expose their full API unconditionally.

## Conventions

- **`no_std`**: Both crates. Do not add `std` dependencies.
- **Edition 2024**: MSRV 1.85.
- **Wide downstream impact**: This crate is a dependency of most Dusk repos. Check `Cargo.lock` in downstream repos before releasing. See the Change Propagation table below.

## Change Propagation

| Changed | Also verify |
|---------|-------------|
| `dusk-bytes` or `derive-hex` | Most repos — check `Cargo.lock` for users. Key dependents: `bls12_381`, `jubjub`, `phoenix`, `safe`, `Poseidon252`, `merkle`, `rusk` |

## Git Conventions

- Default branch: `main`
- License: MPL-2.0

### Commit messages

Format: `<scope>: <Description>` — imperative mood, capitalize first word after colon.

**One commit per crate per concern.** Each commit touches exactly one crate and one logical concern. Never bundle changes to different crates in one commit, and don't mix unrelated changes within the same crate either.

Canonical scopes:

| Scope | Crate/Directory |
|-------|----------------|
| `dusk-bytes` | `dusk-bytes/` |
| `derive-hex` | `derive-hex/` |
| `workspace` | Root `Cargo.toml`, root Makefile |
| `ci` | `.github/workflows/` |
| `chore` | Makefile, rustfmt, etc. |

Examples:
- `dusk-bytes: Add Read impl for Vec<u8>`
- `derive-hex: Fix category slugs`
- `workspace: Update edition to 2024`

### Changelog

`derive-hex` has a `CHANGELOG.md`. Add entries under `[Unreleased]` using [keep-a-changelog](https://keepachangelog.com/) format. If a change traces to a GitHub issue, reference it as a link: `[#42](https://github.com/dusk-network/dusk-bytes/issues/42)`. Only link to GitHub issues — do not reference any other tracking system.
1 change: 1 addition & 0 deletions CLAUDE.md
32 changes: 32 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
help: ## Display this help screen
@grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

fmt: ## Format code (requires nightly)
@rustup component add --toolchain nightly rustfmt 2>/dev/null || true
@cargo +nightly fmt --all $(if $(CHECK),-- --check,)

clippy: ## Run clippy
@cargo clippy --all-features -- -D warnings

cq: ## Run code quality checks (formatting + clippy)
@$(MAKE) fmt CHECK=1
@$(MAKE) clippy

check: ## Type-check
@cargo check --all-features

test: ## Run tests
@cargo test --release

no-std: ## Verify no_std compatibility on bare-metal target
@rustup target add thumbv6m-none-eabi 2>/dev/null || true
@cargo build --no-default-features -p dusk-bytes --target thumbv6m-none-eabi

doc: ## Generate docs
@cargo doc --no-deps

clean: ## Clean build artifacts
@cargo clean

.PHONY: help fmt clippy cq check test no-std doc clean
4 changes: 1 addition & 3 deletions dusk-bytes/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
//
// Copyright (c) DUSK NETWORK. All rights reserved.

use dusk_bytes::{BadLength, InvalidChar, Serializable};

use dusk_bytes::HexDebug;
use dusk_bytes::{BadLength, HexDebug, InvalidChar, Serializable};
#[derive(HexDebug)]
pub struct Beef {}

Expand Down
1 change: 0 additions & 1 deletion dusk-bytes/tests/parse_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

mod common;
use common::{Beef, BeefError};

use dusk_bytes::ParseHexStr;

#[test]
Expand Down
4 changes: 2 additions & 2 deletions dusk-bytes/tests/serialize_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
// Copyright (c) DUSK NETWORK. All rights reserved.

mod common;
use common::{Beef, BeefError};
use std::fmt::Debug;

use common::{Beef, BeefError};
use dusk_bytes::{DeserializableSlice, Error, Serializable};
use std::fmt::Debug;

#[test]
fn expected_size() {
Expand Down
5 changes: 5 additions & 0 deletions rustfmt.toml
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
unstable_features = true
group_imports = "StdExternalCrate"
imports_granularity = "Module"
use_field_init_shorthand = true
wrap_comments = true
max_width = 80