Skip to content

Commit 1d909de

Browse files
authored
Merge pull request #2 from ch4r10t33r/main
feat: Added KoalaBear field support to enable Rust hash-sig compatibility
2 parents bfe9cd1 + 012d531 commit 1d909de

File tree

7 files changed

+427
-18
lines changed

7 files changed

+427
-18
lines changed

README.md

Lines changed: 142 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,159 @@ A Zig implementation of the Poseidon2 cryptographic hash function.
44

55
## Supported Configurations
66

7-
Currently, this implementation provides:
7+
This implementation provides:
8+
9+
### Finite Fields
10+
11+
- **BabyBear field** (p = 2³¹ - 2²⁷ + 1 = 0x78000001)
12+
- Width: 16 elements
13+
- S-Box degree: 7
14+
- Internal rounds: 13
15+
- External rounds: 8
16+
- Use case: Ethereum Lean chain
17+
18+
- **KoalaBear field** (p = 2³¹ - 2²⁴ + 1 = 0x7f000001)
19+
- Width: 16 elements
20+
- S-Box degree: 3
21+
- Internal rounds: 20
22+
- External rounds: 8
23+
- Use case: plonky3, Rust hash-sig compatibility
24+
25+
### Features
826

9-
- BabyBear finite field with a width of 16 elements
1027
- Generic Montgomery form implementation for finite fields of 31 bits or less
11-
- Compression mode, since it's the recommended mode for Merkle Trees compared to the sponge construction.
28+
- Compression mode (recommended for Merkle Trees)
29+
- Both naive and optimized (Montgomery) implementations for verification
30+
- Comprehensive test suite ensuring consistency between implementations
1231

13-
The generic implementation makes it straightforward to add support for additional 31-bit fields.
32+
## Installation
1433

15-
## Project Motivation
34+
Add `zig-poseidon` as a dependency in your `build.zig.zon`:
35+
36+
```zig
37+
.dependencies = .{
38+
.poseidon = .{
39+
.url = "https://github.com/jsign/zig-poseidon/archive/<commit-hash>.tar.gz",
40+
.hash = "<hash>",
41+
},
42+
},
43+
```
44+
45+
## Usage
46+
47+
### Using BabyBear16
48+
49+
```zig
50+
const std = @import("std");
51+
const babybear16 = @import("babybear16");
52+
53+
pub fn main() !void {
54+
const Field = babybear16.Poseidon2BabyBear.Field;
55+
56+
// Prepare input state (16 field elements)
57+
var input_state: [16]u32 = .{0} ** 16;
58+
input_state[0] = 42;
59+
60+
// Convert to Montgomery form
61+
var mont_state: [16]Field.MontFieldElem = undefined;
62+
for (0..16) |i| {
63+
Field.toMontgomery(&mont_state[i], input_state[i]);
64+
}
65+
66+
// Apply permutation
67+
babybear16.Poseidon2BabyBear.permutation(&mont_state);
68+
69+
// Convert back to normal form
70+
var output_state: [16]u32 = undefined;
71+
for (0..16) |i| {
72+
output_state[i] = Field.toNormal(mont_state[i]);
73+
}
74+
75+
std.debug.print("Output: {any}\n", .{output_state});
76+
}
77+
```
78+
79+
### Using KoalaBear16 (Rust hash-sig compatible)
80+
81+
```zig
82+
const std = @import("std");
83+
const koalabear16 = @import("koalabear16");
1684
17-
This repository was created primarily to support the upcoming Ethereum Beam chain. The implementation will be updated to match the required configuration once the specifications are finalized.
85+
pub fn main() !void {
86+
const Field = koalabear16.Poseidon2KoalaBear.Field;
87+
88+
// Prepare input state (16 field elements)
89+
var input_state: [16]u32 = .{0} ** 16;
90+
input_state[0] = 42;
91+
92+
// Convert to Montgomery form
93+
var mont_state: [16]Field.MontFieldElem = undefined;
94+
for (0..16) |i| {
95+
Field.toMontgomery(&mont_state[i], input_state[i]);
96+
}
97+
98+
// Apply permutation
99+
koalabear16.Poseidon2KoalaBear.permutation(&mont_state);
100+
101+
// Convert back to normal form
102+
var output_state: [16]u32 = undefined;
103+
for (0..16) |i| {
104+
output_state[i] = Field.toNormal(mont_state[i]);
105+
}
106+
107+
std.debug.print("Output: {any}\n", .{output_state});
108+
}
109+
```
18110

19-
With time this repository can keep expaning on features:
111+
## Building and Testing
20112

21-
- Add support for more finite fields.
22-
- Add support for the sponge construction.
23-
- Add benchmarks and optimizations.
113+
```bash
114+
# Build the library
115+
zig build
116+
117+
# Run all tests (includes BabyBear and KoalaBear)
118+
zig build test
119+
```
120+
121+
## Field Comparison
122+
123+
| Feature | BabyBear | KoalaBear |
124+
|---------|----------|-----------|
125+
| **Prime** | 2³¹ - 2²⁷ + 1 | 2³¹ - 2²⁴ + 1 |
126+
| **Hex Value** | 0x78000001 | 0x7f000001 |
127+
| **Width** | 16 | 16 |
128+
| **S-Box Degree** | 7 | 3 |
129+
| **Internal Rounds** | 13 | 20 |
130+
| **External Rounds** | 8 | 8 |
131+
| **Compatible With** | Ethereum Lean | plonky3, Rust hash-sig |
132+
133+
**Important:** Different fields produce completely different hash outputs! Choose the field that matches your target system.
134+
135+
## Project Motivation
136+
137+
This repository was created primarily to support the upcoming Ethereum Lean chain. The KoalaBear field was added to enable compatibility with Rust's hash-sig implementation and plonky3.
24138

25139
## Compatibility
26140

27-
This implementation has been cross-validated against the [reference repository](https://github.com/HorizenLabs/poseidon2) cited in the Poseidon2 paper to ensure correctness.
141+
- **BabyBear**: Cross-validated against the [Poseidon2 reference repository](https://github.com/HorizenLabs/poseidon2)
142+
- **KoalaBear**: Compatible with [plonky3](https://github.com/Plonky3/Plonky3) and [Rust hash-sig](https://github.com/b-wagn/hash-sig)
143+
144+
Both implementations include tests ensuring the naive and optimized (Montgomery) implementations produce identical outputs.
145+
146+
## Future Enhancements
147+
148+
- Add support for more finite fields
149+
- Add support for the sponge construction
150+
- Add benchmarks and performance optimizations
151+
- Add more S-Box degrees as needed
28152

29153
## License
30154

31155
MIT
156+
157+
## References
158+
159+
- [Poseidon2 Paper](https://eprint.iacr.org/2023/323)
160+
- [HorizenLabs/poseidon2 Reference Implementation](https://github.com/HorizenLabs/poseidon2)
161+
- [Plonky3](https://github.com/Plonky3/Plonky3)
162+
- [Rust hash-sig](https://github.com/b-wagn/hash-sig)

build.zig

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,32 @@ pub fn build(b: *std.Build) void {
44
const target = b.standardTargetOptions(.{});
55
const optimize = b.standardOptimizeOption(.{});
66

7-
_ = b.addModule("poseidon", .{
8-
.root_source_file = .{
9-
.cwd_relative = "src/poseidon2/poseidon2.zig",
10-
},
7+
// Generic Poseidon2 module
8+
_ = b.addModule("poseidon2", .{
9+
.root_source_file = b.path("src/poseidon2/poseidon2.zig"),
10+
});
11+
12+
// BabyBear16 instance
13+
_ = b.addModule("babybear16", .{
14+
.root_source_file = b.path("src/instances/babybear16.zig"),
15+
});
16+
17+
// KoalaBear16 instance (compatible with Rust hash-sig)
18+
_ = b.addModule("koalabear16", .{
19+
.root_source_file = b.path("src/instances/koalabear16.zig"),
1120
});
1221

1322
const lib = b.addStaticLibrary(.{
1423
.name = "zig-poseidon",
15-
.root_source_file = .{ .cwd_relative = "src/main.zig" },
24+
.root_source_file = b.path("src/main.zig"),
1625
.target = target,
1726
.optimize = optimize,
1827
});
1928

2029
b.installArtifact(lib);
2130

2231
const main_tests = b.addTest(.{
23-
.root_source_file = .{ .cwd_relative = "src/main.zig" },
32+
.root_source_file = b.path("src/main.zig"),
2433
.target = target,
2534
.optimize = optimize,
2635
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// KoalaBear field: p = 2^31 - 2^24 + 1 = 127 * 2^24 + 1 = 2130706433 = 0x7f000001
2+
// This field is used in plonky3 and Rust hash-sig implementations
3+
pub const MontgomeryField = @import("../generic_montgomery.zig").MontgomeryField31(127 * (1 << 24) + 1);

src/fields/koalabear/naive.zig

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
const std = @import("std");
2+
3+
// KoalaBear field: p = 2^31 - 2^24 + 1 = 127 * 2^24 + 1 = 2130706433 = 0x7f000001
4+
const modulus = 127 * (1 << 24) + 1;
5+
pub const FieldElem = u32;
6+
pub const MontFieldElem = u32;
7+
8+
pub fn toMontgomery(out1: *MontFieldElem, value: FieldElem) void {
9+
out1.* = value;
10+
}
11+
12+
pub fn toNormal(out1: MontFieldElem) FieldElem {
13+
return out1;
14+
}
15+
16+
pub fn square(out1: *MontFieldElem, value: MontFieldElem) void {
17+
mul(out1, value, value);
18+
}
19+
20+
pub fn add(out1: *MontFieldElem, elem1: MontFieldElem, elem2: MontFieldElem) void {
21+
var tmp: u64 = elem1;
22+
tmp += elem2;
23+
tmp %= modulus;
24+
out1.* = @intCast(tmp);
25+
}
26+
27+
pub fn mul(out1: *MontFieldElem, elem1: MontFieldElem, elem2: MontFieldElem) void {
28+
var tmp: u64 = elem1;
29+
tmp *= elem2;
30+
tmp %= modulus;
31+
out1.* = @intCast(tmp);
32+
}

0 commit comments

Comments
 (0)