Private Money: Part 2

Investigating Project Tachyon: Preliminaries

cryptopcs

⚠️ Warning: Mathematics. This is a fairly technical post! I assume a solid understanding of high-school math and a willingness to bear with me :)

This blog reviews the prerequisite mathematics for understanding the set-noninclusion accumulator in Project Tachyon — if you’re already familiar with basic abstract algebra, feel free to skip to the next post.

Some Mathematics Prerequisites

Musical Motivation

In the 18th century, composer Johann Sebastian Bach wrote music that continues to be admired for its elegance and structure. Despite lacking formal mathematical training, Bach often composed with a precision that feels inherently mathematical. One striking example is his Crab Canon from The Musical Offering:

Bach’s Crab Canon from the Musical Offering is a fascinating example of mathematical music. When played forward and backward simultaneously, it creates a perfect palindrome — a musical Möbius strip where the end connects seamlessly to the beginning. This topological structure, where a one-sided surface is created by twisting and joining a strip, mirrors how the canon’s melody can be read in both directions while maintaining musical coherence.

The connection between musical expression and hidden structure has fascinated people for a long time. One of the key ideas here is the Circle of Fifths — a diagram that lays out musical notes so that closely related keys sit next to each other in a loop. It’s a handy way to make sense of harmony, and its circular shape hints at something deeper going on beneath the sound: a kind of pattern that music follows, even when we’re not consciously aware of it.

Circle of fifths clockwise within one octave

Circle of fifths clockwise within one octave. Source: Wikipedia.

Building on this, modern visualizations let us see musical motion as something geometric. In the animation below, a major seventh progression moves across the surface of an umbilic torus — a looping, twisted shape that shows how harmony can circle around while still shifting forward. Like Bach’s Canon, it turns music into more than just a line of notes — it becomes a kind of movement through space, shaped by patterns and rules we can start to recognize, even if we can’t name them yet.

Major 7th progression on umbilic torus surface

This animation shows a major seventh progression traced on an umbilic torus surface — a higher-dimensional visualization of the circle of fifths. The smooth rotation through harmonic space represents tonal motion as continuous geometry, revealing deep symmetries between pitch, interval, and curvature.

These patterns aren’t just beautiful — there’s clearly something deeper going on under the surface. To really make sense of it, we’ll need a new kind of language — one that helps us talk about how music moves, transforms, and loops back on itself. That’s where we’re headed next: group theory.

From Loops to Logic

A group is a set equipped with a rule for combining elements — an operation — that behaves predictably: there’s a way to combine any two elements (closure), the way elements are grouped doesn’t matter (associativity), there’s an identity element that does nothing (identity), and every element has an inverse that undoes it (inverses).

A simple example of a group is a finite cyclic group Z/nZ\mathbb{Z} / n \mathbb{Z}, the integers modulo nn which consists of the set

0,1,2,,n1 {0, 1, 2, \ldots, n - 1}

with the action being addition modulo nn. For example, in Z/12Z\mathbb{Z} / 12 \mathbb{Z}, 12012 \cong 0 so 7+6=1mod127 + 6 = 1 \mod 12. Check out Wikipedia for the mathematical definition of a group.

It turns out that cyclic groups are also very useful for cryptography. Suppose that you start with 00 in Z/12Z\mathbb{Z} / 12 \mathbb{Z} and keep adding 5. Then you get the sequence:

0,5,10,3,8,1, 0, 5, 10, 3, 8, 1, \ldots

Eventually, you reach every number in the set. Now, if I asked you to tell how many times you have to add 5 to get the number 8, would you be able to tell me?

This is the Discrete Logarithm Problem. Given a generator (like 5) and a result (like 8), the challenge is to figure out how many times the generator was applied. In this example, the answer is 4, since 5×4=208mod125 \times 4 = 20 \cong 8 \mod 12.

In cryptography, we often write cyclic groups using multiplicative notation, where repeated application of a generator gg is written as:

g0,  g1,  g2,  ,  gn1g^0, \; g^1, \; g^2, \; \dots, \; g^{n - 1}

This gives all elements of the group if gg is a generator.

The discrete logarithm problem asks: Given gg and h=gxh = g^x, find xx.

For small numbers, this is easy to solve by trial. But in large cyclic groups (especially those built from primes with hundreds of digits) the problem becomes extremely hard. For small numbers, this is easy to solve by trial. But in large cyclic groups (like those used in Ethereum’s BLS signatures, where the modulus is a 381-bit prime) the problem becomes practically impossible to reverse, and that’s exactly what makes it secure against classical computers.

Pedersen Vector Commitment Scheme

Finally, we have the language to talk about tools that rely on group structure to ensure both hiding and binding: the two essential properties of a cryptographic commitment. One of the simplest and most elegant examples is the Pedersen commitment.

Hiding and Binding

Before going further, it’s worth pausing to explain what we mean by hiding and binding.

In short: hiding protects privacy; binding ensures integrity.

The Pedersen Commitment

Let GG be a cyclic group of prime order qq, with generators gg and hh such that no one knows the discrete logarithm between them. To commit to a value mZ/qZm \in \mathbb{Z} / q \mathbb{Z}, choose a random blinding factor rZ/qZr \in \mathbb{Z} / q \mathbb{Z} and compute:

Com(m,r)=gmhr\text{Com}(m, r) = g^m h^r

This is a commitment to mm that is:

Additionally, Pedersen commitments are homomorphic:

Com(m1,r1)Com(m2,r2)=Com(m1+m2,r1+r2)\text{Com}(m_1, r_1) \cdot \text{Com}(m_2, r_2) = \text{Com}(m_1 + m_2, r_1 + r_2)

This means commitments can be added without opening them, a property useful in many protocols.

Now here’s how we implement this in Rust:

pub fn commit(m: Scalar, r: Scalar, g: GroupAffine, h: GroupAffine) -> GroupAffine {
    (g * m + h * r).into_affine()
}

From Commitments to Vector Commitments

To commit to a whole vector m=(m1,m2,,mn)\mathbf{m} = (m_1, m_2, \dots, m_n), we extend the idea by using nn independent generators g1,g2,,gnGg_1, g_2, \dots, g_n \in G, and a single blinding base hh. The commitment is:

Com(m,r)=g1m1g2m2gnmnhr\text{Com}(\mathbf{m}, r) = g_1^{m_1} \cdot g_2^{m_2} \cdots g_n^{m_n} \cdot h^r

The vector commitment version in Rust takes an array of generators:

pub fn open(v: &[Scalar], r: Scalar, j: usize) -> Result<(Scalar, Scalar, GroupAffine)> {
    if j >= v.len() {
        return Err(anyhow!("Index out of bounds"));
    }

    let blind = POINTS[0] * r;
    let witness = POINTS[1..].iter().enumerate()
        .filter(|(i, _)| *i != j)
        .map(|(i, p)| *p * v[i])
        .sum::<GroupProjective>() + blind;
    
    Ok((v[j], r, witness.into_affine()))
}

This compactly binds the entire vector m\mathbf{m} into a single group element. It maintains the same properties:

And here’s how to verify the commitment:

pub fn check(c: GroupAffine, v_j: Scalar, witness: GroupAffine, h_j: GroupAffine) -> bool {
    c == witness + h_j * v_j
}

Algebraic Properties

What makes Pedersen (vector) commitments especially powerful is their algebraic structure:

These algebraic properties make Pedersen commitments a favorite building block in privacy-preserving protocols, SNARK-friendly constructions, and succinct proofs of integrity over large datasets. We will see how we can build Sean Bowe’s set-noninclusion accumulator from this in the next blog post.