|
| 1 | +{-# LANGUAGE ViewPatterns, RecordWildCards, ScopedTypeVariables #-} |
| 2 | +{-# LANGUAGE DeriveAnyClass, DeriveGeneric #-} |
| 3 | + |
| 4 | +module Bulletproofs.ArithmeticCircuit.Internal where |
| 5 | + |
| 6 | +import Protolude hiding (head) |
| 7 | +import Data.List (head) |
| 8 | +import qualified Data.List as List |
| 9 | +import qualified Data.Map as Map |
| 10 | + |
| 11 | +import System.Random.Shuffle (shuffleM) |
| 12 | +import qualified Crypto.Random.Types as Crypto (MonadRandom(..)) |
| 13 | +import Crypto.Number.Generate (generateMax, generateBetween) |
| 14 | +import Control.Monad.Random (MonadRandom) |
| 15 | +import qualified Crypto.PubKey.ECC.Types as Crypto |
| 16 | +import qualified Crypto.PubKey.ECC.Prim as Crypto |
| 17 | + |
| 18 | +import Bulletproofs.Curve |
| 19 | +import Bulletproofs.Utils |
| 20 | +import Bulletproofs.RangeProof |
| 21 | +import qualified Bulletproofs.InnerProductProof as IPP |
| 22 | + |
| 23 | +data ArithCircuitProofError |
| 24 | + = TooManyGates Integer -- ^ The number of gates is too high |
| 25 | + | NNotPowerOf2 Integer -- ^ The number of gates is not a power of 2 |
| 26 | + deriving (Show, Eq) |
| 27 | + |
| 28 | +data ArithCircuitProof f |
| 29 | + = ArithCircuitProof |
| 30 | + { tBlinding :: f |
| 31 | + -- ^ Blinding factor of the T1 and T2 commitments, |
| 32 | + -- combined into the form required to make the committed version of the x-polynomial add up |
| 33 | + , mu :: f |
| 34 | + -- ^ Blinding factor required for the Verifier to verify commitments A, S |
| 35 | + , t :: f |
| 36 | + -- ^ Dot product of vectors l and r that prove knowledge of the value in range |
| 37 | + -- t = t(x) = l(x) · r(x) |
| 38 | + , aiCommit :: Crypto.Point |
| 39 | + -- ^ Commitment to vectors aL and aR |
| 40 | + , aoCommit :: Crypto.Point |
| 41 | + -- ^ Commitment to vectors aO |
| 42 | + , sCommit :: Crypto.Point |
| 43 | + -- ^ Commitment to new vectors sL, sR, created at random by the Prover |
| 44 | + , tCommits :: [Crypto.Point] |
| 45 | + -- ^ Commitments to t1, t3, t4, t5, t6 |
| 46 | + , productProof :: IPP.InnerProductProof f |
| 47 | + } deriving (Show, Eq, Generic, NFData) |
| 48 | + |
| 49 | +data ArithCircuit f |
| 50 | + = ArithCircuit |
| 51 | + { weights :: GateWeights f |
| 52 | + -- ^ Weights for vectors of left and right inputs and for vector of outputs |
| 53 | + , commitmentWeights :: [[f]] |
| 54 | + -- ^ Weigths for a commitments V of rank m |
| 55 | + , cs :: [f] |
| 56 | + -- ^ Vector of constants of size Q |
| 57 | + } deriving (Show, Eq, Generic, NFData) |
| 58 | + |
| 59 | + |
| 60 | +data GateWeights f |
| 61 | + = GateWeights |
| 62 | + { wL :: [[f]] -- ^ WL ∈ F^(Q x n) |
| 63 | + , wR :: [[f]] -- ^ WR ∈ F^(Q x n) |
| 64 | + , wO :: [[f]] -- ^ WO ∈ F^(Q x n) |
| 65 | + } deriving (Show, Eq, Generic, NFData) |
| 66 | + |
| 67 | +data ArithWitness f |
| 68 | + = ArithWitness |
| 69 | + { assignment :: Assignment f -- ^ Vectors of left and right inputs and vector of outputs |
| 70 | + , commitments :: [Crypto.Point] -- ^ Vector of commited input values ∈ F^m |
| 71 | + , commitBlinders :: [f] -- ^ Vector of blinding factors for input values ∈ F^m |
| 72 | + } deriving (Show, Eq, Generic, NFData) |
| 73 | + |
| 74 | +data Assignment f |
| 75 | + = Assignment |
| 76 | + { aL :: [f] -- ^ aL ∈ F^n. Vector of left inputs of each multiplication gate |
| 77 | + , aR :: [f] -- ^ aR ∈ F^n. Vector of right inputs of each multiplication gate |
| 78 | + , aO :: [f] -- ^ aO ∈ F^n. Vector of outputs of each multiplication gate |
| 79 | + } deriving (Show, Eq, Generic, NFData) |
| 80 | + |
| 81 | +-- | Pad circuit weights to make n be a power of 2, which |
| 82 | +-- is required to compute the inner product proof |
| 83 | +padCircuit :: Num f => ArithCircuit f -> ArithCircuit f |
| 84 | +padCircuit ArithCircuit{..} |
| 85 | + = ArithCircuit |
| 86 | + { weights = GateWeights wLNew wRNew wONew |
| 87 | + , commitmentWeights = commitmentWeights |
| 88 | + , cs = cs |
| 89 | + } |
| 90 | + where |
| 91 | + GateWeights{..} = weights |
| 92 | + wLNew = padToNearestPowerOfTwo <$> wL |
| 93 | + wRNew = padToNearestPowerOfTwo <$> wR |
| 94 | + wONew = padToNearestPowerOfTwo <$> wO |
| 95 | + |
| 96 | +-- | Pad assignment vectors to make their length n be a power of 2, which |
| 97 | +-- is required to compute the inner product proof |
| 98 | +padAssignment :: Num f => Assignment f -> Assignment f |
| 99 | +padAssignment Assignment{..} |
| 100 | + = Assignment aLNew aRNew aONew |
| 101 | + where |
| 102 | + aLNew = padToNearestPowerOfTwo aL |
| 103 | + aRNew = padToNearestPowerOfTwo aR |
| 104 | + aONew = padToNearestPowerOfTwo aO |
| 105 | + |
| 106 | +delta :: (Eq f, Field f) => Integer -> f -> [f] -> [f] -> f |
| 107 | +delta n y zwL zwR= (powerVector (recip y) n `hadamardp` zwR) `dot` zwL |
| 108 | + |
| 109 | +commitBitVector :: (AsInteger f) => f -> [f] -> [f] -> Crypto.Point |
| 110 | +commitBitVector vBlinding vL vR = vLG `addP` vRH `addP` vBlindingH |
| 111 | + where |
| 112 | + vBlindingH = vBlinding `mulP` h |
| 113 | + vLG = foldl' addP Crypto.PointO ( zipWith mulP vL gs ) |
| 114 | + vRH = foldl' addP Crypto.PointO ( zipWith mulP vR hs ) |
| 115 | + |
| 116 | +shamirGxGxG :: (Show f, Num f) => Crypto.Point -> Crypto.Point -> Crypto.Point -> f |
| 117 | +shamirGxGxG p1 p2 p3 |
| 118 | + = fromInteger $ oracle $ show q <> pointToBS p1 <> pointToBS p2 <> pointToBS p3 |
| 119 | + |
| 120 | +shamirGs :: (Show f, Num f) => [Crypto.Point] -> f |
| 121 | +shamirGs ps = fromInteger $ oracle $ show q <> foldMap pointToBS ps |
| 122 | + |
| 123 | +shamirZ :: (Show f, Num f) => f -> f |
| 124 | +shamirZ z = fromInteger $ oracle $ show q <> show z |
| 125 | + |
| 126 | +--------------------------------------------- |
| 127 | +-- Polynomials |
| 128 | +--------------------------------------------- |
| 129 | + |
| 130 | +evaluatePolynomial :: (Num f) => Integer -> [[f]] -> f -> [f] |
| 131 | +evaluatePolynomial (fromIntegral -> n) poly x |
| 132 | + = foldl' |
| 133 | + (\acc (idx, e) -> acc ^+^ ((*) (x ^ idx) <$> e)) |
| 134 | + (replicate n 0) |
| 135 | + (zip [0..] poly) |
| 136 | + |
| 137 | +multiplyPoly :: Num n => [[n]] -> [[n]] -> [n] |
| 138 | +multiplyPoly l r |
| 139 | + = snd <$> Map.toList (foldl' (\accL (i, li) |
| 140 | + -> foldl' |
| 141 | + (\accR (j, rj) -> case Map.lookup (i + j) accR of |
| 142 | + Just x -> Map.insert (i + j) (x + (li `dot` rj)) accR |
| 143 | + Nothing -> Map.insert (i + j) (li `dot` rj) accR |
| 144 | + ) accL (zip [0..] r)) |
| 145 | + (Map.empty :: Num n => Map.Map Int n) |
| 146 | + (zip [0..] l)) |
| 147 | + |
| 148 | + |
| 149 | +--------------------------------------------- |
| 150 | +-- Linear algebra |
| 151 | +--------------------------------------------- |
| 152 | + |
| 153 | +vectorMatrixProduct :: (Num f) => [f] -> [[f]] -> [f] |
| 154 | +vectorMatrixProduct v m |
| 155 | + = sum . zipWith (*) v <$> transpose m |
| 156 | + |
| 157 | +vectorMatrixProductT :: (Num f) => [f] -> [[f]] -> [f] |
| 158 | +vectorMatrixProductT v m |
| 159 | + = sum . zipWith (*) v <$> m |
| 160 | + |
| 161 | +matrixVectorProduct :: (Num f) => [[f]] -> [f] -> [f] |
| 162 | +matrixVectorProduct m v |
| 163 | + = head $ matrixProduct m [v] |
| 164 | + |
| 165 | +powerMatrix :: (Num f) => [[f]] -> Integer -> [[f]] |
| 166 | +powerMatrix m 0 = m |
| 167 | +powerMatrix m n = matrixProduct m (powerMatrix m (n-1)) |
| 168 | + |
| 169 | +matrixProduct :: Num a => [[a]] -> [[a]] -> [[a]] |
| 170 | +matrixProduct a b = (\ar -> sum . zipWith (*) ar <$> transpose b) <$> a |
| 171 | + |
| 172 | +insertAt :: Int -> a -> [a] -> [a] |
| 173 | +insertAt z y xs = as ++ (y : bs) |
| 174 | + where |
| 175 | + (as, bs) = splitAt z xs |
| 176 | + |
| 177 | +genIdenMatrix :: (Num f) => Integer -> [[f]] |
| 178 | +genIdenMatrix size = (\x -> (\y -> fromIntegral (fromEnum (x == y))) <$> [1..size]) <$> [1..size] |
| 179 | + |
| 180 | +genZeroMatrix :: (Num f) => Integer -> Integer -> [[f]] |
| 181 | +genZeroMatrix (fromIntegral -> n) (fromIntegral -> m) = replicate n (replicate m 0) |
| 182 | + |
| 183 | +generateWv :: (Num f, MonadRandom m) => Integer -> Integer -> m [[f]] |
| 184 | +generateWv lConstraints m |
| 185 | + | lConstraints < m = panic "Number of constraints must be bigger than m" |
| 186 | + | otherwise = shuffleM (genIdenMatrix m ++ genZeroMatrix (lConstraints - m) m) |
| 187 | + |
| 188 | +generateGateWeights :: (Crypto.MonadRandom m, Num f) => Integer -> Integer -> m (GateWeights f) |
| 189 | +generateGateWeights lConstraints n = do |
| 190 | + [wL, wR, wO] <- replicateM 3 ((\i -> insertAt (fromIntegral i) (oneVector n) (replicate (fromIntegral lConstraints - 1) (zeroVector n))) <$> generateMax (fromIntegral lConstraints)) |
| 191 | + pure $ GateWeights wL wR wO |
| 192 | + where |
| 193 | + zeroVector x = replicate (fromIntegral x) 0 |
| 194 | + oneVector x = replicate (fromIntegral x) 1 |
| 195 | + |
| 196 | +generateRandomAssignment :: forall f m . (Num f, AsInteger f, Crypto.MonadRandom m) => Integer -> m (Assignment f) |
| 197 | +generateRandomAssignment n = do |
| 198 | + aL <- replicateM (fromIntegral n) ((fromInteger :: Integer -> f) <$> generateMax (2^n)) |
| 199 | + aR <- replicateM (fromIntegral n) ((fromInteger :: Integer -> f) <$> generateMax (2^n)) |
| 200 | + let aO = aL `hadamardp` aR |
| 201 | + pure $ Assignment aL aR aO |
| 202 | + |
| 203 | +computeInputValues :: (Field f, Eq f) => GateWeights f -> [[f]] -> Assignment f -> [f] -> [f] |
| 204 | +computeInputValues GateWeights{..} wV Assignment{..} cs |
| 205 | + = solveLinearSystem $ zipWith (\row s -> reverse $ s : row) wV solutions |
| 206 | + where |
| 207 | + solutions = vectorMatrixProductT aL wL |
| 208 | + ^+^ vectorMatrixProductT aR wR |
| 209 | + ^+^ vectorMatrixProductT aO wO |
| 210 | + ^-^ cs |
| 211 | + |
| 212 | +gaussianReduce :: (Field f, Eq f) => [[f]] -> [[f]] |
| 213 | +gaussianReduce matrix = fixlastrow $ foldl reduceRow matrix [0..length matrix-1] |
| 214 | + where |
| 215 | + -- Swaps element at position a with element at position b. |
| 216 | + swap xs a b |
| 217 | + | a > b = swap xs b a |
| 218 | + | a == b = xs |
| 219 | + | a < b = let (p1, p2) = splitAt a xs |
| 220 | + (p3, p4) = splitAt (b - a - 1) (List.tail p2) |
| 221 | + in p1 ++ [xs List.!! b] ++ p3 ++ [xs List.!! a] ++ List.tail p4 |
| 222 | + |
| 223 | + -- Concat the lists and repeat |
| 224 | + reduceRow matrix1 r = take r matrix2 ++ [row1] ++ nextrows |
| 225 | + where |
| 226 | + --First non-zero element on or below (r,r). |
| 227 | + firstnonzero = head $ filter (\x -> matrix1 List.!! x List.!! r /= 0) [r..length matrix1 - 1] |
| 228 | + --Matrix with row swapped (if needed) |
| 229 | + matrix2 = swap matrix1 r firstnonzero |
| 230 | + --Row we're working with |
| 231 | + row = matrix2 List.!! r |
| 232 | + --Make it have 1 as the leading coefficient |
| 233 | + row1 = (\x -> x * recip (row List.!! r)) <$> row |
| 234 | + --Subtract nr from row1 while multiplying |
| 235 | + subrow nr = let k = nr List.!! r in zipWith (\a b -> k*a - b) row1 nr |
| 236 | + --Apply subrow to all rows below |
| 237 | + nextrows = subrow <$> drop (r + 1) matrix2 |
| 238 | + |
| 239 | + |
| 240 | + fixlastrow matrix' = a ++ [List.init (List.init row) ++ [1, z * recip nz]] |
| 241 | + where |
| 242 | + a = List.init matrix'; row = List.last matrix' |
| 243 | + z = List.last row |
| 244 | + nz = List.last (List.init row) |
| 245 | + |
| 246 | +-- Solve a matrix (must already be in REF form) by back substitution. |
| 247 | +substituteMatrix :: (Field f, Eq f) => [[f]] -> [f] |
| 248 | +substituteMatrix matrix = foldr next [List.last (List.last matrix)] (List.init matrix) |
| 249 | + where |
| 250 | + next row found = let |
| 251 | + subpart = List.init $ drop (length matrix - length found) row |
| 252 | + solution = List.last row - sum (zipWith (*) found subpart) |
| 253 | + in solution : found |
| 254 | + |
| 255 | +solveLinearSystem :: (Field f, Eq f) => [[f]] -> [f] |
| 256 | +solveLinearSystem = reverse . substituteMatrix . gaussianReduce |
0 commit comments