module Test.QAP where
import Protolude
import Data.Map (Map)
import qualified Data.Map as Map
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck
import Circuit.Affine
import Circuit.Arithmetic
import Data.Pairing.BN254 (Fr, getRootOfUnity)
import QAP
import Test.Circuit.Affine (arbAffineCircuit, arbInputVector)
-------------------------------------------------------------------------------
-- Generators
-------------------------------------------------------------------------------
arbGate
:: Arbitrary f
=> Int -> Gen (Gate Wire f)
arbGate numVars
= oneof
[ Mul <$> (mapVarsAffine InputWire <$> sized (arbAffineCircuit numVars))
<*> (mapVarsAffine InputWire <$> sized (arbAffineCircuit numVars))
<*> pure (OutputWire 0)
, Equal <$> (InputWire <$> choose (0, numVars - 1))
<*> pure (IntermediateWire 0)
<*> pure (OutputWire 0)
]
data GateWithInputs f = GateWithInputs (Gate Wire f) [Map Int f]
deriving Show
instance Arbitrary f => Arbitrary (GateWithInputs f) where
arbitrary = do
numVars <- (+1) . abs <$> arbitrary
program <- scale (`div` 7) $ arbGate numVars
inputs <- vectorOf 10 (arbInputVector numVars)
pure $ GateWithInputs program inputs
-------------------------------------------------------------------------------
-- Test values
-------------------------------------------------------------------------------
testArithCircuit :: ArithCircuit Fr
testArithCircuit
= ArithCircuit
[ Mul (Var (InputWire 0)) (Var (InputWire 1)) (IntermediateWire 0)
, Mul (Var (InputWire 2)) (Var (InputWire 3)) (IntermediateWire 1)
, Mul (Add (ConstGate 10) (Var (IntermediateWire 0))) (Var (IntermediateWire 1)) (OutputWire 0)
]
testVarsArithCircuit :: Map Int Fr
testVarsArithCircuit
= Map.fromList [ (0, 2)
, (1, 3)
, (2, 4)
, (3, 5)
]
-------------------------------------------------------------------------------
-- Tests
-------------------------------------------------------------------------------
unit_arithCircuitToQapCorrect :: Assertion
unit_arithCircuitToQapCorrect
= assertBool "Verifying assignment against QAP of circuit failed"
$ verifyAssignment qap assignment
where
roots = [[7],[8],[9]]
qap = arithCircuitToQAP roots testArithCircuit
assignment = generateAssignment testArithCircuit testVarsArithCircuit
unit_arithCircuitToQapNoFalsePositive :: Assertion
unit_arithCircuitToQapNoFalsePositive
= assertBool "Verifying whether verification fails on faulty assignment"
$ not $ verifyAssignment qap invalidAssignment
where
roots = [[7],[8],[9]]
qap = arithCircuitToQAP roots testArithCircuit
invalidAssignment
= QapSet
{ qapSetConstant = 1
, qapSetInput = Map.fromList [(0,2),(1,3),(2,4),(3,5)]
, qapSetIntermediate = Map.fromList [(0,7),(1,20)]
, qapSetOutput = Map.fromList [(0,320)]
}
prop_gateToQapCorrect
:: GateWithInputs Fr -> Bool
prop_gateToQapCorrect (GateWithInputs program inputs)
= all testInput inputs
where
roots = case program of
Mul {} -> [1]
Equal {} -> [1,2]
_ -> panic "Invalid roots"
qap = gateToQAP getRootOfUnity roots program
assignment input = generateAssignmentGate program input
testInput = verifyAssignment qap . assignment