content: module Example.RangeProof where
  
  import Protolude
  import Control.Monad.Random (MonadRandom, getRandomR)
  import Data.Curve.Weierstrass.SECP256K1 (Fr)
  import Data.Field.Galois (rnd)
  
  import qualified Bulletproofs.RangeProof as RP
  import qualified Bulletproofs.MultiRangeProof as MRP
  import Bulletproofs.Utils (commit)
  
  testSingleRangeProof :: Integer -> (Fr, Fr) -> IO Bool
  testSingleRangeProof upperBound (v, vBlinding) = do
    let vCommit = commit v vBlinding
    -- Prover
    proofE <- runExceptT $ RP.generateProof upperBound (v, vBlinding)
    -- Verifier
    case proofE of
      Left err -> panic $ show err
      Right proof@RP.RangeProof{..}
        -> pure $ RP.verifyProof upperBound vCommit proof
  
  testMultiRangeProof :: Integer -> [(Fr, Fr)] -> IO Bool
  testMultiRangeProof upperBound vsAndvBlindings = do
    let vCommits = fmap (uncurry commit) vsAndvBlindings
    -- Prover
    proofE <- runExceptT $ MRP.generateProof upperBound vsAndvBlindings
    -- Verifier
    case proofE of
      Left err -> panic $ show err
      Right proof@RP.RangeProof{..}
        -> pure $ MRP.verifyProof upperBound vCommits proof
  
  setupV :: MonadRandom m => Integer -> m (Fr, Fr)
  setupV n = do
    v <- fromInteger <$> getRandomR (1, 2^n - 1) -- value that needs to be in a certain range
    vBlinding <- rnd -- blinding value
    pure (v, vBlinding)
  
  runExamples :: IO ()
  runExamples = do
    n <- (2 ^) <$> getRandomR (0 :: Integer, 7)
    let upperBound = 2 ^ n
    (v, vBlinding) <- setupV n
    singleRangeProof <- testSingleRangeProof upperBound (v, vBlinding)
    putText $ "Single-range proof success: " <> show singleRangeProof
    vsAndvBlindings <- replicateM 5 (setupV n)
    testMultiRangeProof <- testMultiRangeProof upperBound vsAndvBlindings
    putText $ "Multi-range proof success: " <> show singleRangeProof
  
  