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
37 changes: 29 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ jobs:
- name: Setup Zig
uses: goto-bus-stop/setup-zig@v2
with:
version: 0.15.2
version: 0.14.1

- name: Fetch dependencies
run: |
zig build --fetch

- name: Run linting checks
run: |
Expand All @@ -40,11 +44,16 @@ jobs:
- name: Setup Zig
uses: goto-bus-stop/setup-zig@v2
with:
version: 0.15.2
version: 0.14.1

- name: Fetch dependencies
run: |
zig build --fetch

- name: Build for Linux x86_64
run: |
zig build -Dtarget=x86_64-linux-gnu
# Specify glibc 2.38+ for RocksDB compatibility (requires __isoc23_* symbols)
zig build -Dtarget=x86_64-linux-gnu.2.38

- name: Verify binary exists
run: |
Expand All @@ -62,7 +71,11 @@ jobs:
- name: Setup Zig
uses: goto-bus-stop/setup-zig@v2
with:
version: 0.15.2
version: 0.14.1

- name: Fetch dependencies
run: |
zig build --fetch

- name: Build for macOS x86_64
run: |
Expand All @@ -84,7 +97,11 @@ jobs:
- name: Setup Zig
uses: goto-bus-stop/setup-zig@v2
with:
version: 0.15.2
version: 0.14.1

- name: Fetch dependencies
run: |
zig build --fetch

- name: Build for macOS ARM64
run: |
Expand All @@ -106,7 +123,11 @@ jobs:
- name: Setup Zig
uses: goto-bus-stop/setup-zig@v2
with:
version: 0.15.2
version: 0.14.1

- name: Fetch dependencies
run: |
zig build --fetch

- name: Build for Windows x86_64
run: |
Expand All @@ -124,8 +145,8 @@ jobs:
include:
- platform: linux/amd64
tag: amd64
- platform: linux/arm64
tag: arm64
# - platform: linux/arm64
# tag: arm64

steps:
- name: Checkout code
Expand Down
11 changes: 8 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ RUN apt-get update && apt-get install -y \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*

# Install Zig 0.15.2
# Install Zig 0.14.1
# Detect architecture and download appropriate Zig binary
ARG TARGETPLATFORM
ARG BUILDPLATFORM
ENV ZIG_VERSION=0.15.2
ENV ZIG_VERSION=0.14.1
RUN ARCH_SUFFIX=$(echo ${TARGETPLATFORM} | cut -d'/' -f2) && \
if [ "${ARCH_SUFFIX}" = "amd64" ]; then \
ZIG_ARCH="x86_64"; \
Expand All @@ -40,8 +40,13 @@ COPY build.zig build.zig.zon ./
COPY src ./src
COPY vendor ./vendor

# Fetch dependencies
RUN --mount=type=cache,target=/root/.cache/zig \
zig build --fetch

# Build the sequencer
RUN zig build -Doptimize=ReleaseSafe
RUN --mount=type=cache,target=/root/.cache/zig \
zig build -Doptimize=ReleaseSafe

# Stage 2: Runtime stage
FROM ubuntu:22.04
Expand Down
49 changes: 32 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Native Sequencer

A production-grade sequencer built in Zig for L2 rollups that accepts transactions, orders them, forms batches, and posts them to L1.
**⚠️ EXPERIMENTAL SOFTWARE - USE AT YOUR OWN RISK ⚠️**

This is experimental software and is provided "as is" without warranty of any kind. Use at your own risk. The software may contain bugs, security vulnerabilities, or other issues that could result in loss of funds or data.

A sequencer built in Zig for L2 rollups that accepts transactions, orders them, forms batches, and posts them to L1.

## Overview

Expand All @@ -12,7 +16,7 @@ The Native Sequencer is a high-performance transaction sequencer designed for La
- **Excellent C interop** - reuse battle-tested C libraries (RocksDB, libsecp256k1, etc.)
- **Strong control over memory layout** - enables zero-copy network stacks and deterministic serialization
- **Modern tooling** - easy cross-compilation for Linux amd64/arm64 containers
- **Production-ready** - built with Zig 0.15.2 for stability and performance
- **Built with Zig 0.14.1** for stability and performance

## Features

Expand Down Expand Up @@ -70,7 +74,7 @@ The sequencer follows a modular architecture:

### Prerequisites

- **Zig 0.15.2** or later ([Install Zig](https://ziglang.org/download/))
- **Zig 0.14.1** ([Install Zig](https://ziglang.org/download/))
- **C compiler** (for vendored C dependencies)

### Build Commands
Expand Down Expand Up @@ -127,7 +131,7 @@ docker rm sequencer

The Dockerfile uses a multi-stage build:

1. **Builder Stage**: Installs Zig 0.15.2 and builds the sequencer
1. **Builder Stage**: Installs Zig 0.14.1 and builds the sequencer
2. **Runtime Stage**: Creates a minimal runtime image with just the binary

#### Runtime Environment Variables
Expand All @@ -141,7 +145,7 @@ The container accepts the following environment variables (all have defaults set
**L1 Configuration**:
- `L1_RPC_URL`: L1 JSON-RPC endpoint (default: `http://host.docker.internal:8545`)
- `L1_CHAIN_ID`: L1 chain ID (default: `1`)
- `SEQUENCER_KEY`: Sequencer private key in hex format (required for production)
- `SEQUENCER_KEY`: Sequencer private key in hex format

**Sequencer Configuration**:
- `BATCH_SIZE_LIMIT`: Maximum blocks per batch (default: `1000`)
Expand Down Expand Up @@ -232,9 +236,9 @@ docker buildx build --platform linux/amd64 -t native-sequencer:amd64 .
docker buildx build --platform linux/amd64,linux/arm64 -t native-sequencer:latest --push .
```

#### Production Deployment
#### Deployment Considerations

For production deployments, consider:
For deployments, consider:

1. **Use a specific tag** instead of `latest`
2. **Set resource limits**
Expand Down Expand Up @@ -417,15 +421,15 @@ Available metrics:

## Development Status

This is an initial implementation. Production use requires:
This is an experimental implementation. The following features are implemented or in progress:

- ✅ Core sequencer architecture
- ✅ Transaction validation and mempool
- ✅ Batch formation and L1 submission
- ✅ Basic state management
- ✅ RLP encoding/decoding (complete implementation with tests)
- ✅ Docker support
- ✅ HTTP server implementation (Zig 0.15 networking APIs)
- ✅ HTTP server implementation (Zig 0.14.1 networking APIs)
- ✅ HTTP client for L1 communication (JSON-RPC support)
- ✅ Conditional transaction submission (EIP-7796 support)
- ⏳ Complete ECDSA signature verification and recovery (basic implementation)
Expand Down Expand Up @@ -495,7 +499,7 @@ The workflow will fail if:

### Networking Implementation

The sequencer uses Zig 0.15.2's standard library networking APIs:
The sequencer uses Zig 0.14.1's standard library networking APIs:

- **HTTP Server**: Built on `std.net.Server` and `std.net.Stream` for accepting JSON-RPC connections
- **HTTP Client**: Uses `std.net.tcpConnectToAddress` for L1 RPC communication
Expand All @@ -504,7 +508,7 @@ The sequencer uses Zig 0.15.2's standard library networking APIs:

### Custom U256 Implementation

Due to a compiler bug in Zig 0.15.2's HashMap implementation with native `u256` types, we use a custom `U256` struct implementation. This struct:
Due to a compiler bug in Zig 0.14.x's HashMap implementation with native `u256` types, we use a custom `U256` struct implementation. This struct:
- Uses two `u128` fields to represent 256-bit values
- Provides conversion functions to/from native `u256` and byte arrays
- Includes custom hash and equality functions for HashMap compatibility
Expand All @@ -514,11 +518,25 @@ See `src/core/types.zig` for implementation details and rationale.

## Known Issues & Workarounds

### Zig 0.15.2 HashMap Allocator Bug (RESOLVED)
### Linux Build Requirements

**glibc Version**: The Linux build requires glibc 2.38 or later due to RocksDB dependencies that use ISO C23 compatibility symbols (`__isoc23_*`). When building for Linux, specify the glibc version:

```bash
zig build -Dtarget=x86_64-linux-gnu.2.38
```

**CI Compatibility**: GitHub Actions `ubuntu-latest` runners use Ubuntu 22.04 (glibc 2.35), which is insufficient. The CI workflow specifies glibc 2.38 in the build target to ensure compatibility. For local builds on older Linux distributions, you may need to:

1. Use a newer Linux distribution (Ubuntu 24.04+ or equivalent)
2. Build in a container with glibc 2.38+
3. Use the Docker build which includes the correct glibc version

### Zig 0.14.x HashMap Allocator Bug (RESOLVED)

**Status**: ✅ **RESOLVED** - Custom U256 implementation workaround implemented

This project encountered a compiler bug in Zig 0.15.2 related to HashMap initialization with native `u256` types as keys. The error manifests as:
This project encountered a compiler bug in Zig 0.14.x related to HashMap initialization with native `u256` types as keys. The error manifests as:
```
error: access of union field 'pointer' while field 'int' is active
at std/mem/Allocator.zig:425:45
Expand All @@ -541,11 +559,8 @@ See `src/core/types.zig` for detailed comments explaining the implementation.

### Zig 0.14.x Allocator Bug (Historical)

This project previously encountered allocator bugs in Zig 0.14.0 and 0.14.1 related to allocating arrays of structs containing slices. **Verified through testing**: The bug exists in both versions (at different line numbers: 400 vs 412). See **[ZIG_0.14_ALLOCATOR_ERROR.md](ZIG_0.14_ALLOCATOR_ERROR.md)** for detailed explanation and workarounds attempted.

### Upgrading to Zig 0.15.2
This project previously encountered allocator bugs in Zig 0.14.0 and 0.14.1 related to allocating arrays of structs containing slices. **Verified through testing**: The bug exists in both versions (at different line numbers: 400 vs 412). The issue was resolved by using a custom `U256` implementation instead of native `u256` types.

This project has been successfully upgraded to Zig 0.15.2. See **[ZIG_0.15_UPGRADE.md](ZIG_0.15_UPGRADE.md)** for detailed information about the upgrade process, encountered errors, and solutions.

## License

Expand Down
56 changes: 55 additions & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
_ = b.standardOptimizeOption(.{}); // Available for future use

// Note: For Linux builds, specify glibc 2.38+ in the target (e.g., x86_64-linux-gnu.2.38)
// This is required for RocksDB compatibility (uses __isoc23_* symbols from glibc 2.38+)

// Build libsecp256k1 static C library from vendor directory
// In Zig 0.15, we create a library with a dummy Zig root module
const libsecp256k1_root = b.addModule("secp256k1_lib", .{
.root_source_file = b.path("vendor/zig-eth-secp256k1/secp256k1_wrapper.zig"),
.target = target,
Expand Down Expand Up @@ -44,6 +46,16 @@ pub fn build(b: *std.Build) void {
});
sequencer_module.addImport("secp256k1", secp256k1_mod);

// Add RocksDB dependency (using Syndica/rocksdb-zig like zeam)
// Note: RocksDB doesn't support Windows, so we conditionally include it
const is_windows = target.result.os.tag == .windows;
if (!is_windows) {
const dep_rocksdb = b.dependency("rocksdb", .{
.target = target,
});
sequencer_module.addImport("rocksdb", dep_rocksdb.module("bindings"));
}

// Library
const lib = b.addLibrary(.{
.name = "native-sequencer",
Expand All @@ -52,6 +64,20 @@ pub fn build(b: *std.Build) void {
});
// Link secp256k1 library
lib.linkLibrary(libsecp256k1);
// Add RocksDB module and link library (only on non-Windows)
if (!is_windows) {
const dep_rocksdb = b.dependency("rocksdb", .{
.target = target,
});
lib.root_module.addImport("rocksdb", dep_rocksdb.module("bindings"));
lib.linkLibrary(dep_rocksdb.artifact("rocksdb"));
lib.linkLibCpp(); // RocksDB requires C++ standard library
lib.linkSystemLibrary("pthread"); // Required for pthread functions
// librt is Linux-specific (gettid, etc.) - not needed on macOS
if (target.result.os.tag == .linux) {
lib.linkSystemLibrary("rt");
}
}
lib.linkLibC();
b.installArtifact(lib);

Expand All @@ -68,6 +94,20 @@ pub fn build(b: *std.Build) void {
exe.root_module.addImport("secp256k1", secp256k1_mod);
// Link secp256k1 library
exe.linkLibrary(libsecp256k1);
// Add RocksDB module and link library (only on non-Windows)
if (!is_windows) {
const dep_rocksdb = b.dependency("rocksdb", .{
.target = target,
});
exe.root_module.addImport("rocksdb", dep_rocksdb.module("bindings"));
exe.linkLibrary(dep_rocksdb.artifact("rocksdb"));
exe.linkLibCpp(); // RocksDB requires C++ standard library
exe.linkSystemLibrary("pthread"); // Required for pthread functions
// librt is Linux-specific (gettid, etc.) - not needed on macOS
if (target.result.os.tag == .linux) {
exe.linkSystemLibrary("rt");
}
}
exe.linkLibC();

b.installArtifact(exe);
Expand All @@ -93,6 +133,20 @@ pub fn build(b: *std.Build) void {
unit_tests.root_module.addImport("secp256k1", secp256k1_mod);
// Link secp256k1 library
unit_tests.linkLibrary(libsecp256k1);
// Add RocksDB module and link library (only on non-Windows)
if (!is_windows) {
const dep_rocksdb = b.dependency("rocksdb", .{
.target = target,
});
unit_tests.root_module.addImport("rocksdb", dep_rocksdb.module("bindings"));
unit_tests.linkLibrary(dep_rocksdb.artifact("rocksdb"));
unit_tests.linkLibCpp(); // RocksDB requires C++ standard library
unit_tests.linkSystemLibrary("pthread"); // Required for pthread functions
// librt is Linux-specific (gettid, etc.) - not needed on macOS
if (target.result.os.tag == .linux) {
unit_tests.linkSystemLibrary("rt");
}
}
unit_tests.linkLibC();
const run_unit_tests = b.addRunArtifact(unit_tests);
const test_step = b.step("test", "Run unit tests");
Expand Down
9 changes: 7 additions & 2 deletions build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@
.name = .native_sequencer,
.version = "0.1.0",
.fingerprint = 0xf01d1595a0ff6442,
.minimum_zig_version = "0.15.2",
.dependencies = .{},
.minimum_zig_version = "0.14.1",
.dependencies = .{
.rocksdb = .{
.url = "https://github.com/Syndica/rocksdb-zig/archive/70137101ad89640e0fc2e5ddbe60a26c522c7ae7.tar.gz",
.hash = "rocksdb-9.7.4-z_CUTmO5AAD0CQ2ZvShSDZHjC2x9MKrTnpvbNAIU7ah0",
},
},
.paths = .{
"build.zig",
"build.zig.zon",
Expand Down
11 changes: 8 additions & 3 deletions src/api/http.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ pub const HttpServer = struct {
allocator: std.mem.Allocator,
address: std.net.Address,
server: ?std.net.Server = null,
host: []const u8,
port: u16,

pub fn init(allocator: std.mem.Allocator, address: std.net.Address) HttpServer {
pub fn init(allocator: std.mem.Allocator, address: std.net.Address, host: []const u8, port: u16) HttpServer {
return .{
.allocator = allocator,
.address = address,
.host = host,
.port = port,
};
}

Expand All @@ -22,7 +26,8 @@ pub const HttpServer = struct {
});
self.server = server;

std.log.info("HTTP server listening on {any}", .{self.address});
// Format address for readable logging
std.log.info("HTTP server listening on {s}:{d}", .{ self.host, self.port });
}

pub fn accept(self: *HttpServer) !Connection {
Expand Down Expand Up @@ -122,7 +127,7 @@ pub const HttpResponse = struct {
}

pub fn format(self: *const HttpResponse, allocator: std.mem.Allocator) ![]u8 {
var result = std.array_list.Managed(u8).init(allocator);
var result = std.ArrayList(u8).init(allocator);
errdefer result.deinit();

const status_text = switch (self.status_code) {
Expand Down
Loading
Loading