Commit graph

225 commits

Author SHA1 Message Date
Rob Knight
1e592e11cf
Self-referential predicate hashes as statement template args (#494)
* Support quoted predicate hashes, including self-referential predicates

* Clippy

* Review feedback
2026-03-24 07:25:11 -07:00
13cabdb511
Support persistent storage in Containers (#493)
Extend the work of https://github.com/0xPARC/pod2/pull/487 to the Containers (Dictionary, Set, Array).

The merkle tree only stores `RawValue` for both the key and the value, so it is the responsibility of the Container to store the rich value.

In order to handle containers with persistent storage efficiently (which means, cloning them or updating them should not cause an O(n) data copy) I figured we need to have a database of `Value`s indexed by their raw value; as this gives us deduplication and free cloning of containers.
The issue with this approach is that in the current design we have collisions between Value's of different types: https://github.com/0xPARC/pod2/issues/426 and the current API relies on the single type of values.

To resolve this issue I decided to change the API, instead of assuming that a Value has a fixed type, let the value be possibly multiple compatible types and let the user of the library try casting the Value to a particular type.
For this I deprecated the public access of everything related to `TypedValue` and I propose for it to be considered an implementation detail and a blackbox from the external developer point of view.  The `Value` type is now used like this:
- To create a new Value use `Value::from(...)` where you can pass any compatible type (the same types as before)
- To access the Value in typed form you cast it like `value.as_foo()` which returns `Option<Foo>`.

Previously we had a collision between `true` and `1` (and `false` and `0`).  Now it doesn't matter whether a value holds a `true` or a `1`, both should be seen as the same and both return `Some` when doing `as_int` and `as_bool`.

Similarly we had collisions with containers.  For example `set(0, 1, 2) == array[0, 1, 2]` and `set("a", "b") = dict("a": "a", "b": "b")`.  Now any container can be casted to any of `set, array, dict`.  There's a caveat here: each of these types expects a particular encoding of keys, so casting to the wrong type will return errors on some operations.

With this design it no longer matters what is being stored and recovered because the API requires the user to express the expected type and any type with collisions for particular values can be casted to the right type.

There's only one case where it's not desirable to swap one `TypedValue` for another: the `TypedValue::Raw`.  If a non-`RawValue` in the DB is replaced by the corresponding `RawValue` we erase the required information to recover the rich value.  For this reason the implementations of the database treat the `RawValue` as a special case: if an value is stored in non-`RawValue`, the corresponding `RawValue` can never overwrite it.  If a value is stored in `RawValue`, a matching non-`RawValue` will overwrite it (promoting it to a rich value).  This way we never lose data.

A consequence of this is that the serialization, `Display` and `Debug` of a container is not stable.  At any point any of the entries can be swapped for a "compatible" one if they share the storage with other containers that introduce collisions.

I rewrote all containers as wrapper to a generic `Container` which holds a `Map` from `Value` to `Value`.  The serialization of each container now uses the single implementation of the generic `Container`.
2026-03-23 12:31:28 +01:00
arnaucube
32f45872d7
Re-implement merkletree with persistent storage (key-value db) (#487)
* refactor merkletree to work with disk keyvalue database (wip)

* various fixes post reimplementation; pending delete leaf

* add delete operation case for the new in db tree approach

* polish tree update & delete; everything works (pending polishing)

* polish panics into errs, prints, etc

* Implement iterator

* Lint

* fix case no-siblings

* case delete with semi-empty branch

* polishing

* starting to add rocksdb & heeddb for the DB & Txn traits

* Satisfy the borrow checker

* abstract merkletree tests to use the various available DBs

* update store_node interface (rm hash input), rm heed.rs

* polishing

* typos

* Ditch transactions

* add feature for rocksdb, return errs at new_with_db, remove empty leaf case in Leaf::new

* intermediate instead of leaf in empty node when deleting leaf

---------

Co-authored-by: Ahmad <root@ahmadafuni.com>
2026-03-11 16:32:42 +01:00
a79f82eb9d
fix disk cache datarace (#486)
There was a data race when multiple threads/processes found that the cache directory for a given params didn't exist as they both tried to:
- Create a tmp params file
- Rename the tmp params file

Both threads would create the tmp params file (one would overwrite the other).  Then one would rename the tmp file successfully, and the other would try to rename it again but would find it missing.

The fix involves using a file lock on the tmp file so that only one thread goes through the writing and renaming while others wait.  It's the same approach done for cached files.
2026-02-23 14:13:34 +01:00
Rob Knight
a389ff1dc4
Multipod external fix (#485) 2026-02-23 11:26:39 +00:00
Dhvani Patel
c185d27344
fix: points wildcard name same as predicate (#484) 2026-02-23 11:22:29 +01:00
Rob Knight
f6c6ec43ef
Splitter fixes (#483)
* Add failing tests

* Model statements depending on public args as cheaper than those depending on private args

* Tidying

* Fix unnecessary propagation of unused public args

* More tidying

* Tidy test comments

* Fix incorrect Delete arities
2026-02-20 08:13:42 -08:00
Rob Knight
e950661090
Fix nondeterminism in splitting (#482) 2026-02-16 15:46:36 +01:00
cdf227e353
MultiPodBuilder: keep public sts order (#479) 2026-02-13 12:39:28 +01:00
Rob Knight
bf56c86cfc
MultiPodBuilder fixes (#480)
* Dedupe statements during POD-building

* Fix failure to assume existence of Contains statement

* Remove possible source of non-determinism

* Faster ILP backend

* Formatting
2026-02-13 02:32:40 -08:00
Rob Knight
09d67de989
Add annotate_snippets for better parsing errors (#477)
Adds nicer errors for Podlang code, using the `annotate_snippets` crate, the same crate used by the Rust compiler to generate contextual errors. This prints a short snippet of the code containing the error within the error message, highlighting the part that needs to be fixed.

It also includes a change to the `load_module` function, changing a `Vec` function argument to a slice.
2026-02-11 11:14:23 +01:00
Rob Knight
acab26e5c1
Remove batch splitting system (#475)
* First pass at removing batch splitting

* Refactor to separate module loading from request parsing

* Consolidate module functionality

* Tidy up comments

* Use array of modules instead of HashMap

* Formatting

* Use module hashes when importing modules
2026-02-09 10:31:47 +01:00
Rob Knight
5dab8195b4
Fix accidental inclusion of extra input PODs (#476) (#478)
* Fix issue with adding extra input PODs

* Panic if input PODs are missing
2026-02-06 17:18:35 +01:00
2bd99ef322
MultiPodBuilder improvements (#473)
- Export MultiPodBuilder Error
- Redefine MultiPodBuilder error to wrap the frontend Error (this way we get better formatting instead of an embedded string)
- handle initial wildcard values in `MultiPodBuilder.op` just like the `MainPodBuilder` does
2026-02-06 10:53:01 +01:00
arnaucube
b04560c362
merkletree: reduce gate amount (-23%) by custom poseidon to use flag as initial state (#472)
* merkletree: custom poseidon to use flag as initial state.

This allows to do the merkletree related hashing in 1 gate instead of 2,
reducing ~23% of gates per merkle proof.

| tree levels   | 10 | 16 | 32  | 40  | 64  | 128 | 130 | 250  | 256  |
|---------------|----|----|-----|-----|-----|-----|-----|------|------|
| old num gates | 50 | 76 | 144 | 178 | 280 | 554 | 564 | 1076 | 1102 |
| new num gates | 39 | 59 | 111 | 137 | 215 | 425 | 433 | 825  | 845  |

* update docs with new tree hashing approach

* add inline comment stating clear how the flag is used in the state permutation
2026-02-04 12:31:56 +01:00
641d8dabdd
Merkle tree for custom predicate batches (#471)
Resolve https://github.com/0xPARC/pod2/issues/466

Now batches are identified by the root of a merkle tree that contains all the predicates (using sequential indices as keys).  This means that the format to identify a custom predicate reference is still a hash + index, but the calculation of the hash is different.
The MainPod circuit now isn't limited by number of batches but instead number of custom predicates; and for each one we verify a merkle proof to verify the batch id.

I've removed a bunch of tests from lang that were testing splitting into multiple batches because there's no longer any need for that.  In a future PR we'll remove the code that handles batch splitting.

Each custom predicate needs 148.2 gates (which is very close to my estimate of 142.7 in https://github.com/0xPARC/pod2/issues/466#issuecomment-3823531286 where I actually made a mistake and considered 5 predicates per batch instead of 4 in the previous Params).
2026-02-04 11:12:32 +01:00
a7a30176a7
Split Params into base and developer-defined (#458)
I thought it would be nice to have a Predicate for the typed value so that the developer can work with predicates as values comfortably.  Then I noticed that hashing a predicate required `Params` which would have been annoying for converting a `TypedValue::Predicate` to `RawValue` and this led to a small refactor over how `Params` work.

We already had some fields in the `Params` struct that determine compatibility between encoded data.  They can be seen as determining a kind of ABI compatibility.  In general it's better if those parameters don't change so that different circuit configurations can still verify proofs from each other.  So I decided to force those parameters to be constant in the code base and not allow the user of our library to change them.  Many field element serialization/deserialization functions in our code depended on those parameters, and since now they are constant many functions get rid of the `Params` argument, which simplifies the code.  This includes the serialization of a `Predicate` which was required to calculate its hash.
2026-02-02 16:23:32 +01:00
498e946612
Feat/fst order pred part3 & part4 (#457)
* support wildcard predicates in frontend

* suport wildcard predicate in podlang

* add validation test

* test full flow and apply some fixes

* fix clippy

* fix merge issues

* use desugared predicate

* Fix parsing of intro statement templates inside custom predicates

* Tidy up comments

* lang: handle wildcard predicate

* add unreachable message

---------

Co-authored-by: Rob Knight <mail@robknight.org.uk>
2026-02-02 10:59:33 +01:00
Rob Knight
b66f5051b5
MultiPodBuilder fixes (#468)
* Remove CopyStatement constraints

* Use a constant objective, since the iterative approach already finds the minimum number of PODs

* Make solving/proving consume the builder

* Remove use of cached builder
2026-02-01 11:54:09 -08:00
Rob Knight
879c7201ad
Fix parsing of intro statement templates inside custom predicates (#467)
* Fix parsing of intro statement templates inside custom predicates

* Tidy up comments
2026-01-30 10:30:57 -08:00
337a51135e
make hash_verifier_data_gadget and dummy pub (#464)
These two functions are used by the VDF Pod (previously known as PoW
Pod)
2026-01-28 16:08:22 +01:00
Rob Knight
48aa004ae5
Create multiple PODs where resource limits for a single POD are exceeded (#444)
* Create multiple PODs where resource limits for a single POD are exceeded

* HashSet -> BTreeSet determinism fix

* Fixed incorrect assignment of input PODs and added test

* Ensure only a single output POD

* Return error when reveal() called with unknown statement

* Use unreachable! for presumed-impossible cases

* Use assert_eq! rather than debug_assert_eq

* Use FIFO for topological sort

* Simplify bounds calculation

* Some more simplifications/comments

* Enforce dep_idx < idx invariant

* Incrementally solve rather than estimating slack

* Fix tests to correctly test dependencies between private and public statements

* More tidying

* Note possible optimisation of MainPodBuilder cloning of input PODs

* Fix tracking of total input POD count

* Refactor tests

* Formatting

* Small optimisation: use Vec in place of BTreeSet

* Account for automatically-inserted Contains statements

* Formatting

* Fix possible issue with copied statements

* Simplify result type given only a single result MainPod

* Remove unnecessary POD count estimate functionality

* Simplify dependency ordering and tracking

* Remove notion of multiple output PODs from solver

* Minor simplifications

* Use add_constraint instead of with

* Remove unnecessary check following assertion

* Fix handling of anchored keys given that Contains statements are not auto-inserted if they already exist

* Fix confusing dependency graph test

* Remove prove_order

* Fix deduplication and possible double-counting of public but not copied statements

* Reorder so that the output POD is the final POD

* Add more detailed tests

* Remove redundant tests

* Simplify POD counting

* More docs

* Flag more branches as unreachable

* Formatting

* Fix for changed custom batch parsing
2026-01-28 07:44:04 +01:00
Rob Knight
d1b7b4d37e
Improved predicate splitting (#445)
* Multi-batch splitting

* Invoke split predicates by name, passing in full argument list

* Reorder batches to prevent failure of forward references where possible

* Rename APIs for clarity

* Simplify example

* Add more docs

* Review updates

* Remove duplicate code

* Comment topological sort algorithm
2026-01-27 21:54:21 -08:00
9c9a2c454c
Feat/fst order pred part1 & part2 (#454)
Implement support for first order predicates in the backend.
Now a statement template can have a predicate hash or a wildcard.

## predicate <-> predicate hash constraints

To build the custom predicate table we need to calculate the custom predicate batch id, which uses the serialization of the statement templates before normalization.  This serialization uses the predicate hash when the template uses a predicate (instead of a wildcard).  Then in normalization we recalculate the predicate hash if it was a Batch Self.

This means that the relation between hash and predicate must be checked before and after normalization when the template is not using a wildcard.  How this is achieved:
- Before normalization: the constructor of StatementTmplTarget forces that if we keep a predicate, it's hash must be equal to the pred_hash when the template has a predicate (and not a wildcard)
- After normalization: the predicate hash is calculated in the normalization and replaced in the case of the template using a predicate and it being a BatchSelf.  If it was a predicate but not batch self, the old value was used which was constrained via the constructor.

See `CircuitBuilder::add_virtual_statement_tmpl` and `normalize_st_tmpl_circuit`

## Wildcard predicate resolution

It is done via `make_predicate_from_template_circuit` and is fairly simple as it's contains similar logic to `make_statement_arg_from_template_circuit` but simpler.
2026-01-20 13:14:22 +01:00
1724e7b146
feat: in MainPodBuilder track literal contains in dict_contains (#456)
The MainPodBuilder automatically adds contains statements for operations
that are created from entries.  But if the contains statement was
already added manually there will be duplicates.  We now track manually
added contains statements so that we don't generate duplicates when
adding statements from entries that use them.
2026-01-19 16:50:02 +01:00
0fca00cc93
Use predicate hash in statements instead of the literal predicate
Resolve #448 

Previously a predicate was 6 elements.  Now it grows to 8 elements; and the hash is 4 elements.

Some parts of the circuit require only require equality checks with the predicate: that works with the predicate hash.  Other parts require inspecting or working with particular elements in the predicate, those need the preimage of the predicate hash.
Both `StatementTarget` and `StatementTmplTarget` have been updated to include the predicate hash and optionally the predicate.  When the predicate is included, constraints are automatically generated for `pred_hash = hash(pred)`.  We only include the predicate when needed.
2026-01-19 11:02:11 +01:00
arnaucube
2eb1daeb92
[docs] Port the example that @ax0 told me months ago about a theorem-ish perspective on predicates, and updates the signature section adding a note (& references) on the current signature scheme. (#443)
* Port the example that @ax0 told me months ago about a theorem-ish perspective on predicates, and updates the signature section adding a note (& references) on the current signature scheme.

* apply review corrections from @ed255

* update example with @artwyman IsComposite suggestion & transitive_eq example
2026-01-07 16:24:56 +01:00
813a86c670
Remove max_depth in native MerkleTree (#442)
This simplifies the MerkleTree (and container) API.
Defer the max depth check when assigning the witness (merkle proof siblings) to the merkle tree circuit.

In this implementation the native Merkle Tree branches grow as much as they needed.  There are no checks of max depth in the merkle tree.  All keys are 256 bits (I added a debug_assert for this); so in the worst case a path will have depth 256.  It can't have a longer depth because the `insert` method calls `prove_nonexistence` which errors if the key already exists; another one may exist which must be different and thus require a path <= 256 depth. 

Resolve #436
2025-12-16 13:18:49 +01:00
Ahmad Afuni
32dc85471d
Add max input POD check to MainPodBuilder (#440) 2025-12-08 23:23:51 +10:00
Rob Knight
42f979c408
Frontend AST for Podlang (#432)
* Basic frontend AST and semantic validation

* Intro statement support

* Simplify validator lifetime

* Fix arity validation

* Lowering and splitting

* Remove legacy processor and use frontend AST by default

* Use builders instead of creating middleware types directly

* Typos/formatting

* Improve error messages when overflowing a batch due to splitting

* Add FromStr implementation for NativePredicate

* Remove 'raw' fields, and switch HashHex representation to byte vector rather than string

* Simpler wrapper types for batch and intro predicate hashes

* Parse secret and public keys to their respective data structures earlier

* More detail around string escape validity

* Simplify native predicate arity handling and move  method to NativePredicate impl

* Store hashes using middleware::Hash, and simplify lowering by using pre-parsed values

* Simplify predicate building

* Formatting

* Better error messages/suggestions for cases where predicate splitting fails

* Formatting

* Clippy fix

* Return error if we get a too-large int
2025-11-13 01:23:21 -08:00
c382bf487c
allow manually setting wildcard values (#438)
* allow manually setting wildcard values

* check for duplicate assignment
2025-10-27 11:31:21 +01:00
Rob Knight
aa4b531ac7
New 'use' syntax with support for intro predicates (#431)
* New 'use' syntax with support for intro predicates

* Use empty statement in test

* Review feedback
2025-10-17 03:27:11 -07:00
ffed5b4fbd
fix: remove dup arg in template builder for SetDelete (#430)
* fix: remove dup arg in template builder for SetDelete

* fix typo

* remove dbg
2025-10-01 16:10:24 +02:00
352b1fdac1
Allow creation of opaque custom predicate batch (#427)
This is useful for a verifier that wants to verify a MainPod that
contains a custom predicate in a public statement without
knowledge of how the custom predicate is defined.
2025-09-26 20:47:00 +10:00
Evan Laufer
5a80fba618
Add cases to desugar function for container update ops (#425)
* Add cases to desugar function for container update ops

* Make point hashable
2025-09-19 13:22:21 -07:00
Ahmad Afuni
6dcd17cc37
Update dependencies (#429) 2025-09-18 17:47:39 +10:00
Daniel Gulotta
26548cf612
Fix for #413 (#415) 2025-09-15 07:14:24 -07:00
1d14338351
make cache pub (#418) 2025-09-15 11:43:59 +02:00
Andrew Twyman
5de08da32c
Wildcards without the ? prefix (#422) 2025-09-12 13:08:17 -07:00
Andrew Twyman
7e04eb51ff
Optional dot syntax for anchored keys (#423) 2025-09-11 12:23:46 -07:00
Andrew Twyman
f95a27b412
Allow block comments with /* */ (#421) 2025-09-11 12:17:32 -07:00
Daniel Gulotta
03db60d94c
fix cargo doc warnings (#417) 2025-09-10 10:29:01 -07:00
Daniel Gulotta
c6c78304a9
remove unused file backends/plonky2/primitives/ec/gates/generic.rs (#416) 2025-09-10 07:49:35 -07:00
arnaucube
3d098c3ce6
update to latest plonky2 & plonky2-u32 versions in Cargo.toml (#414) 2025-09-09 10:50:51 +02:00
Andrew Twyman
a8a8734cd5
DictNotContains doesn't take a value arg (#412) 2025-09-08 01:19:43 -07:00
e1f8a9ad8b
Adjust default parameters (#406)
- Update formula in `estimate_verif_num_gates` after the update in the recursive verification from https://github.com/0xPARC/pod2/pull/397
- Update the parameters to get better utilization of 2^16 rows
- Update metrics report to be more compact
2025-09-08 10:14:12 +02:00
Daniel Gulotta
a24bbf7a3b
add container operation support to parser (#408) 2025-09-04 13:15:23 -07:00
Daniel Gulotta
b02d0ec462
allow downstream crates to instantiate *CircuitDataSerializer (#405) 2025-09-03 10:13:54 -07:00
Ahmad Afuni
1479400555
feat(backend): use custom gate for Schnorr signature verification (#397)
* schnorr signature prover optimization. TODO: implemented eval_unfiltered_circuit, serialize

* Use new gate(s) in MainPod circuit

* Formatting

* Clean-up

* Code review

* Code review

* Code review

* Formatting

* Remove unnecessary elements

---------

Co-authored-by: Linus Tang <linust@mit.edu>
2025-09-04 00:47:59 +10:00
ca97d9edc4
update docs with no-pod-id (#403)
* update docs with no-pod-id

* Update book/src/anchoredkeys.md

Co-authored-by: Ahmad Afuni <root@ahmadafuni.com>

* Update book/src/custompred.md

Co-authored-by: Ahmad Afuni <root@ahmadafuni.com>

* Update book/src/statements.md

Co-authored-by: Ahmad Afuni <root@ahmadafuni.com>

* Update book/src/statements.md

Co-authored-by: Ahmad Afuni <root@ahmadafuni.com>

---------

Co-authored-by: Ahmad Afuni <root@ahmadafuni.com>
2025-09-02 13:34:19 +02:00