sigma-compiler by Ian Goldberg, [email protected]
Version 0.1.0, 2025-10-10
This crate provides the sigma_compiler! macro as an easy interface
to the sigma-proofs API
for non-interactive zero-knowledge sigma protocols.
The general form of this macro is:
sigma_compiler! { proto_name<Grp>,
(scalar_list),
(point_list),
statement_1,
statement_2,
...
}
The pieces are as follows:
proto_name: The name of the protocol. A Rust submodule will be created with this name, containing all of the data structures and code associated with this sigma protocol.<Grp>: an optional indication of the mathematical group to use (a set ofPoints and associatedScalars) for this sigma protocol. The group must implement thePrimeGrouptrait. If<Grp>is omitted, it defaults to assuming there is a group calledGin the current scope.scalar_listis a list of variables representingScalars. Each variable can be optionally tagged with one or more of the tagspub,rand, orvec. The tagspubandrandcannot both be used on the same variable.pubmeans that theScalaris public; this can be used for public parameters to the protocol, such as the limits of ranges, or other constants that can appear in the statements. AnyScalarnot marked aspubis assumed to be private to the prover, and the verifier will learn nothing about that value other than what is implied by the truth of the statements.randmeans that theScalaris a uniform randomScalar. ArandScalarmust be used only once in the statements. These are typically used as randomizers in Pedersen commitments.vecmeans that the variable represents a vector ofScalars, as opposed to a singleScalar. The number of entries in the vector can be set at runtime, when the sigma protocol is executed.
point_listis a list of variables representingPoints. AllPointvariables are considered public. Each variable can be optionally tagged with one or more of the tagscind,const, orvec. All combinations of tags are valid.- All
Points tagged withcindare computationally independent. This means that the prover does not know a discrete logarithm relationship between any of them. Formally,P1,P2, ...,Pnbeing computationally independentPoints means that if the prover knowsScalarss1,s2, ...,snsuch thats1*P1 + s2*P2 + ... + sn*Pn = 0(where0is the identity element of the group), then it must be the case that each ofs1,s2, ...,snis the zeroScalar(modulo the order of the group). Typically, these elements would be generators of the group, generated with a hash-to-group function, as opposed to multiplying the standard generator by a random number (at least not one known by the prover).Points markedcindare typically the bases used in Pedersen commitments. constmeans that the value of thePointwill always be the same for each invocation of the sigma protocol. This is typical for fixed generators, but possibly otherPoints as well.vecmeans that the variable represents a vector ofPoints, as opposed to a singlePoint. The number of entries in the vector can be set at runtime, when the sigma protocol is executed.
- All
- Each
statementis a statement that the prover is proving the truth of to the verifier. Each statement can have one of the following forms:-
C = arith_expr, whereCis a variable representing aPoint, andarith_expris an arithmetic expression evaluating to aPoint. This is a linear combination statement. An arithmetic expression can consist of:ScalarorPointvariables- integer constants
- the operations
*,+,-(binary or unary) - the operation
<<where both operands are expressions with no variables - the function
sumthat takes a single vector argument and returns the sum of its elements - parens
You cannot multiply together two private subexpressions, and you cannot multiply together two subexpressions that both evaluate to
Points. You cannot add aPointto aScalar. Integer constants are consideredScalars, but all arithmetic subexpressions involving only constants must have values that fit in ani128.If any variable in
arith_expris markedvec, then this is a vector expression, andCmust also be markedvec. The statement is considered to hold in 'SIMD' style; that is, the lengths of all of the vector variables involved in the statement must be the same, and the statement is proven to hold component-wise. Any non-vector variable in the statement is considered equivalent to a vector variable, all of whose entries have the same value. Note that you can do a dot product between two vectorsxandAwithsum(x*A).As an extension, you can also use an arithmetic expression evaluating to a public
Pointin place ofCon the left side of the=. For example, ifais aScalartaggedpub, andCis aPoint, then the expression(2*a+1)*C = arith_expris a valid linear combination statement. -
a = arith_expr, whereais a variable representing a privateScalar. This is a substitution statement. Its meaning is to say that the privateScalarahas the value given by the arithmetic expression, which must evaluate to aScalar. The effect is to substituteaanywhere it appears in the list of statements (including the right side of other substitutions) with the given expression. The expression must not contain the variableaitself, either directly, or after other substitutions. For example, the statementa = a + bis not allowed, nor is the combination of substitutionsa = b + 1, b = c + 2, c = 2*a. -
a = arith_expr, whereais a variable representing a publicScalar. This is a public Scalar equality statement. Its meaning is to say that the publicScalarahas the value given by the arithmetic expression, which must evaluate to a publicScalar. The statement is simply removed from the list of statements to be proven in the zero-knowledge sigma protocol, and code is emitted for the prover and verifier to each just check that the statement is satisfied. Currently, there can be no vector variables in this kind of statement. -
(a..b).contains(x), whereaandbare constants or publicScalars (or arithmetic expressions evaluating to publicScalars), andxis a privateScalar, possibly multiplied by a constant and adding or subtracting an expression evaluating to a publicScalar. For example,((a+2)..(3*b-7)).contains(2*x+2*c*c+12)is allowed, ifa,b, andcare publicScalars andxis a privateScalar.(a..b).contains(x)is a range statement, and it means thatxlies in the rangea..b. As usual in Rust, the rangea..bincludesa, but excludesb. If you want to include both endpoints, you can also use the usual Rust notationa..=b. The size of the range must fit in ani128. -
x != a, wherexis a privateScalar, possibly multiplied by a constant and adding or subtracting an expression evaluating to a publicScalar, andais a constant or publicScalar(or an arithmetic expression evaluating to a publicScalar). For example,2*x+2*c*c+12 != a*b+17is allowed, ifa,b, andcare publicScalars andxis a privateScalar.x != 0is a more typical example. This is a not-equals statement, and it means that the value of the expression on the left is not equal to the value of the expression on the right.
-
- Statements can also be combined with
AND(st1,st2,...,stn)andOR(st1,st2,...,stn). The list of statements in the macro invocation are implicitly put into a top-levelAND.ANDs andORs can be arbitrarily nested. As usual, anANDstatement is true when all of its component statements are true; anORstatement is true when at least one of its component statements is true.
The macro creates a submodule with the name specified by
proto_name. This module contains:
- A struct
Instancecontaining all thePoints and publicScalars specified in the macro invocation. Any public vectorScalarorPointvariable will be represented as aVec<Scalar>orVec<Point>respectively. - A struct
Witnesscontaining all the privateScalars specified in the macro invocation. Any private vector variable will be represented as aVec<Scalar>. - A function
provewith the signatureThe parameterpub fn prove( instance: &Instance, witness: &Witness, session_id: &[u8], rng: &mut (impl CryptoRng + RngCore), ) -> sigma_proofs::errors::Result<Vec<u8>>instancecontains the public variables (also known to the verifier). The parameterwitnesscontains the private variables known only to the prover. The parametersession_idcan be any byte slice; the proof is bound to this byte slice, and the verifier must use the same byte slice in order to verify the proof. The parameterrngis a random number generator that implements theCryptoRngandRngCoretraits. The output, if successful, is the proof as a byte vector. - A function
verifywith the signatureThe parameterpub fn verify( instance: &Instance, proof: &[u8], session_id: &[u8], ) -> sigma_proofs::errors::Result<()>instancecontains the public variables, and must be the same as passed to theprovefunction. The parameterproofcontains the output of theprovefunction. The parametersession_idmust be the same as passed to theprovefunction. IfverifyreturnsOk(()), then the verifier can assume that the prover did know aWitnessstruct for which the statements (with public values specified by the givenInstancestruct) are all true, but the verifier does not learn any other information about thatWitnessstruct.