4841c5e0eb89ce7bb29991899c8919ac9bf22783
[novacoin.git] / src / zerocoin / Coin.cpp
1 /**
2  * @file       Coin.cpp
3  *
4  * @brief      PublicCoin and PrivateCoin classes for the Zerocoin library.
5  *
6  * @author     Ian Miers, Christina Garman and Matthew Green
7  * @date       June 2013
8  *
9  * @copyright  Copyright 2013 Ian Miers, Christina Garman and Matthew Green
10  * @license    This project is released under the MIT license.
11  **/
12
13 #include <stdexcept>
14 #include "Zerocoin.h"
15
16 namespace libzerocoin {
17
18 //PublicCoin class
19 PublicCoin::PublicCoin(const Params* p):
20         params(p), denomination(ZQ_LOVELACE) {
21         if(this->params->initialized == false) {
22                 throw std::invalid_argument("Params are not initialized");
23         }
24 };
25
26 PublicCoin::PublicCoin(const Params* p, const Bignum& coin, const CoinDenomination d):
27         params(p), value(coin), denomination(d) {
28         if(this->params->initialized == false) {
29                 throw std::invalid_argument("Params are not initialized");
30         }
31 };
32
33 bool PublicCoin::operator==(const PublicCoin& rhs) const {
34         return this->value == rhs.value;// FIXME check param equality
35 }
36
37 bool PublicCoin::operator!=(const PublicCoin& rhs) const {
38         return !(*this == rhs);
39 }
40
41 const Bignum& PublicCoin::getValue() const {
42         return this->value;
43 }
44
45 const CoinDenomination PublicCoin::getDenomination() const {
46         return static_cast<CoinDenomination>(this->denomination);
47 }
48
49 bool PublicCoin::validate() const {
50         return (this->params->accumulatorParams.minCoinValue < value) && (value < this->params->accumulatorParams.maxCoinValue) && value.isPrime(params->zkp_iterations);
51 }
52
53 //PrivateCoin class
54 PrivateCoin::PrivateCoin(const Params* p, const CoinDenomination denomination): params(p), publicCoin(p) {
55         // Verify that the parameters are valid
56         if(this->params->initialized == false) {
57                 throw std::invalid_argument("Params are not initialized");
58         }
59
60 #ifdef ZEROCOIN_FAST_MINT
61         // Mint a new coin with a random serial number using the fast process.
62         // This is more vulnerable to timing attacks so don't mint coins when
63         // somebody could be timing you.
64         this->mintCoinFast(denomination);
65 #else
66         // Mint a new coin with a random serial number using the standard process.
67         this->mintCoin(denomination);
68 #endif
69         
70 }
71
72 /**
73  *
74  * @return the coins serial number
75  */
76 const Bignum& PrivateCoin::getSerialNumber() const {
77         return this->serialNumber;
78 }
79
80 const Bignum& PrivateCoin::getRandomness() const {
81         return this->randomness;
82 }
83
84 void PrivateCoin::mintCoin(const CoinDenomination denomination) {
85         // Repeat this process up to MAX_COINMINT_ATTEMPTS times until
86         // we obtain a prime number
87         for(uint32_t attempt = 0; attempt < MAX_COINMINT_ATTEMPTS; attempt++) {
88
89                 // Generate a random serial number in the range 0...{q-1} where
90                 // "q" is the order of the commitment group.
91                 Bignum s = Bignum::randBignum(this->params->coinCommitmentGroup.groupOrder);
92
93                 // Generate a Pedersen commitment to the serial number "s"
94                 Commitment coin(&params->coinCommitmentGroup, s);
95
96                 // Now verify that the commitment is a prime number
97                 // in the appropriate range. If not, we'll throw this coin
98                 // away and generate a new one.
99                 if (coin.getCommitmentValue().isPrime(ZEROCOIN_MINT_PRIME_PARAM) &&
100                         coin.getCommitmentValue() >= params->accumulatorParams.minCoinValue &&
101                         coin.getCommitmentValue() <= params->accumulatorParams.maxCoinValue) {
102                         // Found a valid coin. Store it.
103                         this->serialNumber = s;
104                         this->randomness = coin.getRandomness();
105                         this->publicCoin = PublicCoin(params,coin.getCommitmentValue(), denomination);
106
107                         // Success! We're done.
108                         return;
109                 }
110         }
111
112         // We only get here if we did not find a coin within
113         // MAX_COINMINT_ATTEMPTS. Throw an exception.
114         throw ZerocoinException("Unable to mint a new Zerocoin (too many attempts)");
115 }
116
117 void PrivateCoin::mintCoinFast(const CoinDenomination denomination) {
118         
119         // Generate a random serial number in the range 0...{q-1} where
120         // "q" is the order of the commitment group.
121         Bignum s = Bignum::randBignum(this->params->coinCommitmentGroup.groupOrder);
122         
123         // Generate a random number "r" in the range 0...{q-1}
124         Bignum r = Bignum::randBignum(this->params->coinCommitmentGroup.groupOrder);
125         
126         // Manually compute a Pedersen commitment to the serial number "s" under randomness "r"
127         // C = g^s * h^r mod p
128         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);
129         
130         // Repeat this process up to MAX_COINMINT_ATTEMPTS times until
131         // we obtain a prime number
132         for (uint32_t attempt = 0; attempt < MAX_COINMINT_ATTEMPTS; attempt++) {
133                 // First verify that the commitment is a prime number
134                 // in the appropriate range. If not, we'll throw this coin
135                 // away and generate a new one.
136                 if (commitmentValue.isPrime(ZEROCOIN_MINT_PRIME_PARAM) &&
137                         commitmentValue >= params->accumulatorParams.minCoinValue &&
138                         commitmentValue <= params->accumulatorParams.maxCoinValue) {
139                         // Found a valid coin. Store it.
140                         this->serialNumber = s;
141                         this->randomness = r;
142                         this->publicCoin = PublicCoin(params, commitmentValue, denomination);
143                                 
144                         // Success! We're done.
145                         return;
146                 }
147                 
148                 // Generate a new random "r_delta" in 0...{q-1}
149                 Bignum r_delta = Bignum::randBignum(this->params->coinCommitmentGroup.groupOrder);
150
151                 // The commitment was not prime. Increment "r" and recalculate "C":
152                 // r = r + r_delta mod q
153                 // C = C * h mod p
154                 r = (r + r_delta) % this->params->coinCommitmentGroup.groupOrder;
155                 commitmentValue = commitmentValue.mul_mod(this->params->coinCommitmentGroup.h.pow_mod(r_delta, this->params->coinCommitmentGroup.modulus), this->params->coinCommitmentGroup.modulus);
156         }
157                 
158         // We only get here if we did not find a coin within
159         // MAX_COINMINT_ATTEMPTS. Throw an exception.
160         throw ZerocoinException("Unable to mint a new Zerocoin (too many attempts)");
161 }
162         
163 const PublicCoin& PrivateCoin::getPublicCoin() const {
164         return this->publicCoin;
165 }
166
167 } /* namespace libzerocoin */