123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619(*****************************************************************************)(* *)(* MIT License *)(* Copyright (c) 2022 Nomadic Labs <contact@nomadic-labs.com> *)(* *)(* Permission is hereby granted, free of charge, to any person obtaining a *)(* copy of this software and associated documentation files (the "Software"),*)(* to deal in the Software without restriction, including without limitation *)(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *)(* and/or sell copies of the Software, and to permit persons to whom the *)(* Software is furnished to do so, subject to the following conditions: *)(* *)(* The above copyright notice and this permission notice shall be included *)(* in all copies or substantial portions of the Software. *)(* *)(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *)(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *)(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *)(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *)(* DEALINGS IN THE SOFTWARE. *)(* *)(*****************************************************************************)moduleS=Csir.Scalar(** Plompiler core language.
The {!COMMON} module type defines the set of primitives needed to interpret
a Plompiler program.
This module type provides the monadic interface needed to deal with
Plompier computations, and a number of sub-modules to handle inputs
and basic types.
*)(** Numeric operations over the native field. *)moduletypeNUM=sig(** Element of the native scalar field. *)typescalar(** Representation of values. *)type'arepr(** Plompiler program. *)type'at(** [constant s] returns the constant value [s]. *)valconstant:S.t->scalarreprt(** [zero] returns the value 0. *)valzero:scalarreprt(** [one] returns the value 1. *)valone:scalarreprt(** [range_check ~nb_bits s] asserts that [s] is in the
range \[0, 2^nb_bits). *)valrange_check:nb_bits:int->scalarrepr->unitreprt(** [custom ~qc ~ql ~qr ~qo ~qm ~qx2b ~qx5a a b] returns a value [c]
for which the following arithmetic constraint is added:
[qc + ql * a + qr * b + qo * c + qm * a * b +
qx2b * b^2 + qx5a * a^5 = 0]
Manually adding constraints can be error-prone. Handle with care.
*)valcustom:?qc:S.t->?ql:S.t->?qr:S.t->?qo:S.t->?qm:S.t->?qx2b:S.t->?qx5a:S.t->scalarrepr->scalarrepr->scalarreprt(** [assert_custom ~qc ~ql ~qr ~qo ~qm a b c] asserts the
following arithmetic constraint:
[qc + ql * a + qr * b + qo * c + qm * a * b +
qx2b * b^2 + qx5a * a^5 = 0]
Manually adding constraints can be error-prone. Handle with care.
*)valassert_custom:?qc:S.t->?ql:S.t->?qr:S.t->?qo:S.t->?qm:S.t->scalarrepr->scalarrepr->scalarrepr->unitreprt(** [add ~qc ~ql ~qr a b] returns a value [c] such that
[ql * a + qr * b + qc = c].
*)valadd:?qc:S.t->?ql:S.t->?qr:S.t->scalarrepr->scalarrepr->scalarreprt(** [add_constant ~ql k a] returns a value [c] such that
[ql * a + k = c].
*)valadd_constant:?ql:S.t->S.t->scalarrepr->scalarreprt(** [mul ~qm a b] returns a value [c] such that
[qm * a * b = c].
*)valmul:?qm:S.t->scalarrepr->scalarrepr->scalarreprt(** [div ~den_coeff a b] asserts [b] is non-zero and returns a
value [c] such that [a / (b * den_coeff) = c].
*)valdiv:?den_coeff:S.t->scalarrepr->scalarrepr->scalarreprt(** [pow5 a] returns a value [c] such that [a^5 = c]. *)valpow5:scalarrepr->scalarreprt(** [is_zero a] returns a boolean [c] representing whether [a] is zero. *)valis_zero:scalarrepr->boolreprt(** [is_not_zero a] is the opposite of [is_zero a]. *)valis_not_zero:scalarrepr->boolreprt(** [assert_nonzero a] asserts that [a] is not zero. *)valassert_nonzero:scalarrepr->unitreprt(** [assert_bool a] asserts that [a] is either zero or one. *)valassert_bool:scalarrepr->unitreprtendmoduletypeBOOL=sig(** Element of the native scalar field. *)typescalar(** Representation of values. *)type'arepr(** Plompiler program. *)type'at(** [band a b] returns the conjunction of [a] and [b]. *)valband:boolrepr->boolrepr->boolreprt(** [xor a b] returns the exclusive disjunction of [a] and [b]. *)valxor:boolrepr->boolrepr->boolreprt(** [bor a b] returns the disjunction of [a] and [b]. *)valbor:boolrepr->boolrepr->boolreprt(** [bnot a] returns the negation of [a]. *)valbnot:boolrepr->boolreprt(** [ifthenelse c t e] returns [t] if [c] is true and [e] otherwise. *)valifthenelse:boolrepr->'arepr->'arepr->'areprt(** [swap c a b] returns the pair [(b, a)] if [c] is true and
[(a, b)] otherwise. *)valswap:boolrepr->'arepr->'arepr->('a*'a)reprt(** [assert_true a] asserts that [a] is true. *)valassert_true:boolrepr->unitreprt(** [assert_false a] asserts that [a] is false. *)valassert_false:boolrepr->unitreprt(** [constant kb] returns the constant [kb] as a Plompiler value. *)valconstant:bool->boolreprt(** [band_list bs] returns the conjunction of the list of booleans
[bs]. *)valband_list:boolreprlist->boolreprtmoduleInternal:sigvalbor_lookup:boolrepr->boolrepr->boolreprtvalxor_lookup:boolrepr->boolrepr->boolreprtvalband_lookup:boolrepr->boolrepr->boolreprtvalbnot_lookup:boolrepr->boolreprtendendmoduletypeCOMMON=sig(** Element of the native scalar field. *)typescalar(** Inputs to a plompiler program have three kinds:
{ul
{li Public: known by both the prover and verifier.}
{li Private: known only by the prover.}
{li InputCom: part of an Input Commitment.
See {!Lib_plonk.Input_commitment}.}
}
*)typeinput_kind=[`InputCom|`Public|`Private](** The trace is the sequence of scalar values used in a Plompiler
program. It includes the inputs and the intermediary variables.
Inputs have to be a prefix of the trace, and public inputs come
before private ones.
*)typetrace_kind=[input_kind|`NoInput](** Representation of values. *)type'arepr(** Plompiler program. *)type'at(** Monadic return. *)valret:'a->'at(** Monadic bind. *)val(let*):'at->('a->'bt)->'bt(** Monadic sequence operator. *)val(>*):unitreprt->'at->'at(** Infix map operator. *)val(<$>):'at->('a->'b)->'bt(* Add a boolean check *)(** [with_bool_check c] adds an implicit boolean check computed by [c]
to the circuit.
The computation of this check is delayed until the end of the circuit
construction, which is useful for defining complex conditions while
still processing inputs.
*)valwith_bool_check:boolreprt->unitreprt(** [get_checks_wire] retrieves the boolean representing the conjunction
of all previous implicit checks.
WARNING: This will "reset" the implicit check accumulator.
*)valget_checks_wire:boolreprt(** Module for describing inputs to Plompiler circuits. *)moduleInput:sig(** Input of type ['a] to a Plompiler program.
These hold a value of type ['a] with some additional structure used
for constructing the circuit.
Often, after implementing a Plompiler program, one is left with a
function of the shape:
[val prog : 'a input -> 'b input -> unit repr t].
If the user only wants to execute the program in order to compute
the corresponding circuit, but without producing a proof, then
the [input]s can be set to dummy values, as they will be ignored.
That is, only the structure of the [input] is used for building
the circuit.
*)type'ainput(** [scalar s] describes a scalar input holding the value [s]. *)valscalar:S.t->scalarinput(** [to_scalar i] retrieves the value from a scalar input. *)valto_scalar:scalarinput->S.t(** [bool b] describes a boolean input holding the value [b]. *)valbool:bool->boolinput(** [to_bool i] retrieves the value from a boolean input. *)valto_bool:boolinput->bool(** [unit] describes a unit input. *)valunit:unitinput(** [pair a b] describes the input tuple [(a, b)] made out
of inputs [a] and [b]. *)valpair:'ainput->'binput->('a*'b)input(** [to_pair p] retrieves the inputs [(a, b)] that make
up the input tuple [p]. *)valto_pair:('a*'b)input->'ainput*'binput(** [list l] turns a list of inputs [l] into an list input. *)vallist:'ainputlist->'alistinput(** [to_list li] turns a list input [li] into a list of inputs. *)valto_list:'alistinput->'ainputlist(** [with_implicit_bool_check bc i] attaches an implicit bool
check [bc] to the input [i]. *)valwith_implicit_bool_check:('arepr->boolreprt)->'ainput->'ainput(** [with_assertion assrtn i] attaches an assertion [assrtn]
to the input [i]. *)valwith_assertion:('arepr->unitreprt)->'ainput->'ainputtype'at='ainputend(** Type that describes an open input commitment. *)type'bopen_input_com(** [serialize i] returns the array of scalars corresponding to its values. *)valserialize:'aInput.t->S.tarray(** [input ~kind i] declares an input of a given [kind] to the Plompiler
program. It returns a Plompiler representation of the inputted value. *)valinput:?kind:input_kind->'aInput.t->'areprt(** [begin_input_com builder] starts a new input commitment.
[builder] is a function that takes the inputs to be committed one
by one and returns the composite commitment.
An example of usage is
{[
let* x1, x2 =
begin_input_com (fun a b -> (a, b))
|: Input.scalar x1 |: Input.scalar x2 |> end_input_com
in
]}
*)valbegin_input_com:'b->'bopen_input_com(** [ic |: i] adds [i] to the input commitment [ic] *)val(|:):('crepr->'d)open_input_com->'cInput.t->'dopen_input_com(** [end_input_com ic] ends the declaration of an input commitment. *)valend_input_com:'aopen_input_com->'at(** [eq a b] returns the physical equality of [a] and [b].
Handle with care. *)valeq:'arepr->'arepr->bool(** Monadic fold over a list. *)valfoldM:('a->'b->'at)->'a->'blist->'at(** [pair x y] makes a pair value out of two values. *)valpair:'arepr->'brepr->('a*'b)repr(** [of_pair p] retrieves the values out of a pair value. *)valof_pair:('a*'b)repr->'arepr*'brepr(** [to_list l] makes a list value out of a list of values. *)valto_list:'areprlist->'alistrepr(** [of_list v] retrieves a list of Plompiler values out of
a list value. *)valof_list:'alistrepr->'areprlist(** [hd l] returns the head of list [l] *)valhd:'alistrepr->'areprt(** [unit] is the unit value. *)valunit:unitrepr(** [scalar_of_bool b] converts a boolean value into a scalar. *)valscalar_of_bool:boolrepr->scalarrepr(** [unsafe_bool_of_scalar s] converts a scalar value into a bool.
WARNING: [s] is expected to hold the values 0 or 1, but this
is not checked. *)valunsafe_bool_of_scalar:scalarrepr->boolrepr(** Assertion that two values are (structurally) equal. *)valassert_equal:'arepr->'arepr->unitreprt(** [equal a b] computes the structural equality between [a] and [b]. *)valequal:'arepr->'arepr->boolreprtvalscalar_of_limbs:nb_bits:int->scalarlistrepr->scalarreprt(** Returns a list of Boolean variables representing the little endian
bit decomposition of the given scalar (with the least significant bit
on the head). *)valbits_of_scalar:?shift:Z.t->nb_bits:int->scalarrepr->boollistreprtvallimbs_of_scalar:?shift:Z.t->total_nb_bits:int->nb_bits:int->scalarrepr->scalarlistreprt(** [with_label ~label c] adds a [label] to the Plompiler computation
[c]. Useful for debugging and flamegraphs. *)valwith_label:label:string->'at->'at(** Prints on stdout the prefix string and the repr. It works only when
running the Result interpreter, it has no effect in the Circuit
interpreter. *)valdebug:string->'arepr->unitreprtmoduleNum:NUMwithtypescalar=scalarandtype'arepr='areprandtype'at='atmoduleBool:BOOLwithtypescalar=scalarandtype'arepr='areprandtype'at='at(** Module for describing operations over fixed size integers *)moduleLimb(N:sigvalnb_bits:intend):sig(** [xor_lookup a b] returns the exclusive disjunction of [a] and [b].
This primitive uses a precomputed lookup table called "xor" ^ [nb_bits].
*)valxor_lookup:scalarrepr->scalarrepr->scalarreprt(** [band_lookup a b] returns the conjunction of [a] and [b].
This primitive uses a precomputed lookup table called "band" ^ [nb_bits].
*)valband_lookup:scalarrepr->scalarrepr->scalarreprt(** [bnot_lookup b] returns the negation of [b].
This primitive uses a precomputed lookup table called "bnot" ^ [nb_bits].
*)valbnot_lookup:scalarrepr->scalarreprt(** [rotate_right_lookup x y i] returns the low [nb_bits] of
[rotate_right (x + y * 2 ^ nb_bits) i] where [0 < i < nb_bits].
This primitive uses a precomputed lookup table called
"rotate_right" ^ [nb_bits] ^ "_" ^ [i].
*)valrotate_right_lookup:scalarrepr->scalarrepr->int->scalarreprtend(** Addition on ECC curves. *)moduleEcc:sig(** [weierstrass_add (px, py) (qx, qy)] returns a pair [(rx, ry)]
representing point addition over the Jubjub curve in Weierstrass
coordinates of the given input points. Namely, it enforces constraints
[rx = λ² - (px + qx)] and [ry = λ * (px - rx) - py],
where [λ := (qy - py) / (qx - px)].
*)valweierstrass_add:(scalar*scalar)repr->(scalar*scalar)repr->(scalar*scalar)reprt(** [edwards_add (px, py) (qx, qy)] returns a pair [(rx, ry)]
representing point addition over the Jubjub curve in Edwards
coordinates of the given input points. Namely, it enforces constraints
[rx = (px * qy + qx * py) / (1 + d * px * py * qx * qy)] and
[ry = (py * qy - a * px * qx) / (1 - d * px * py * qx * qy)]
where [a := -1] and [d] are fixed parameters of the Jubjub curve
in this representation. See {!Lib_plonk.Ecc_gates}.
*)valedwards_add:(scalar*scalar)repr->(scalar*scalar)repr->(scalar*scalar)reprt(** [edwards_cond_add p q b] returns [edwards_add p q] if [b] is true and
[p] otherwise.
*)valedwards_cond_add:(scalar*scalar)repr->(scalar*scalar)repr->boolrepr->(scalar*scalar)reprtend(* See [lib_plompiler/gadget_mod_arith.ml] for documentation on mod_arith *)moduleMod_arith:sigvaladd:?subtraction:bool->label:string->modulus:Z.t->nb_limbs:int->base:Z.t->moduli:Z.tlist->qm_bound:Z.t*Z.t->ts_bounds:(Z.t*Z.t)list->scalarlistrepr->scalarlistrepr->scalarlistreprtvalmul:?division:bool->label:string->modulus:Z.t->nb_limbs:int->base:Z.t->moduli:Z.tlist->qm_bound:Z.t*Z.t->ts_bounds:(Z.t*Z.t)list->scalarlistrepr->scalarlistrepr->scalarlistreprtvalassert_non_zero:label:string->modulus:Z.t->is_prime:bool->nb_limbs:int->base:Z.t->moduli:Z.tlist->qm_bound:Z.t*Z.t->ts_bounds:(Z.t*Z.t)list->scalarlistrepr->unitreprtvalis_zero:label:string->modulus:Z.t->is_prime:bool->nb_limbs:int->base:Z.t->moduli:Z.tlist->qm_bound:Z.t*Z.t->ts_bounds:(Z.t*Z.t)list->scalarlistrepr->boolreprtend(** Helper functions for the Poseidon Hash defined over the scalar
field of the BLS12-381 curve, using S-box function [x -> x^5]. *)modulePoseidon:sig(** [poseidon128_full_round ~matrix ~k (x0, y0, z0)] returns
[\[x1; y1; z1\]] where [(x1, y1, z1)] is the result of applying
a (shifted) Poseidon full round (parametrized by [matrix] and [k]) to
the 3-registers state [(x0, y0, z0)].
Here, [matrix] is a 3 x 3 matrix and [k] is a vector of 3 elements.
Note that this is a shifted round, that is, the S-box is applied first,
followed by the linear layer. Namely:
[(x1, y1, z1) = matrix * (x0^5, y0^5, z0^5) + k].
*)valposeidon128_full_round:matrix:S.tarrayarray->k:S.tarray->scalarrepr*scalarrepr*scalarrepr->scalarlistreprt(** [poseidon128_four_partial_rounds ~matrix ~k (x0, y0, z0)] returns
[\[x4; y4; z4\]] where [(x4, y4, z4)] is the result of applying
four (shifted) Poseidon partial round (parametrized by [matrix] and
[ks]) to the 3-registers state [(x0, y0, z0)].
Here, [matrix] is a 3 x 3 matrix and [ks] is an array of 4 vectors of
3 elements each (one vector for each of the 4 rounds).
In particular, for i = 1,...,4:
[(xi, yi, zi) = matrix * (x_{i-1}, y_{i-1}, z_{i-1}^5) + ki].
*)valposeidon128_four_partial_rounds:matrix:S.tarrayarray->ks:S.tarrayarray->scalarrepr*scalarrepr*scalarrepr->scalarlistreprtend(** Helper functions for the Anemoi Hash defined over the scalar
field of the BLS12-381 curve. *)moduleAnemoi:sig(** [anemoi_round ~kx ~ky (x0, y0)] returns [(x1, y1)], the result of
applying an Anemoi round (parametrized by [kx] and [ky]) to the
2-registers state [(x0, y0)].
In particular,
[x1 = u + kx + 7 * (v + ky)] and [y1 = 7 * (u + kx) + 50 * (v + ky)]
where [(u, v) = S-BOX(x0, y0)] defined as:
[u := t + beta * (y0 - t^(1/5))^2 + delta] and [v := y0 - t^(1/5)]
where [t := x0 - beta * y0^2 - gamma] and [beta], [gamma], [delta]
are system parameters.
*)valanemoi_round:kx:S.t->ky:S.t->scalarrepr*scalarrepr->(scalar*scalar)reprt(** [anemoi_double_round ~kx1 ~ky1 ~kx2 ~ky2 (x0, y0)] returns [(x2, y2)],
the result of applying two Anemoi rounds.
In particular, it is equivalent to
[anemoi_round ~kx:kx2 ~ky:ky2 (anemoi_round ~kx:kx1 ~ky:ky1 (x0, y0))],
but models the necessary constraints with 5 PlonK rows instead of 8.
(Note that [anemoi_round] requires 4 PlonK rows.)
*)valanemoi_double_round:kx1:S.t->ky1:S.t->kx2:S.t->ky2:S.t->scalarrepr*scalarrepr->(scalar*scalar)reprt(** [anemoi_custom ~kx1 ~ky1 ~kx2 ~ky2 (x0, y0)] returns [(x2, y2)], the
result of applying two Anemoi rounds.
In particular, it is equivalent to
[anemoi_round ~kx:kx2 ~ky:ky2 (anemoi_round ~kx:kx1 ~ky:ky1 (x0, y0))],
but models the necessary constraints with 2 Plonk rows.
This is possible thanks to our custom gate for Anemoi double rounds.
See {!Lib_plonk.Hash_gates}. Furthermore, the second row is "compatible"
with the one after if another Anemoi round follows this one.
(Our optimizer would combine such rows in that case).
*)valanemoi_custom:kx1:S.t->ky1:S.t->kx2:S.t->ky2:S.t->scalarrepr*scalarrepr->(scalar*scalar)reprtendend