bulletproofs-0.2.1: tests/TestArithCircuitProtocol.hs
{-# LANGUAGE ViewPatterns, RecordWildCards #-}
module TestArithCircuitProtocol where
import Protolude
import qualified Data.Map as Map
import qualified Data.List as List
import Test.Tasty
import Test.Tasty.QuickCheck
import Test.QuickCheck
import qualified Test.QuickCheck.Monadic as QCM
import Crypto.Number.Generate (generateMax, generateBetween)
import Control.Monad.Random (MonadRandom)
import qualified Bulletproofs.InnerProductProof as IPP
import qualified Bulletproofs.Fq as Fq
import Bulletproofs.Utils
import Bulletproofs.Curve
import Bulletproofs.Fq
import Bulletproofs.ArithmeticCircuit
import Bulletproofs.ArithmeticCircuit.Internal
-- | Test an arbitrary circuit
-- Construction:
-- 1. aL, aR, aO; wL, wR, wO; c
-- such that wL * aL + wR * aR + wO * aO = c
--
-- 2. Create wV and v to
-- - reduce the size of the prove (m <= n)
-- - hide assignment
-- wL * aL + wR * aR + wO * aO - c = wV * v
test_arithCircuitProof_arbitrary :: TestTree
test_arithCircuitProof_arbitrary = localOption (QuickCheckTests 10) $
testProperty "Arbitrary arithmetic circuit proof" $ QCM.monadicIO $ do
n <- QCM.run $ generateBetween 1 100
m <- QCM.run $ generateBetween 1 n
let lConstraints = m
weights@GateWeights{..} <- QCM.run $ generateGateWeights lConstraints n
commitmentWeights <- QCM.run $ generateWv lConstraints m
Assignment{..} <- QCM.run $ generateRandomAssignment n
cs <- QCM.run $ replicateM (fromIntegral m) Fq.random
commitBlinders <- QCM.run $ replicateM (fromIntegral m) Fq.random
let gateWeights = GateWeights wL wR wO
gateInputs = Assignment aL aR aO
vs = computeInputValues weights commitmentWeights gateInputs cs
commitments = zipWith commit vs commitBlinders
arithCircuit = ArithCircuit gateWeights commitmentWeights cs
arithWitness = ArithWitness gateInputs commitments commitBlinders
proof <- QCM.run $ generateProof arithCircuit arithWitness
QCM.assert $ verifyProof commitments proof arithCircuit
-- | Test hadamard product relation
-- 2 linear constraints (q = 2):
-- aL[0] + aL[1] + ... + aL[15] = v[0]
-- aR[0] + aR[1] + ... + aR[15] = v[1]
--
-- 16 multiplication constraints (implicit) (n = 16):
--
-- 2 input values (m = 2)
test_arithCircuitProof_hadamardp :: TestTree
test_arithCircuitProof_hadamardp = localOption (QuickCheckTests 20) $
testProperty "Arithmetic circuit proof. Hadamard product relation" $ QCM.monadicIO $ do
let n = 16
aL <- QCM.run $ replicateM (fromIntegral n) Fq.random
aR <- QCM.run $ replicateM (fromIntegral n) Fq.random
let aO = aL `hadamardp` aR
r <- QCM.run Fq.random
s <- QCM.run Fq.random
let v0 = sum aL
v1 = sum aR
let v0Commit = commit v0 r
v1Commit = commit v1 s
let zeroVector = replicate (fromIntegral n) 0
oneVector = replicate (fromIntegral n) 1
let wL = [oneVector, zeroVector]
wR = [zeroVector, oneVector]
wO = [zeroVector, zeroVector]
commitmentWeights = [[1, 0], [0, 1]]
cs = [0, 0]
commitments = [v0Commit, v1Commit]
commitBlinders = [r, s]
gateWeights = GateWeights wL wR wO
gateInputs = Assignment aL aR aO
arithCircuit = ArithCircuit gateWeights commitmentWeights cs
arithWitness = ArithWitness gateInputs commitments commitBlinders
proof <- QCM.run $ generateProof arithCircuit arithWitness
QCM.assert $ verifyProof commitments proof arithCircuit
-- | Test that an addition circuit without multiplication gates succeeds
-- 1 linear constraints (q = 1):
-- v[0] + v[1] = v[2]
--
-- 0 multiplication constraints (implicit) (n = 0):
--
-- 3 input values (m = 3)
test_arithCircuitProof_no_mult_gates :: TestTree
test_arithCircuitProof_no_mult_gates = localOption (QuickCheckTests 20) $
testProperty "Arithmetic circuit proof. n = 0, m = 3, q = 1"
$ QCM.monadicIO $ do
let n = 0
m = 3
commitBlinders <- QCM.run $ replicateM m Fq.random
let wL = [[]]
wR = [[]]
wO = [[]]
cs = [0]
aL = []
aR = []
aO = []
commitmentWeights = [[1, 1, -1]]
vs = [2, 5, 7]
commitments = zipWith commit vs commitBlinders
gateWeights = GateWeights wL wR wO
gateInputs = Assignment aL aR aO
arithCircuit = ArithCircuit gateWeights commitmentWeights cs
arithWitness = ArithWitness gateInputs commitments commitBlinders
proof <- QCM.run $ generateProof arithCircuit arithWitness
QCM.assert $ verifyProof commitments proof arithCircuit
-- | Test that a circuit with a single multiplication gate
-- with linear contraints and not committed values succeeds
-- 3 linear constraints (q = 3):
-- aL[0] = 3
-- aR[0] = 4
-- aO[0] = 9
--
-- 1 multiplication constraint (implicit) (n = 1):
-- aL[0] * aR[0] = aO[0]
--
-- 0 input values (m = 0)
test_arithCircuitProof_no_input_values :: TestTree
test_arithCircuitProof_no_input_values = localOption (QuickCheckTests 20) $
testProperty "Arithmetic circuit proof. n = 1, m = 0, q = 3"
$ QCM.monadicIO $ do
let n = 1
m = 0
commitBlinders <- QCM.run $ replicateM m Fq.random
let wL = [[0], [0], [1]]
wR = [[0], [1], [0]]
wO = [[1], [0], [0]]
cs = [35, 5, 7]
aL = [7]
aR = [5]
aO = [35]
commitmentWeights = [[], [], []]
vs = []
commitments = zipWith commit vs commitBlinders
gateWeights = GateWeights wL wR wO
gateInputs = Assignment aL aR aO
arithCircuit = ArithCircuit gateWeights commitmentWeights cs
arithWitness = ArithWitness gateInputs commitments commitBlinders
proof <- QCM.run $ generateProof arithCircuit arithWitness
QCM.assert $ verifyProof commitments proof arithCircuit
-- 5 linear constraints (q = 5):
-- aO[0] = aO[1]
-- aL[0] = V[0] - z
-- aL[1] = V[2] - z
-- aR[0] = V[1] - z
-- aR[1] = V[3] - z
--
-- 2 multiplication constraint (implicit) (n = 2):
-- aL[0] * aR[0] = aO[0]
-- aL[1] * aR[1] = aO[1]
--
-- 4 input values (m = 4)
test_arithCircuitProof_shuffle_circuit :: TestTree
test_arithCircuitProof_shuffle_circuit = localOption (QuickCheckTests 20) $
testProperty "Arithmetic circuit proof. n = 2, m = 4, q = 5" $ QCM.monadicIO $ do
z <- QCM.run Fq.random
commitBlinders <- QCM.run $ replicateM 4 Fq.random
let wL = [[0, 0]
,[1, 0]
,[0, 1]
,[0, 0]
,[0, 0]]
wR = [[0, 0]
,[0, 0]
,[0, 0]
,[1, 0]
,[0, 1]]
wO = [[1, -1]
,[0, 0]
,[0, 0]
,[0, 0]
,[0, 0]]
wV = [[0, 0, 0, 0]
,[1, 0, 0, 0]
,[0, 0, 1, 0]
,[0, 1, 0 ,0]
,[0, 0, 0, 1]]
cs = [0, -z, -z, -z, -z]
aL = [4 - z, 9 - z]
aR = [9 - z, 4 - z]
aO = aL `hadamardp` aR
vs = [4, 9, 9, 4]
commitments = zipWith commit vs commitBlinders
gateWeights = GateWeights wL wR wO
gateInputs = Assignment aL aR aO
arithCircuit = ArithCircuit gateWeights wV cs
arithWitness = ArithWitness gateInputs commitments commitBlinders
proof <- QCM.run $ generateProof arithCircuit arithWitness
QCM.assert $ verifyProof commitments proof arithCircuit