* Merge changes to docs * Fix typo * Correct SUMMARY so it compiles; update .gitignore * Clean up statements.md Make syntax and notation consistent with Rust source code. * Fix statements for Merkle trees and compound types * First draft of custom statements and small updates to signedpod.md * Update book/src/merkletree.md Co-authored-by: Ahmad Afuni <root@ahmadafuni.com> * merklestatements correct typo Co-authored-by: Ahmad Afuni <root@ahmadafuni.com> * add todo for gadget ids Co-authored-by: Ahmad Afuni <root@ahmadafuni.com> * Separate out custom statements version 1 * More details on custom statements version 1 * new file custom2 * Partial draft of version 2 * First draft of version 2 spec, it's kind of a mess * Another version of the custom predicates spec * Update book/src/custom2.md Co-authored-by: Eduard S. <eduardsanou@posteo.net> * Simple example of deduction rule applied in circuit * Implement Edu's comments on custom predicates * Backend predicates must be defined in groups * Add more examples * Two diff statements using same constant * Remove deprecated example --------- Co-authored-by: Ahmad Afuni <root@ahmadafuni.com> Co-authored-by: Eduard S. <eduardsanou@posteo.net>
4 KiB
Custom operations (or: how to define a custom predicate): VERSION 1
(Note: At the moment, we consider a "custom operation" to be exactly the same thing as the "definition of a custom predicate.")
A custom operation 1 is a rule that allows one to deduce a custom statement from one or more existing statements according to a logical rule, described below.
Note: Unlike built-in operations, it is not possible to perform arbitrary calculations inside a custom operation.
The syntax of a custom operation is best explained with an example.
Original example with anchored keys, origins, and keys.
| Args | Condition | Output |
|---|---|---|
| pod: Origin, good_boy_issuers: AnchoredKey::MerkleRoot, receiver: AnchoredKey |
ValueOf(AK(pod, "_type"), SIGNATURE), Contains(good_boy_issuers, AK(pod,"_signer")), Equals(AK(pod, "friend"), receiver) |
GoodBoy(receiver, good_boy_issuers) |
Compiled example with only origins and keys.
| Args | Condition | Output |
|---|---|---|
| pod: Origin, good_boy_issuers_origin: Origin, good_boy_issuers_key: Key::MerkleRoot, receiver_origin: Origin, receiver_key: Key |
ValueOf(AK(pod, "_type"), SIGNATURE), Contains(AK(good_boy_issuers_origin, good_boy_issuers_key), AK(pod,"_signer")), Equals(AK(pod, "friend"), AK(receiver_origin, receiver_key)) |
GoodBoy(AK(receiver_origin, receiver_key), AK(good_boy_issuers_origin, good_boy_issuers_key)) |
A custom operation accepts as input a number of statements (the Condition);
each statement has a number of arguments, which may be constants or anchored keys; and an anchored key in turn can optionally be decomposed as a pair of an Origin and a Key.
In the "original example" above, the anchored keys good_boy_issuers and receiver are not broken down, but AK(pod, "_type"), AK(pod, "_signer"), AK(pod, "friend") are. The purpose of breaking them down, in this case, is to force the three anchored keys to come from the same pod.
In the "compiled example", all the anchored keys have been broken down into origins and keys.
In general, in the front-end language, the "arguments" to an operation define a list of identifiers with types. Every statement in the "condition" must have valid arguments of the correct types: either constants, or identifiers defined in the "arguments".
In order to apply the operation, the user who wants to create a POD must give acceptable values for all the arguments. The POD prover will substitute those values for all the statements in the "Condition" and check that all substituted statements previously appear in the POD. If this check passes, the output statement is then a valid statement.
What applying the operation looks like on the back end
On the back end the "compiled example" deduction rule is converted to a sort of "template":
| Args | Condition | Output |
|---|---|---|
| *1 (pod), *2 (good_boy_issuers_origin), *3 (good_boy_issuers_key), *4 (receiver_origin), *5 (receiver_key) |
ValueOf(AK(*1, "_type"), SIGNATURE), Contains(AK(*2, *3), AK(*1,"_signer")), Equals(AK(*1, "friend"), AK(*4, *5)) |
GoodBoy(AK(*4, *5), AK(*2, *3)) |
If you want to apply this deduction rule to prove a GoodBoy statement,
you have to provide the following witnesses in-circuit.
- Copy of the deduction rule
- Values for *1, *2, *3, *4, *5.
- Copy of the three statements in the deduction rule with *1, *2, *3, *4, *5 filled in
- Indices of the three statements
ValueOf,Contains,Equalsin the list of previous statements.
And the circuit will verify:
- *1, *2, *3, *4, *5 were correctly substituted into the statements
- The three statements
ValueOf,Contains,Equalsdo indeed appear at the claimed indices.
-
In previous versions of these docs, "operations" were called "deduction rules". ↩︎