libdpf/doc/pages/ppvc.md
Ryan Henry cf8054a0b3 Add point-programmable vector commitments.
A commitment can be published before its hidden coordinate is chosen.
Opening one side of each aligned 1-bit DPF pair sets that coordinate or
the vector sum, and a shift moves it onto a public index.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-26 18:34:05 -06:00

85 lines
3.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Point-programmable vector commitments {#ppvc_manual}
A point-programmable vector commitment binds a vector
`x` in `(Z/2^s Z)^n` and still lets one hidden coordinate be chosen
after the commitment is published.
`n` is a power of two, the bit length of the input type, and at most
2^16. `s` is the `Width` parameter, from 1 to 64.
The manual construction is `dpf::ppvc`. `dpf::k_ppvc<K, ...>` is `K`
independent copies of that object.
The committer samples an index `i` and builds `s` aligned 1-bit DPF
pairs there, the same point key as [DPF basics](@ref basics_body).
Both roots of every pair are bound with a Naor commitment under a
public matrix `A`. Opening releases one key from each pair, together
with a shift `delta = xi - i`.
Off `i`, the two keys of a pair evaluate to the same bit.
At `i`, they evaluate to opposite bits.
Choosing the side therefore writes an arbitrary value into that one
coordinate and leaves every other coordinate fixed.
The shift moves the written coordinate from `i` onto the public target
`xi`. The opening carries `delta`, not `i` and not `xi`.
`open(st, mu, tau, xi)` has two modes.
- `mu = 0` programs the coordinate. After rotation, entry `xi` equals `tau`.
- `mu = 1` programs the sum of every coordinate. That sum equals `tau`.
Those two maps are bijections on `Z/2^s Z`. Programming one of them
programs the other.
```cpp
using scheme = dpf::ppvc<std::uint8_t, 8>;
const auto pp = scheme::setup();
const auto [com, st] = scheme::commit(pp);
const std::uint8_t xi = 40;
const auto op = scheme::open(st, 0, 0x5a, xi);
const auto x = scheme::eval(op); // hidden indexing
const auto rotated = scheme::eval_rotated(op); // value 0x5a sits at xi
const bool ok = scheme::accept(pp, com, op, x, xi);
```
`setup` samples `A`. `setup_from_seed` expands one 128-bit seed into the
same matrix, which is the common random string when many sessions share
it. `commit` samples `i`. `commit_at` uses an index the caller already
chose. The shift hides `i` when that index was sampled independently of
`xi`. `commit_from_seed` and `commit_at_from_seed` rerun key generation
from a replica seed. Seed expansion keeps its counter in thread-local
storage, so two expansions on one thread must not overlap.
## What an opening proves {#ppvc_verify}
`verify` checks each opened root against its Naor string.
`accept` also checks the programmed statement: the rotated coordinate
when `mu` is 0, the column sum when `mu` is 1.
Correction words travel with the opened key. They are not inside the
commitment. `verify` sees one side of each pair.
`check_well_formed` is the check on a replica the committer still holds:
shared correction words, party bits 0 and 1, both Naor openings, and
exactly one place where the two keys disagree, at the recorded index,
with payload 1.
`audit` expands a seed and accepts when the published commitment matches
that expansion and the replica is well formed.
`k_ppvc` asks for the same checks on every copy, and for distinct hidden
indices. `combine_rotated` adds the rotated vectors in `Z/2^s Z`.
Reprogramming copy `r` changes coordinate `xi[r]` of that sum.
The commitment is `2 * s * (3 * 128 + Sigma)` bits.
`Sigma` defaults to 128 and must be a multiple of 8.
The generator is `dpf::prg::aes128` unless another 128-bit PRG is named.
**Defined in**\n
@ref dpf/ppvc.hpp
**Try**\n
@ref mwe/ppvc.cpp
Naor's string commitment is Moni Naor, "Bit Commitment Using
Pseudorandomness," Journal of Cryptology 4(2), 1991, pp. 151–158.
The point keys are the Boyle–Gilboa–Ishai construction named in
[DPF basics](@ref point_functions).