public class ExperimentUtils {
    public static void generateRandomCustomers(int numCustomers, int maxBits) throws SQLException {
        try {
            SQLCustomerDatabase database = new SQLCustomerDatabase();
            try {
                database.truncate();
            } finally {

                database.close();

            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        Random rng = new Random();
        int availableProcessors = Runtime.getRuntime().availableProcessors();
        Semaphore semaphore = new Semaphore(0);
        int process = 0;
        while (process < availableProcessors) {
            SQLCustomerDatabase processDb = new SQLCustomerDatabase();
            int processId = process++;
            ForkJoinTask.adapt(() -> {
                        for (int i = processId; i < numCustomers; i += availableProcessors) {
                            String customerId = "C" + i;
                            BigInteger balance = rng.nextDouble() > 0.9 ? new BigInteger(maxBits, rng) : new BigInteger(10, rng);
                            processDb.addBalance(customerId, balance);
                        }
                        try {
                            processDb.close();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        semaphore.release();
                    }
            ).fork();
        }
        try {
            semaphore.acquire(availableProcessors);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void generatePublicKeys(int numAddresses) throws SQLException {
        SQLBlockchain blockchain = new SQLBlockchain();
        try {
            blockchain.truncate();
        } finally {

            blockchain.close();
        }
        Random rng = new Random();
        int availableProcessors = Runtime.getRuntime().availableProcessors();
        Semaphore semaphore = new Semaphore(0);
        int process = 0;
        while (process < availableProcessors) {
            SQLBlockchain processBlockchain = new SQLBlockchain();
            SQLPrivateKeyDatabase privateKeyDatabase = new SQLPrivateKeyDatabase();
            int processId = process++;
            ForkJoinTask.adapt(() -> {
                        try {
                            for (int i = processId; i < numAddresses; i += availableProcessors) {
                                BigInteger privateKey = new BigInteger(256, rng);
                                ECPoint publicKey = ECConstants.G.multiply(privateKey);
                                BigInteger balance = new BigInteger(10, rng);
                                processBlockchain.addEntry(publicKey, balance);
                                if (rng.nextDouble() >= 0.05) continue;
                                privateKeyDatabase.store(publicKey, privateKey);
                            }
                        } finally {
                            try {
                                privateKeyDatabase.close();
                                processBlockchain.close();
                            } catch (SQLException e) {
                                e.printStackTrace();
                            }

                            semaphore.release();
                        }
                    }
            ).fork();
        }
        try {
            semaphore.acquire(availableProcessors);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
} public class SQLCustomerDatabase
implements CustomerDatabase,
SQLDatabase {
    public static final String QUERY_BALANCE_SQL = "SELECT balance FROM customer_balance WHERE customer_id=?";
    public static final String LIST_CUSTOMERS_SQL = "SELECT customer_id,balance FROM customer_balance";
    public static final String INSERT_CUSTOMER_SQL = "INSERT INTO customer_balance VALUES (?,?)";
    public static final String TRUNCATE_TABLE_SQL = "TRUNCATE TABLE customer_balance,balance_proof_secrets";
    private final PreparedStatement queryBalance;
    private final PreparedStatement listCustomers;
    private final PreparedStatement insertCustomer;
    private final PreparedStatement truncateDB;
    private final Connection connection = Database.getConnection();

    public SQLCustomerDatabase() throws SQLException {
        this.queryBalance = this.connection.prepareStatement("SELECT balance FROM customer_balance WHERE customer_id=?");
        this.listCustomers = this.connection.prepareStatement("SELECT customer_id,balance FROM customer_balance");
        this.insertCustomer = this.connection.prepareStatement("INSERT INTO customer_balance VALUES (?,?)");
        this.truncateDB = this.connection.prepareStatement("TRUNCATE TABLE customer_balance,balance_proof_secrets");
    }

    @Override
    public BigInteger getBalance(String customerId) {
        try {
            this.queryBalance.setString(1, customerId);
            ResultSet resultSet = this.queryBalance.executeQuery();
            if (resultSet.next()) {
                long balance = resultSet.getLong(1);
                return BigInteger.valueOf(balance);
            }
            throw new AssertionError((Object)("Customer not found " + customerId));
        }
        catch (SQLException e) {
            e.printStackTrace();
            throw new AssertionError(e);
        }
    }

    @Override
    public void addBalance(String customerId, BigInteger balance) {
        try {
            PreparedStatement statement = this.insertCustomer;
            statement.setString(1, customerId);
            statement.setLong(2, balance.longValueExact());
            statement.execute();
        }
        catch (SQLException e) {
            e.printStackTrace();
            throw new IllegalStateException(e);
        }
    }

    @Override
    public Iterator<CustomerInfo> getCustomers() {
        try {
            this.connection.setAutoCommit(false);
            final ResultSet resultSet = this.listCustomers.executeQuery();
            resultSet.setFetchSize(1000);
            return new Iterator<CustomerInfo>(){

                @Override
                public boolean hasNext() {
                    try {
                        boolean hasNext = resultSet.next();
                        if (!hasNext) {
                            SQLCustomerDatabase.this.connection.setAutoCommit(true);
                        }
                        return hasNext;
                    }
                    catch (SQLException e) {
                        throw new IllegalStateException(e);
                    }
                }

                @Override
                public CustomerInfo next() {
                    try {
                        String id = resultSet.getString(1);
                        long balance = resultSet.getLong(2);
                        return new CustomerInfo(id, BigInteger.valueOf(balance));
                    }
                    catch (SQLException e) {
                        throw new IllegalStateException(e);
                    }
                }
            };
        }
        catch (SQLException e) {
            e.printStackTrace();
            throw new AssertionError(e);
        }
    }

    @Override
    public void truncate() {
        try {
            this.truncateDB.execute();
        }
        catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void close() throws SQLException {
        this.connection.close();
    }

} public class SQLCustomerSecretsDatabase
implements CustomerSecretsDatabase,
SQLDatabase {
    public static final String RETRIEVE_SECRETS_SQL = "SELECT hash_salt,balance_salt FROM balance_proof_secrets WHERE customer_id=?";
    public static final String INSERT_INTO_SQL = "INSERT INTO balance_proof_secrets VALUES (?,?,?)";
    private final PreparedStatement retrieveSecrets;
    private final PreparedStatement insertSecrets;
    private final Connection connection = Database.getConnection();

    public SQLCustomerSecretsDatabase() throws SQLException {
        this.retrieveSecrets = this.connection.prepareStatement("SELECT hash_salt,balance_salt FROM balance_proof_secrets WHERE customer_id=?");
        this.insertSecrets = this.connection.prepareStatement("INSERT INTO balance_proof_secrets VALUES (?,?,?)");
    }

    @Override
    public void store(String customerId, BigInteger hashSalt, BigInteger balanceSalt) {
        try {
            this.insertSecrets.setString(1, customerId);
            this.insertSecrets.setBytes(2, hashSalt.toByteArray());
            this.insertSecrets.setBytes(3, balanceSalt.toByteArray());
            this.insertSecrets.execute();
        }
        catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Override
    public CustomerSecrets retrieve(String customerId) {
        try {
            this.retrieveSecrets.setString(1, customerId);
            ResultSet resultSet = this.retrieveSecrets.executeQuery();
            if (resultSet.next()) {
                BigInteger hashSalt = new BigInteger(resultSet.getBytes(1));
                BigInteger balanceRandomness = new BigInteger(resultSet.getBytes(2));
                return new CustomerSecrets(hashSalt, balanceRandomness);
            }
            throw new IllegalArgumentException("Customer not found " + customerId);
        }
        catch (SQLException e) {
            e.printStackTrace();
            throw new IllegalArgumentException(e);
        }
    }

    @Override
    public void close() throws SQLException {
        this.connection.close();
    }
} public final class Database {
    public static final String URL = "jdbc:postgresql://127.0.0.1/provisions";
    public static final String USER = "admin";
    public static final String PASSWORD = "password";

    private Database() throws UnknownHostException, ClassNotFoundException, SQLException {
    }

    public static Connection getConnection() throws SQLException {
        DriverManager.setLoginTimeout(10);
        return DriverManager.getConnection(URL, USER, PASSWORD);
    }

    public static void clearProofs() {
        try {
            Connection connection = Database.getConnection();
            Throwable throwable = null;
            try {
                Statement statement = connection.createStatement();
                statement.execute("TRUNCATE TABLE balance_proof,balance_proof_secrets,asset_proof");
            }
            catch (Throwable statement) {
                throwable = statement;
                throw statement;
            }
            finally {
                if (connection != null) {
                    if (throwable != null) {
                        try {
                            connection.close();
                        }
                        catch (Throwable statement) {
                            throwable.addSuppressed(statement);
                        }
                    } else {
                        connection.close();
                    }
                }
            }
        }
        catch (SQLException e) {
            e.printStackTrace();
            throw new IllegalStateException(e);
        }
    }

    public static void createTables() throws SQLException {
        Connection connection = Database.getConnection();
        String CREATE_BALANCE_TABLE_SQL = "CREATE TABLE IF NOT EXISTS balance_proof (  customer_id_hash BYTEA NOT NULL,\n  range_proof BYTEA,\n  CONSTRAINT balance_proof_pkey PRIMARY KEY (customer_id_hash)\n)WITH (  OIDS=FALSE);ALTER TABLE balance_proof   OWNER TO USER";
        PreparedStatement createBalanceStatement = connection.prepareStatement(CREATE_BALANCE_TABLE_SQL);
        createBalanceStatement.execute();
        String CREATE_BALANCE_SECRETS_TABLE_SQL = "CREATE TABLE IF NOT EXISTS balance_proof_secrets\n(\n  customer_id CHARACTER VARYING(9) NOT NULL,\n  hash_salt BYTEA,\n  balance_salt BYTEA,\n  CONSTRAINT balance_proof_secrets_pkey PRIMARY KEY (customer_id),\n  CONSTRAINT balance_proof_secrets_customer_id_fkey FOREIGN KEY (customer_id)\n      REFERENCES customer_balance (customer_id) MATCH SIMPLE\n      ON UPDATE CASCADE ON DELETE CASCADE\n)\nWITH (\n  OIDS=FALSE\n);\nALTER TABLE balance_proof_secrets\n  OWNER TO USER;";
        PreparedStatement createBalanceSecretsTable = connection.prepareStatement(CREATE_BALANCE_SECRETS_TABLE_SQL);
        createBalanceSecretsTable.execute();
        String CREATE_CUSTOMER_BALANCE = "CREATE TABLE IF NOT EXISTS customer_balance\n(\n  customer_id CHARACTER VARYING(9) NOT NULL,\n  balance BIGINT,\n  CONSTRAINT customer_balance_pkey PRIMARY KEY (customer_id)\n)\nWITH (\n  OIDS=FALSE\n);\nALTER TABLE customer_balance\n  OWNER TO USER;\n";
        PreparedStatement createCustomerBalance = connection.prepareStatement(CREATE_CUSTOMER_BALANCE);
        createCustomerBalance.execute();
        String CREATE_ASSETS_TABLE_SQL = "CREATE TABLE IF NOT EXISTS assets_proof(hash_salt BYTEA,balance_salt BYTEA);";
        PreparedStatement createAssetsTable = connection.prepareStatement(CREATE_ASSETS_TABLE_SQL);
        createAssetsTable.execute();
        String CREATE_BLOCKCHAIN_SQL = "CREATE TABLE IF NOT EXISTS blockchain\n(\n  pubkey BYTEA NOT NULL,\n  balance BIGINT,\n  CONSTRAINT blockchain_pkey PRIMARY KEY (pubkey)\n)\nWITH (\n  OIDS=FALSE\n);\nALTER TABLE blockchain\n  OWNER TO USER;";
        PreparedStatement createBlockchain = connection.prepareStatement(CREATE_BLOCKCHAIN_SQL);
        createBlockchain.execute();
        String CREATE_ASSET_PROOF_SQL = "CREATE TABLE IF NOT EXISTS asset_proof\n(\n  public_key BYTEA NOT NULL,\n  main_proof BYTEA,\n  binary_proof BYTEA,\n  CONSTRAINT asset_proof_pkey PRIMARY KEY (public_key),\n  CONSTRAINT asset_proof_public_key_fkey FOREIGN KEY (public_key)\n      REFERENCES blockchain (public_key) MATCH SIMPLE\n      ON UPDATE NO ACTION ON DELETE NO ACTION\n)\nWITH (\n  OIDS=FALSE\n);\nALTER TABLE asset_proof\n  OWNER TO USER;\n";
        PreparedStatement createAssetProof = connection.prepareStatement(CREATE_ASSET_PROOF_SQL);
        createAssetProof.execute();
        String CREATE_ASSET_PROOF_SECRETS_SQL = "CREATE TABLE IF NOT EXISTS asset_proof_secrets \n(\n  public_key BYTEA NOT NULL,\n  private_key BYTEA,\n  CONSTRAINT asset_proof_secrets_pkey PRIMARY KEY (public_key),\n  CONSTRAINT asset_proof_secrets_public_key_fkey FOREIGN KEY (public_key)\n      REFERENCES blockchain (public_key) MATCH SIMPLE\n      ON UPDATE NO ACTION ON DELETE NO ACTION\n)\nWITH (\n  OIDS=FALSE\n);\nALTER TABLE asset_proof_secrets\n  OWNER TO USER;\n";
        PreparedStatement createAssetProofSecrets = connection.prepareStatement(CREATE_ASSET_PROOF_SECRETS_SQL);
        createAssetProofSecrets.execute();
        connection.close();
    }
} public class BalanceProof
        implements Proof,
        SQLDatabase {
    public static final String ADD_USER_SQL = "INSERT INTO balance_proof VALUES  (?, ?) ";
    public static final String GET_PROOF_SQL = "SELECT balance_proof.range_proof FROM balance_proof WHERE customer_id_hash= ?";
    public static final String LIST_PROOFS_SQL = "SELECT balance_proof.range_proof FROM balance_proof ";
    public static final String READ_SIZE = "SELECT pg_size_pretty(pg_total_relation_size('public.balance_proof'))";
    private final PreparedStatement updateStatement;
    private final PreparedStatement readStatement;
    private final Connection connection = Database.getConnection();

    public BalanceProof() throws SQLException {
        this.updateStatement = this.connection.prepareStatement(ADD_USER_SQL);
        this.readStatement = this.connection.prepareStatement(GET_PROOF_SQL);
    }

    public void addCustomer(BigInteger hashedId, BinaryRangeProof balanceProof) {
        try {
            this.updateStatement.setBytes(1, hashedId.toByteArray());
            this.updateStatement.setBytes(2, balanceProof.serialize());
            this.updateStatement.executeUpdate();
        } catch (SQLException ex) {
            throw new IllegalArgumentException(ex);
        }
    }

    public BinaryRangeProof getCustomerProof(BigInteger hashedId) {
        try {
            this.readStatement.setBytes(1, hashedId.toByteArray());
            ResultSet resultSet = this.readStatement.executeQuery();
            if (resultSet.next()) {
                byte[] proof = resultSet.getBytes(1);
                return new BinaryRangeProof(proof);
            }
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
        throw new IllegalArgumentException("No such id " + Arrays.toString(hashedId.toByteArray()));
    }

    public Iterator<BinaryRangeProof> getRangeProofs() {
        try {
            this.connection.setAutoCommit(false);
            final ResultSet resultSet = this.connection.createStatement().executeQuery(LIST_PROOFS_SQL);
            resultSet.setFetchSize(1000);
            return new Iterator<BinaryRangeProof>() {

                @Override
                public boolean hasNext() {
                    try {
                        boolean next = resultSet.next();
                        if (!next) {
                            BalanceProof.this.connection.setAutoCommit(true);
                        }
                        return next;
                    } catch (SQLException e) {
                        e.printStackTrace();
                        throw new IllegalStateException(e);
                    }
                }

                @Override
                public BinaryRangeProof next() {
                    try {
                        byte[] bytes = resultSet.getBytes(1);
                        return new BinaryRangeProof(bytes);
                    } catch (SQLException e) {
                        e.printStackTrace();
                        throw new IllegalStateException(e);
                    }
                }
            };
        } catch (SQLException e) {
            e.printStackTrace();
            throw new IllegalStateException(e);
        }
    }

    public String getSizeInfo() {
        try {
            ResultSet resultSet = this.connection.createStatement().executeQuery(READ_SIZE);
            if (resultSet.next()) {
                return resultSet.getString(1);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        throw new IllegalStateException("Couldn't run query " + READ_SIZE);
    }

    @Override
    public void close() throws SQLException {
        this.connection.close();
    }

    public Connection getConnection() {
        return this.connection;
    }

} public class BalanceProofData
implements ProofData<BalanceProof> {
    private final int maxBits;

    public BalanceProofData(int maxBits) {
        this.maxBits = maxBits;
    }

    public int getMaxBits() {
        return this.maxBits;
    }
} public class BalanceProofSystem
        implements ProofSystem<BalanceProof, BalanceProofData> {
    private final BinaryRangeProofSystem rangeProofSystem = new BinaryRangeProofSystem();
    private final BlockingQueue<CustomerInfo> customerInfos = new ArrayBlockingQueue<CustomerInfo>(Runtime.getRuntime().availableProcessors() * 3);
    private final AtomicReference<BigInteger> balanceKey = new AtomicReference<BigInteger>(BigInteger.ZERO);

    /*
     * Enabled aggressive block sorting
     * Enabled unnecessary exception pruning
     * Enabled aggressive exception aggregation
     */
    @Override
    public BalanceProof createProof(BalanceProofData data) {
        try (SQLCustomerDatabase customerSecretsDatabase = new SQLCustomerDatabase()) {
            BalanceProof proof;
            ExecutorService executorService;
            Iterator<CustomerInfo> customerInfoIterator = customerSecretsDatabase.getCustomers();
            proof = new BalanceProof();
            int nThreads = Runtime.getRuntime().availableProcessors() - 1;
            executorService = Executors.newFixedThreadPool(nThreads);
            proof.getConnection().setAutoCommit(false);
            Stream.generate(() -> new RangeProofWorkerThread(data.getMaxBits())).limit(nThreads).forEach(executorService::submit);
            try (SQLCustomerSecretsDatabase database2 = new SQLCustomerSecretsDatabase()) {
                CustomerInfo info;
                while (customerInfoIterator.hasNext()) {
                    CustomerInfo customerInfo = customerInfoIterator.next();
                    if (this.customerInfos.offer(customerInfo)) continue;
                    this.createRangeProof(customerInfo, proof, database2, data.getMaxBits());
                }
                while ((info = this.customerInfos.poll()) != null) {
                    this.createRangeProof(info, proof, database2, data.getMaxBits());
                }
            }
            executorService.shutdownNow();
            executorService.awaitTermination(1, TimeUnit.MINUTES);
            proof.getConnection().setAutoCommit(true);
            return proof;

        } catch (SQLException e) {
            e.printStackTrace();
            throw new IllegalArgumentException("Couldn't complete proof");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        throw new IllegalArgumentException("Couldn't complete proof");
    }

    public BigInteger getBalanceKey() {
        return this.balanceKey.get();
    }

    private void createRangeProof(CustomerInfo customerInfo, BalanceProof proof, CustomerSecretsDatabase database, int maxBits) {
        List<BigInteger> randomness = Stream.generate(ProofUtils::randomNumber).limit(maxBits).collect(Collectors.toList());
        BigInteger totalRandomness = BigInteger.ZERO;
        for (int i = 0; i < randomness.size(); ++i) {
            totalRandomness = totalRandomness.add(randomness.get(i).shiftLeft(i));
        }
        this.balanceKey.accumulateAndGet(totalRandomness, BigInteger::add);
        RangeProofData rangeProofData = new RangeProofData(customerInfo.getBalance(), randomness);
        BinaryRangeProof rangeProof = this.rangeProofSystem.createProof(rangeProofData);
        BigInteger hashSalt = ProofUtils.randomNumber();
        BigInteger hashedId = ProofUtils.hash(customerInfo.getId(), hashSalt);
        proof.addCustomer(hashedId, rangeProof);
        database.store(customerInfo.getId(), hashSalt, rangeProofData.getTotalRandomness());
    }

    private class RangeProofWorkerThread
            implements Runnable {
        private final int maxBits;

        private RangeProofWorkerThread(int maxBits) {
            this.maxBits = maxBits;
        }

        @Override
        public void run() {
            try (BalanceProof balanceProof = new BalanceProof()) {
                try (SQLCustomerSecretsDatabase database = new SQLCustomerSecretsDatabase()) {

                    while (!Thread.interrupted()) {
                        try {
                            CustomerInfo info = BalanceProofSystem.this.customerInfos.take();
                            BalanceProofSystem.this.createRangeProof(info, balanceProof, database, this.maxBits);
                        } catch (InterruptedException e) {
                            // empty catch block
                            break;
                        }
                    }

                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

} public class CustomerSecrets {
    private final BigInteger hashSalt;
    private final BigInteger combinedRandomness;

    public CustomerSecrets(BigInteger hashSalt, BigInteger combinedRandomness) {
        this.hashSalt = hashSalt;
        this.combinedRandomness = combinedRandomness;
    }

    public BigInteger getHashSalt() {
        return this.hashSalt;
    }

    public BigInteger getCombinedRandomness() {
        return this.combinedRandomness;
    }
} public class BalanceVerificationData
implements VerificationData<BalanceProof> {
    private final int maxBits;

    public BalanceVerificationData(int maxBits) {
        this.maxBits = maxBits;
    }

    public int getMaxBits() {
        return this.maxBits;
    }
} public class BalanceVerifier
implements Verifier<BalanceProof, BalanceVerificationData> {
    private final BinaryRangeProofVerifier verifier = new BinaryRangeProofVerifier();

    @Override
    public void verify(BalanceProof proof, BalanceVerificationData data) {
        Iterator<BinaryRangeProof> rangeProofs = proof.getRangeProofs();
        RangeProofVerificationData verificationData = new RangeProofVerificationData(new GeneratorData<>(), BigInteger.ONE.shiftLeft(data.getMaxBits()).subtract(BigInteger.ONE));
        AtomicReference<ECPoint> totalEncryption = new AtomicReference<>(ECConstants.INFINITY);
        BlockingExecutor pool = new BlockingExecutor();
        while (rangeProofs.hasNext()) {
            BinaryRangeProof rangeProof = rangeProofs.next();
            Runnable thread = () -> {
                this.verifier.verify(rangeProof, verificationData);
                totalEncryption.accumulateAndGet(rangeProof.getStatement(), ECPoint::add);
            };
            pool.submit(thread);
        }
        try {
            pool.shutdown();
            pool.awaitTermination(2, TimeUnit.DAYS);
            System.out.println(totalEncryption.get());
        }
        catch (InterruptedException e) {
            e.printStackTrace();
            throw new AssertionError(e);
        }
    }
} public class ParticipationData
implements VerificationData<BalanceProof> {
    private final String customerId;
    private final CustomerSecrets secrets;
    private final BigInteger balance;

    public ParticipationData(String customerId, CustomerSecrets secrets, BigInteger balance) {
        this.customerId = customerId;
        this.secrets = secrets;
        this.balance = balance;
    }

    public String getCustomerId() {
        return this.customerId;
    }

    public CustomerSecrets getSecrets() {
        return this.secrets;
    }

    public BigInteger getBalance() {
        return this.balance;
    }
} public class ParticipationVerifier
implements Verifier<BalanceProof, ParticipationData> {
    @Override
    public void verify(BalanceProof proof, ParticipationData data) {
        BigInteger hashedId = ProofUtils.hash(data.getCustomerId(), data.getSecrets().getHashSalt());
        BinaryRangeProof customerProof = proof.getCustomerProof(hashedId);
        ECPoint assumedStatement = ECConstants.H.multiply(data.getSecrets().getCombinedRandomness()).add(ECConstants.G.multiply(data.getBalance()));
        this.holds(assumedStatement, customerProof.getStatement());
    }
}
import edu.stanford.crypto.ExperimentUtils;
import edu.stanford.crypto.bitcoin.SQLCustomerDatabase;
import edu.stanford.crypto.bitcoin.SQLCustomerSecretsDatabase;
import edu.stanford.crypto.database.Database;
import edu.stanford.crypto.proof.balance.BalanceProof;
import edu.stanford.crypto.proof.balance.BalanceProofData;
import edu.stanford.crypto.proof.balance.BalanceProofSystem;
import edu.stanford.crypto.proof.balance.CustomerSecrets;
import edu.stanford.crypto.verification.BalanceVerificationData;
import edu.stanford.crypto.verification.BalanceVerifier;
import edu.stanford.crypto.verification.ParticipationData;
import edu.stanford.crypto.verification.ParticipationVerifier;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.SQLException;
/*
 * Decompiled with CFR 0_110.
 */
package edu.stanford.crypto.experiments;



public class BalanceProofExperiment {
    public static void main(String[] args) throws IOException, SQLException {
        Path outputFile = Paths.get(args[0], new String[0]);
        OutputStream stream = Files.newOutputStream(outputFile, new OpenOption[0]);
        Throwable throwable = null;
        try {
            stream.write("Bits;Customers;Proof Time;Verify time;Participation verify;File size\n".getBytes());
            int[] numberOfCustomers = new int[]{100, 200000, 2000000};
            int[] numberOfBits = new int[]{51};
            BalanceProofSystem proofSystem = new BalanceProofSystem();
            BalanceVerifier verifier = new BalanceVerifier();
            ParticipationVerifier participationVerifier = new ParticipationVerifier();
            for (int customers : numberOfCustomers) {
                for (int bits : numberOfBits) {
                    long startClearing = System.currentTimeMillis();
                    System.out.println("Starting to clear proofs ");
                    Database.clearProofs();
                    long endClearing = System.currentTimeMillis();
                    System.out.println("Took " + (double)(endClearing - startClearing) / 1000.0 + "s");
                    ExperimentUtils.generateRandomCustomers(customers, bits);
                    long endCreating = System.currentTimeMillis();
                    System.out.println("Took " + (double)(endCreating - endClearing) / 1000.0 + "s to create customers");
                    BalanceProofData data = new BalanceProofData(bits);
                    long startProof = System.currentTimeMillis();
                    BalanceProof proof = proofSystem.createProof(data);
                    long endProof = System.currentTimeMillis();
                    System.out.println("Finished balance proof in " + (endProof - startProof) / 1000 + "s");
                    verifier.verify(proof, new BalanceVerificationData(bits));
                    long endVerifyFull = System.currentTimeMillis();
                    System.out.println("Finished verification  in " + (endVerifyFull - endProof) / 1000 + "s");
                    String customer = "C0";
                    SQLCustomerDatabase customerDatabase = new SQLCustomerDatabase();