Prove you have a pre image to a hash function
Prove your account has enough monies for a transaction
Prove you are part of a group without revealing your identity
Prove who won, without revealing any of the bids
For some cryptographic schemes this also works for encrypted values
PRIVACY
A relation between 2 group elements that gives an output, that has this algebraic relation to the inputs
Text
Verifier generates circuit representation and shares
Prover evaluates the constraints
for the function homomorphically and obtains the output and witness
Verifier verifies using pairing on the homomorphic output to verify the witness
Prove you evaluated $$ m^2 + 36 - n $$
correctly with $$ m = 2, n = 3 $$
type Name = Text
data BinOp
= BMul
| BAdd
| BSub
data Expr
= Var Name
| Lit Rational
| BinOp BinOp Expr Expr
instance Num Expr where
(*) = BinOp BMul
(+) = BinOp BAdd
(-) = BinOp BSub
fromInteger = Lit . fromInteger
abs = notImplemented
signum = notImplemented
task1 = (Var "m") * (Var "m") + 36 - (Var "n")
Convert \(m^2 + 36 - n \) so it only contains commands in the form
data Gate
= GAdd
| GSub
| GMul deriving (Show)
data Constraint
= CSet Name (Either Name Rational)
| CBinOp {
gate :: Gate
, output :: Name
, input1 :: Name
, input2 :: Name
} deriving (Show)
data ConstraintSystem
= ConstraintSystem {
supply :: Int
, cs :: [Constraint]
} deriving (Show)
type Builder a = State ConstraintSystem a
runBuilder :: Builder a -> ConstraintSystem
runBuilder m = execState m initCS
where
initCS = ConstraintSystem { supply = 0, cs = []}
names :: [Text]
names = [1 ..] >>= flip replicateM ['a' .. 'z'] >>= return . toS
fresh :: Builder Name
fresh = do
v <- gets supply
modify $ \s -> s { supply = v + 1 }
pure $ names `unsafeIndex` v
emit :: Constraint -> Builder ()
emit c = modify (\s -> s { cs = c : cs s })
compile :: Name -> Expr -> Builder ()
compile output expr = case expr of
Var nm ->
emit $ CSet output (Left nm)
Lit v ->
emit $ CSet output (Right v)
BinOp op e1 e2 -> do
input1 <- compile' e1
input2 <- compile' e2
let gop = case op of
BMul -> GMul
BAdd -> GAdd
BSub -> GSub
emit $ CBinOp gop output input1 input2
compile' :: Expr -> Builder Name
compile' (Var nm) = return nm
compile' e = do
output <- fresh
compile output e
return output
> constraints task1
b = m * m
c = 36
a = b + c
result = a - n
And with the help of some pretty instances
We can represent each one of our constraints
as a set of 3 polynomials
We are going to construct a clever polynomial that only is 0 on correct inputs
Each constraint can be represented as a relation between vectors.
data R1C
= R1C {
v :: [Rational]
, w :: [Rational]
, y :: [Rational]
} deriving (Show)
newtype R1CS = R1CS [R1C] deriving (Show)
uses :: Name -> [Rational]
uses var = fmap (bool 0 1 . (==var)) vars
toR1C :: Constraint -> R1C
toR1C c = case c of
CBinOp GMul output input1 input2 -> R1C {
v = uses input1
, w = uses input2
, y = uses output
}
(cont.)
uses :: Name -> [Rational]
uses var = fmap (bool 0 1 . (==var)) vars
toR1C :: Constraint -> R1C
toR1C c = case c of
CBinOp GMul output input1 input2 -> R1C {
v = uses input1
, w = uses input2
, y = uses output
}
+ CSet output (Right value) -> R1C {
+ v = zipWith (+)
+ (uses output)
+ (fmap (bool 0 (-value) . (=="one")) vars)
+ , w = [1]
+ , y = [0]
+ }
(cont.)
uses :: Name -> [Rational]
uses var = fmap (bool 0 1 . (==var)) vars
toR1C :: Constraint -> R1C
toR1C c = case c of
CBinOp GMul output input1 input2 -> R1C {
v = uses input1
, w = uses input2
, y = uses output
}
CSet output (Right value) -> R1C {
v = zipWith (+)
(uses output)
(fmap (bool 0 (-value) . (=="one")) vars)
, w = [1]
, y = [0]
}
+ CBinOp GAdd output input1 input2 -> R1C {
+ v = zipWith (+) (uses input1) (uses input2)
+ , w = [1]
+ , y = uses output
+ }
(cont.)
...
CSet output (Right value) -> R1C {
v = zipWith (+)
(uses output)
(fmap (bool 0 (-value) . (=="one")) vars)
, w = [1]
, y = [0]
}
CBinOp GAdd output input1 input2 -> R1C {
v = zipWith (+) (uses input1) (uses input2)
, w = [1]
, y = uses output
}
+ CBinOp GSub output input1 input2 -> R1C {
+ v = zipWith (+)
+ (uses input1)
+ (fmap (bool 0 (-1) . (==input2)) vars)
+ , w = [1]
+ , y = uses output
+ }
> r1cs $ constraints task1
b = m * m
==================
v = [0,1,0,0,0,0,0]
w = [0,1,0,0,0,0,0]
y = [0,0,0,0,0,1,0]
c = 36
==================
v = [-36,0,0,0,0,0,1]
w = [1]
y = [0]
a = b + c
==================
v = [0,0,0,0,0,1,1]
w = [1]
y = [0,0,0,0,1,0,0]
result = a - n
==================
v = [0,0,-1,0,1,0,0]
w = [1]
y = [0,0,0,1,0,0,0]
The witness is the assignment to all variables.
Execute the flattened commands and set the values as you go
Initial state ( with our inputs )
satisfy :: [Rational] -> R1C -> Bool
satisfy s (R1C v w y) = (sv * sw) - sy == 0
where
sv = dot s v
sw = dot s w
sy = dot s y
dot a b = sum (zipWith (*) a b)
> fmap (satisfy witness) (r1cs $ constraints task1)
[True,True,True,True]
Given a set of points, give me a polynomial that passes through all of those points.
Thanks to this we can check
the witness in a single step
...
data BinOp
= BMul
| BAdd
| BSub
+ | BAnd
+ | BOr
+ | BXor
...
compile output expr = case expr of
...
BinOp op e1 e2 -> do
input1 <- compile' e1
input2 <- compile' e2
binOp op output input1 input2
binOp :: BinOp -> Name -> Name -> Name -> Builder ()
binOp BAnd out input1 input2 = binOp BMul out input1 input2
binOp BOr out input1 input2 = do
tmp1 <- fresh
tmp2 <- fresh
binOp BMul tmp1 input1 input2
binOp BAdd tmp2 input1 input2
binOp BSub out tmp2 tmp1
binOp BXor out input1 input2 = do
tmp1 <- fresh
binOp BSub tmp1 input1 input2
binOp BMul out tmp1 tmp1
binOp op output input1 input2 = emit $ CBinOp gate output input1 input2
where
gate =
case op of
BMul -> GMul
BAdd -> GAdd
BSub -> GSub
All our arithmetic operations would be not be done with regular numbers.
Instead it'd be
elliptic curve elements
over a
finite field with a prime modulus.
A \( (x, y) \) coordinate on a curve that looks like
Instead of being a real curve, you have a set of discrete points.
( Barreto-Naehrig Curve: BN128 )
The final verification step, relates the circuit output, the witness, and the circuit description, checked without having access the the private inputs.
It's computationally infeasible to construct a forged proof