Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c2c45df
perf(blake2): reuse packed Spongefish prefix compressions
Acentelles Oct 20, 2026
cab2954
perf(recursion): forward Akita's factored compression-weight feature
a16zentelles Oct 22, 2026
7a72d0f
perf(recursion): turn factored compression weights on by default
Acentelles Oct 23, 2026
987289c
perf(recursion): forward Akita's terminal quotient certificate feature
Acentelles Oct 22, 2026
68990a2
perf(recursion): turn the terminal quotient certificate on by default
Acentelles Oct 23, 2026
142bf1f
perf(field,transcript): word-sized Fp128 decoding and transcript scalars
Acentelles Oct 23, 2026
8e8ec59
Define canonical big-endian bytes for transcript feature callers
Acentelles Oct 23, 2026
e79d6d9
Gate Solinas encoding tests before lint expectations
Acentelles Oct 24, 2026
c0722ae
perf(recursion): turn word decoding on by default
Acentelles Oct 24, 2026
c14ac93
perf(verifier): reuse instruction read RAF public inputs
Acentelles Oct 23, 2026
407e5de
style: rustfmt the instruction read RAF tests
Acentelles Oct 24, 2026
fb0c01a
Add opt-in RV64 word memory routines for recursion
Acentelles Oct 25, 2026
da9243b
Preserve assembler sections around word memory routines
Acentelles Oct 25, 2026
8ebf215
perf(recursion): turn word memory routines on by default
Acentelles Oct 25, 2026
ec01b72
build(akita): pin the recursion verifier changes on the Akita fork
Acentelles Oct 25, 2026
63c4870
perf(recursion): build the recursion verifier guest with inline-thres…
Acentelles Oct 25, 2026
7fe2a92
perf(recursion): raise the recursion verifier guest inline threshold …
Acentelles Oct 25, 2026
e50659d
perf(recursion): enable GVN hoisting and DFA jump threading in the re…
Acentelles Oct 26, 2026
1a48794
perf(recursion): use ExtTSP block placement without tail merging in t…
Acentelles Oct 26, 2026
85bee6f
test(jolt-sdk): skip the Dory verifier fixture test under akita
Acentelles Oct 26, 2026
81cbce4
perf(recursion): use size-oriented spill splitting in the recursion v…
Acentelles Oct 26, 2026
0d918f6
perf(recursion): disable the RISC-V register cost-per-use model in th…
Acentelles Oct 26, 2026
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
32 changes: 16 additions & 16 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 14 additions & 12 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -306,18 +306,20 @@ dory = { package = "dory-pcs", version = "0.4.2", features = [
"disk-persistence",
"zk",
] }
# LayerZero-Labs/akita branch markos/recursion-integration: akita cf95fd05
# (the revision Jolt main pins) plus the recursion verifier-guest PRs
# (LayerZero-Labs/akita#186-#190). Repin to akita main once those merge.
akita-algebra = { git = "https://github.com/LayerZero-Labs/akita.git", rev = "ee03aa30544caaa3f5f2948b222790b919e9b8e7", default-features = false }
akita-config = { git = "https://github.com/LayerZero-Labs/akita.git", rev = "ee03aa30544caaa3f5f2948b222790b919e9b8e7", default-features = false }
akita-error = { git = "https://github.com/LayerZero-Labs/akita.git", rev = "ee03aa30544caaa3f5f2948b222790b919e9b8e7", default-features = false }
akita-pcs = { git = "https://github.com/LayerZero-Labs/akita.git", rev = "ee03aa30544caaa3f5f2948b222790b919e9b8e7", default-features = false }
akita-planner = { git = "https://github.com/LayerZero-Labs/akita.git", rev = "ee03aa30544caaa3f5f2948b222790b919e9b8e7", default-features = false }
akita-schedules = { git = "https://github.com/LayerZero-Labs/akita.git", rev = "ee03aa30544caaa3f5f2948b222790b919e9b8e7", default-features = false }
akita-challenges = { git = "https://github.com/LayerZero-Labs/akita.git", rev = "ee03aa30544caaa3f5f2948b222790b919e9b8e7", default-features = false }
akita-types = { git = "https://github.com/LayerZero-Labs/akita.git", rev = "ee03aa30544caaa3f5f2948b222790b919e9b8e7", default-features = false }
akita-params = { git = "https://github.com/LayerZero-Labs/akita.git", rev = "ee03aa30544caaa3f5f2948b222790b919e9b8e7", default-features = false }
# Akita markos/recursion-integration (ee03aa30, LayerZero-Labs/akita#186-#190)
# plus the recursion verifier changes on Acentelles/akita-lz: factored
# compression weights, the terminal quotient certificate, transposed quotient
# right sides, the L2 proof bound, batched reduced setup rows and signed sparse
# accumulation. Repin to LayerZero-Labs/akita once those land there.
akita-algebra = { git = "https://github.com/Acentelles/akita-lz.git", rev = "9db4ee25b89b1b66c42f399af983230333733c8b", default-features = false }
akita-config = { git = "https://github.com/Acentelles/akita-lz.git", rev = "9db4ee25b89b1b66c42f399af983230333733c8b", default-features = false }
akita-error = { git = "https://github.com/Acentelles/akita-lz.git", rev = "9db4ee25b89b1b66c42f399af983230333733c8b", default-features = false }
akita-pcs = { git = "https://github.com/Acentelles/akita-lz.git", rev = "9db4ee25b89b1b66c42f399af983230333733c8b", default-features = false }
akita-planner = { git = "https://github.com/Acentelles/akita-lz.git", rev = "9db4ee25b89b1b66c42f399af983230333733c8b", default-features = false }
akita-schedules = { git = "https://github.com/Acentelles/akita-lz.git", rev = "9db4ee25b89b1b66c42f399af983230333733c8b", default-features = false }
akita-challenges = { git = "https://github.com/Acentelles/akita-lz.git", rev = "9db4ee25b89b1b66c42f399af983230333733c8b", default-features = false }
akita-types = { git = "https://github.com/Acentelles/akita-lz.git", rev = "9db4ee25b89b1b66c42f399af983230333733c8b", default-features = false }
akita-params = { git = "https://github.com/Acentelles/akita-lz.git", rev = "9db4ee25b89b1b66c42f399af983230333733c8b", default-features = false }

# Core Utilities
clap = { version = "4.6.1", features = ["derive"] }
Expand Down
12 changes: 12 additions & 0 deletions crates/jolt-akita/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,19 @@ ntt-inline = ["akita-pcs/ntt-inline"]
keccak-inline = ["akita-pcs/keccak-inline"]
# Enables field-inline schedule coverage alongside the prover's field profile.
field-inline = []
# L2 terminal quotient certificate (recursion lever, default off): the
# terminal A rows are checked at two points against a sent quotient, under
# Akita protocol identifier v7-l2tq1.
l2-terminal-quotient = ["akita-pcs/l2-terminal-quotient"]
profiling = ["parallel"]
# L3 recursion experiment (default off): Akita evaluates the compression
# relation weights by factor (same field values; see akita-types).
l3-compression-weights = ["akita-types/l3-compression-weights"]

# Byte-preserving recursion experiments, default off.
word-transcript = ["jolt-transcript/word-transcript"]
word-decode = ["jolt-field/word-decode"]
ont-static-base = ["akita-challenges/ont-static-base"]

[dependencies]
# Jolt keeps its own catalogs for Jolt-specific trace shapes; Akita's shipped
Expand Down
4 changes: 4 additions & 0 deletions crates/jolt-field/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ field-inline-guest = ["dep:jolt-riscv"]
field-inline-guest-bn254 = ["field-inline-guest"]
field-inline-guest-fp128 = ["field-inline-guest"]

# Byte-preserving recursion experiments, default off.
word-transcript = []
word-decode = []

[dev-dependencies]
# The spine test's oracle field must satisfy `JoltField`, which requires
# `Allocative` under the `allocative` feature; integration tests do not see
Expand Down
11 changes: 11 additions & 0 deletions crates/jolt-field/src/algebra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,17 @@ pub trait CanonicalBytes {
/// Writes the canonical little-endian encoding into `out`.
fn to_bytes_le(&self, out: &mut [u8]);

/// Writes the canonical encoding in big-endian byte order into `out`.
///
/// `out` must have length [`NUM_BYTES`](Self::NUM_BYTES). This is the
/// byte reversal of [`to_bytes_le`](Self::to_bytes_le), including for
/// non-field encodings.
#[inline]
fn to_bytes_be(&self, out: &mut [u8]) {
self.to_bytes_le(out);
out.reverse();
}

/// Returns the canonical little-endian encoding as a vector.
#[inline]
fn to_bytes_le_vec(&self) -> Vec<u8> {
Expand Down
16 changes: 16 additions & 0 deletions crates/jolt-field/src/solinas/fp128.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1157,6 +1157,13 @@ impl<const P: u128> CanonicalBytes for Fp128<P> {
assert_eq!(out.len(), Self::NUM_BYTES);
out.copy_from_slice(&join(self.0).to_le_bytes());
}

#[cfg(feature = "word-transcript")]
#[inline(always)]
fn to_bytes_be(&self, out: &mut [u8]) {
assert_eq!(out.len(), Self::NUM_BYTES);
out.copy_from_slice(&join(self.0).to_be_bytes());
}
}

impl<const P: u128> CanonicalEncoding for Fp128<P> {
Expand Down Expand Up @@ -1204,12 +1211,21 @@ impl<const P: u128> CanonicalEncoding for Fp128<P> {

#[inline]
fn from_scalar_challenge_bytes(bytes: &[u8]) -> Self {
#[cfg(feature = "word-transcript")]
if let Ok(array) = <[u8; 16]>::try_from(bytes) {
return Self::from_u128(u128::from_le_bytes(array));
}
Self::from_bytes_le_reduced(bytes)
}
}

#[cfg(not(feature = "word-decode"))]
crate::impl_serde_bytes!(impl[const P: u128] Fp128<P>, 16);

#[cfg(feature = "word-decode")]
#[path = "fp128_word_decode.rs"]
mod word_decode;

impl<const P: u128> WithAccumulator for Fp128<P> {
type Accumulator = Fp128Accumulator<P>;
type SmallScalarAccumulator = Fp128SignedAccumulator<P>;
Expand Down
54 changes: 54 additions & 0 deletions crates/jolt-field/src/solinas/fp128_word_decode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//! Fixed byte-tuple decoding with register-sized accumulators.

use core::fmt::{Formatter, Result as FormatResult};

use serde::de::{Error, SeqAccess, Visitor};
use serde::{Deserialize, Deserializer, Serialize, Serializer};

use super::Fp128;
use crate::{CanonicalBytes, CanonicalEncoding};

struct WordArrayVisitor;

impl<'de> Visitor<'de> for WordArrayVisitor {
type Value = u128;

fn expecting(&self, formatter: &mut Formatter) -> FormatResult {
formatter.write_str("an array of length 16")
}

#[inline]
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<u128, A::Error> {
let mut lo = 0u64;
let mut hi = 0u64;
for i in 0..16 {
let byte = seq
.next_element::<u8>()?
.ok_or_else(|| Error::invalid_length(i, &self))?;
// Each shift is at most 56; every byte owns disjoint bits.
if i < 8 {
lo |= u64::from(byte) << (8 * i);
} else {
hi |= u64::from(byte) << (8 * (i - 8));
}
}
Ok(u128::from(lo) | (u128::from(hi) << 64))
}
}

impl<const P: u128> Serialize for Fp128<P> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut bytes = [0u8; 16];
self.to_bytes_le(&mut bytes);
bytes.serialize(serializer)
}
}

impl<'de, const P: u128> Deserialize<'de> for Fp128<P> {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
// Preserve the byte-tuple grammar, including its per-byte error order.
let value = deserializer.deserialize_tuple(16, WordArrayVisitor)?;
Self::from_u128_checked(value)
.ok_or_else(|| Error::custom("non-canonical field element encoding"))
}
}
32 changes: 32 additions & 0 deletions crates/jolt-field/tests/canonical_bytes_big_endian.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use jolt_field::CanonicalBytes;

struct LittleEndianBytes<const N: usize>([u8; N]);

impl<const N: usize> CanonicalBytes for LittleEndianBytes<N> {
const NUM_BYTES: usize = N;

fn to_bytes_le(&self, out: &mut [u8]) {
out.copy_from_slice(&self.0);
}
}

fn write_big_endian<T: CanonicalBytes>(value: &T, out: &mut [u8]) {
value.to_bytes_be(out);
}

#[test]
fn canonical_bytes_default_big_endian_matches_wire_order() {
let value = LittleEndianBytes([0x78, 0x56, 0x34, 0x12]);
let mut out = [0u8; 4];
write_big_endian(&value, &mut out);
assert_eq!(out, [0x12, 0x34, 0x56, 0x78]);
assert_eq!(value.to_bytes_le_vec(), [0x78, 0x56, 0x34, 0x12]);
}

#[test]
fn canonical_bytes_default_big_endian_accepts_empty_encoding() {
let value = LittleEndianBytes([]);
let mut out = [];
write_big_endian(&value, &mut out);
assert!(out.is_empty());
}
Loading
Loading