text: /**
   * @file       Coin.cpp
   *
   * @brief      PublicCoin and PrivateCoin classes for the Zerocoin library.
   *
   * @author     Ian Miers, Christina Garman and Matthew Green
   * @date       June 2013
   *
   * @copyright  Copyright 2013 Ian Miers, Christina Garman and Matthew Green
   * @license    This project is released under the MIT license.
   **/
  
  #include <stdexcept>
  #include "Zerocoin.h"
  
  namespace libzerocoin {
  
  //PublicCoin class
  PublicCoin::PublicCoin(const Params* p):
      params(p), denomination(ZQ_LOVELACE) {
  	if (this->params->initialized == false) {
  		throw ZerocoinException("Params are not initialized");
  	}
  };
  
  PublicCoin::PublicCoin(const Params* p, const Bignum& coin, const CoinDenomination d):
  	params(p), value(coin), denomination(d) {
  	if (this->params->initialized == false) {
  		throw ZerocoinException("Params are not initialized");
  	}
  };
  
  bool PublicCoin::operator==(const PublicCoin& rhs) const {
  	return this->value == rhs.value; // FIXME check param equality
  }
  
  bool PublicCoin::operator!=(const PublicCoin& rhs) const {
  	return !(*this == rhs);
  }
  
  const Bignum& PublicCoin::getValue() const {
  	return this->value;
  }
  
  const CoinDenomination PublicCoin::getDenomination() const {
  	return static_cast<CoinDenomination>(this->denomination);
  }
  
  bool PublicCoin::validate() const{
      return (this->params->accumulatorParams.minCoinValue < value) && (value < this->params->accumulatorParams.maxCoinValue) && value.isPrime(params->zkp_iterations);
  }
  
  //PrivateCoin class
  PrivateCoin::PrivateCoin(const Params* p, const CoinDenomination denomination): params(p), publicCoin(p) {
  	// Verify that the parameters are valid
  	if(this->params->initialized == false) {
  		throw ZerocoinException("Params are not initialized");
  	}
  
  #ifdef ZEROCOIN_FAST_MINT
  	// Mint a new coin with a random serial number using the fast process.
  	// This is more vulnerable to timing attacks so don't mint coins when
  	// somebody could be timing you.
  	this->mintCoinFast(denomination);
  #else
  	// Mint a new coin with a random serial number using the standard process.
  	this->mintCoin(denomination);
  #endif
  	
  }
  
  /**
   *
   * @return the coins serial number
   */
  const Bignum& PrivateCoin::getSerialNumber() const {
  	return this->serialNumber;
  }
  
  const Bignum& PrivateCoin::getRandomness() const {
  	return this->randomness;
  }
  
  void PrivateCoin::mintCoin(const CoinDenomination denomination) {
  	// Repeat this process up to MAX_COINMINT_ATTEMPTS times until
  	// we obtain a prime number
  	for(uint32_t attempt = 0; attempt < MAX_COINMINT_ATTEMPTS; attempt++) {
  
  		// Generate a random serial number in the range 0...{q-1} where
  		// "q" is the order of the commitment group.
  		Bignum s = Bignum::randBignum(this->params->coinCommitmentGroup.groupOrder);
  
  		// Generate a Pedersen commitment to the serial number "s"
  		Commitment coin(&params->coinCommitmentGroup, s);
  
  		// Now verify that the commitment is a prime number
  		// in the appropriate range. If not, we'll throw this coin
  		// away and generate a new one.
  		if (coin.getCommitmentValue().isPrime(ZEROCOIN_MINT_PRIME_PARAM) &&
  		        coin.getCommitmentValue() >= params->accumulatorParams.minCoinValue &&
  		        coin.getCommitmentValue() <= params->accumulatorParams.maxCoinValue) {
  			// Found a valid coin. Store it.
  			this->serialNumber = s;
  			this->randomness = coin.getRandomness();
  			this->publicCoin = PublicCoin(params,coin.getCommitmentValue(), denomination);
  
  			// Success! We're done.
  			return;
  		}
  	}
  
  	// We only get here if we did not find a coin within
  	// MAX_COINMINT_ATTEMPTS. Throw an exception.
  	throw ZerocoinException("Unable to Renew a coin (too many attempts)");
  }
  
  void PrivateCoin::mintCoinFast(const CoinDenomination denomination) {
  	
  	// Generate a random serial number in the range 0...{q-1} where
  	// "q" is the order of the commitment group.
  	Bignum s = Bignum::randBignum(this->params->coinCommitmentGroup.groupOrder);
  	
  	// Generate a random number "r" in the range 0...{q-1}
  	Bignum r = Bignum::randBignum(this->params->coinCommitmentGroup.groupOrder);
  	
  	// Manually compute a Pedersen commitment to the serial number "s" under randomness "r"
  	// C = g^s * h^r mod p
  	Bignum commitmentValue = this->params->coinCommitmentGroup.g.pow_mod(s, this->params->coinCommitmentGroup.modulus).mul_mod(this->params->coinCommitmentGroup.h.pow_mod(r, this->params->coinCommitmentGroup.modulus), this->params->coinCommitmentGroup.modulus);
  	
  	// Repeat this process up to MAX_COINMINT_ATTEMPTS times until
  	// we obtain a prime number
  	for (uint32_t attempt = 0; attempt < MAX_COINMINT_ATTEMPTS; attempt++) {
  		// First verify that the commitment is a prime number
  		// in the appropriate range. If not, we'll throw this coin
  		// away and generate a new one.
  		if (commitmentValue.isPrime(ZEROCOIN_MINT_PRIME_PARAM) &&
  			commitmentValue >= params->accumulatorParams.minCoinValue &&
  			commitmentValue <= params->accumulatorParams.maxCoinValue) {
  			// Found a valid coin. Store it.
  			this->serialNumber = s;
  			this->randomness = r;
  			this->publicCoin = PublicCoin(params, commitmentValue, denomination);
  				
  			// Success! We're done.
  			return;
  		}
  		
  		// Generate a new random "r_delta" in 0...{q-1}
  		Bignum r_delta = Bignum::randBignum(this->params->coinCommitmentGroup.groupOrder);
  
  		// The commitment was not prime. Increment "r" and recalculate "C":
  		// r = r + r_delta mod q
  		// C = C * h mod p
  		r = (r + r_delta) % this->params->coinCommitmentGroup.groupOrder;
  		commitmentValue = commitmentValue.mul_mod(this->params->coinCommitmentGroup.h.pow_mod(r_delta, this->params->coinCommitmentGroup.modulus), this->params->coinCommitmentGroup.modulus);
  	}
  		
  	// We only get here if we did not find a coin within
  	// MAX_COINMINT_ATTEMPTS. Throw an exception.
  	throw ZerocoinException("Unable to Renew a coin (too many attempts)");
  }
  	
  const PublicCoin& PrivateCoin::getPublicCoin() const {
  	return this->publicCoin;
  }
  
  } /* namespace libzerocoin */
  