Remove ncurses dependency
[novacoin.git] / src / zerocoin / ZeroTest.cpp
1 /**
2 * @file       Tests.cpp
3 *
4 * @brief      Test routines for Zerocoin.
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 using namespace std;
14
15 #include <string>
16 #include <iostream>
17 #include <fstream>
18 #include <exception>
19 #include "Zerocoin.h"
20 #include "../util.h"
21
22 using namespace libzerocoin;
23 extern Params* ZCParams;
24
25
26 #define TESTS_COINS_TO_ACCUMULATE   10
27
28 // Global test counters
29 uint32_t    gNumTests        = 0;
30 uint32_t    gSuccessfulTests = 0;
31
32 // Proof size
33 uint32_t    gProofSize                  = 0;
34 uint32_t    gCoinSize                   = 0;
35 uint32_t        gSerialNumberSize       = 0;
36
37 // Global coin array
38 PrivateCoin    *gCoins[TESTS_COINS_TO_ACCUMULATE];
39
40 // Global params
41 Params *g_Params;
42
43 //////////
44 // Utility routines
45 //////////
46
47 void
48 LogTestResult(string testName, bool (*testPtr)())
49 {
50         printf("Testing if %s ...\n", testName.c_str());
51
52         bool testResult = testPtr();
53
54         if (testResult == true) {
55                 printf("\t[PASS]\n");
56                 gSuccessfulTests++;
57         } else {
58                 printf("\t[FAIL]\n");
59         }
60
61         gNumTests++;
62 }
63
64 Bignum
65 GetTestModulus()
66 {
67         static Bignum testModulus(0);
68
69         // TODO: should use a hard-coded RSA modulus for testing
70         if (!testModulus) {
71                 Bignum p, q;
72
73                 // Note: we are NOT using safe primes for testing because
74                 // they take too long to generate. Don't do this in real
75                 // usage. See the paramgen utility for better code.
76                 p = Bignum::generatePrime(1024, false);
77                 q = Bignum::generatePrime(1024, false);
78                 testModulus = p * q;
79         }
80
81         return testModulus;
82 }
83
84 //////////
85 // Test routines
86 //////////
87
88 bool
89 Test_GenRSAModulus()
90 {
91         Bignum result = GetTestModulus();
92
93         if (!result) {
94                 return false;
95         }
96         else {
97                 return true;
98         }
99 }
100
101 bool
102 Test_CalcParamSizes()
103 {
104         bool result = true;
105 #if 0
106
107         uint32_t pLen, qLen;
108
109         try {
110                 calculateGroupParamLengths(4000, 80, &pLen, &qLen);
111                 if (pLen < 1024 || qLen < 256) {
112                         result = false;
113                 }
114                 calculateGroupParamLengths(4000, 96, &pLen, &qLen);
115                 if (pLen < 2048 || qLen < 256) {
116                         result = false;
117                 }
118                 calculateGroupParamLengths(4000, 112, &pLen, &qLen);
119                 if (pLen < 3072 || qLen < 320) {
120                         result = false;
121                 }
122                 calculateGroupParamLengths(4000, 120, &pLen, &qLen);
123                 if (pLen < 3072 || qLen < 320) {
124                         result = false;
125                 }
126                 calculateGroupParamLengths(4000, 128, &pLen, &qLen);
127                 if (pLen < 3072 || qLen < 320) {
128                         result = false;
129                 }
130         } catch (exception &e) {
131                 result = false;
132         }
133 #endif
134
135         return result;
136 }
137
138 bool
139 Test_GenerateGroupParams()
140 {
141         int32_t pLen = 1024, qLen = 256, count;
142         IntegerGroupParams group;
143
144         for (count = 0; count < 1; count++) {
145
146                 try {
147                         group = deriveIntegerGroupParams(calculateSeed(GetTestModulus(), "test", ZEROCOIN_DEFAULT_SECURITYLEVEL, "TEST GROUP"), pLen, qLen);
148                 } catch (std::runtime_error e) {
149                         printf("Caught exception %s\n", e.what());
150                         return false;
151                 }
152
153                 // Now perform some simple tests on the resulting parameters
154                 if (group.groupOrder.bitSize() < qLen || group.modulus.bitSize() < pLen) {
155                         return false;
156                 }
157
158                 Bignum c = group.g.pow_mod(group.groupOrder, group.modulus);
159                 if (!(c.isOne())) return false;
160
161                 // Try at multiple parameter sizes
162                 pLen = pLen * 1.5;
163                 qLen = qLen * 1.5;
164         }
165
166         return true;
167 }
168
169 bool
170 Test_ParamGen()
171 {
172         bool result = true;
173
174         try {
175                 // Instantiating testParams runs the parameter generation code
176                 Params testParams(GetTestModulus(),ZEROCOIN_DEFAULT_SECURITYLEVEL);
177         } catch (runtime_error e) {
178                 printf("ParamGen exception %s\n", e.what());
179                 result = false;
180         }
181
182         return result;
183 }
184
185 bool
186 Test_Accumulator()
187 {
188         // This test assumes a list of coins were generated during
189         // the Test_MintCoin() test.
190         if (gCoins[0] == NULL) {
191                 return false;
192         }
193         try {
194                 // Accumulate the coin list from first to last into one accumulator
195                 Accumulator accOne(&g_Params->accumulatorParams);
196                 Accumulator accTwo(&g_Params->accumulatorParams);
197                 Accumulator accThree(&g_Params->accumulatorParams);
198                 Accumulator accFour(&g_Params->accumulatorParams);
199                 AccumulatorWitness wThree(g_Params, accThree, gCoins[0]->getPublicCoin());
200
201                 for (uint32_t i = 0; i < TESTS_COINS_TO_ACCUMULATE; i++) {
202                         accOne += gCoins[i]->getPublicCoin();
203                         accTwo += gCoins[TESTS_COINS_TO_ACCUMULATE - (i+1)]->getPublicCoin();
204                         accThree += gCoins[i]->getPublicCoin();
205                         wThree += gCoins[i]->getPublicCoin();
206                         if(i != 0) {
207                                 accFour += gCoins[i]->getPublicCoin();
208                         }
209                 }
210
211                 // Compare the accumulated results
212                 if (accOne.getValue() != accTwo.getValue() || accOne.getValue() != accThree.getValue()) {
213                         printf("Accumulators don't match\n");
214                         return false;
215                 }
216
217                 if(accFour.getValue() != wThree.getValue()) {
218                         printf("Witness math not working,\n");
219                         return false;
220                 }
221
222                 // Verify that the witness is correct
223                 if (!wThree.VerifyWitness(accThree, gCoins[0]->getPublicCoin()) ) {
224                         printf("Witness not valid\n");
225                         return false;
226                 }
227
228                 // Serialization test: see if we can serialize the accumulator
229                 CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
230                 ss << accOne;
231
232                 // Deserialize it into a new object
233                 Accumulator newAcc(g_Params, ss);
234
235                 // Compare the results
236                 if (accOne.getValue() != newAcc.getValue()) {
237                         return false;
238                 }
239
240         } catch (runtime_error e) {
241                 return false;
242         }
243
244         return true;
245 }
246
247 bool
248 Test_EqualityPoK()
249 {
250         // Run this test 10 times
251         for (uint32_t i = 0; i < 10; i++) {
252                 try {
253                         // Generate a random integer "val"
254                         Bignum val = Bignum::randBignum(g_Params->coinCommitmentGroup.groupOrder);
255
256                         // Manufacture two commitments to "val", both
257                         // under different sets of parameters
258                         Commitment one(&g_Params->accumulatorParams.accumulatorPoKCommitmentGroup, val);
259
260                         Commitment two(&g_Params->serialNumberSoKCommitmentGroup, val);
261
262                         // Now generate a proof of knowledge that "one" and "two" are
263                         // both commitments to the same value
264                         CommitmentProofOfKnowledge pok(&g_Params->accumulatorParams.accumulatorPoKCommitmentGroup,
265                                                        &g_Params->serialNumberSoKCommitmentGroup,
266                                                        one, two);
267
268                         // Serialize the proof into a stream
269                         CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
270                         ss << pok;
271
272                         // Deserialize back into a PoK object
273                         CommitmentProofOfKnowledge newPok(&g_Params->accumulatorParams.accumulatorPoKCommitmentGroup,
274                                                           &g_Params->serialNumberSoKCommitmentGroup,
275                                                           ss);
276
277                         if (newPok.Verify(one.getCommitmentValue(), two.getCommitmentValue()) != true) {
278                                 return false;
279                         }
280
281                         // Just for fun, deserialize the proof a second time
282                         CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION);
283                         ss2 << pok;
284
285                         // This time tamper with it, then deserialize it back into a PoK
286                         ss2[15] = 0;
287                         CommitmentProofOfKnowledge newPok2(&g_Params->accumulatorParams.accumulatorPoKCommitmentGroup,
288                                                            &g_Params->serialNumberSoKCommitmentGroup,
289                                                            ss2);
290
291                         // If the tampered proof verifies, that's a failure!
292                         if (newPok2.Verify(one.getCommitmentValue(), two.getCommitmentValue()) == true) {
293                                 return false;
294                         }
295
296                 } catch (runtime_error &e) {
297                         return false;
298                 }
299         }
300
301         return true;
302 }
303
304 bool
305 Test_MintCoin()
306 {
307         gCoinSize = 0;
308
309         try {
310                 // Generate a list of coins
311                 for (uint32_t i = 0; i < TESTS_COINS_TO_ACCUMULATE; i++) {
312                         gCoins[i] = new PrivateCoin(g_Params);
313
314                         PublicCoin pc = gCoins[i]->getPublicCoin();
315                         CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
316                         ss << pc;
317                         gCoinSize += ss.size();
318                 }
319
320                 gCoinSize /= TESTS_COINS_TO_ACCUMULATE;
321
322         } catch (exception &e) {
323                 return false;
324         }
325
326         return true;
327 }
328
329 bool
330 Test_MintAndSpend()
331 {
332         try {
333                 // This test assumes a list of coins were generated in Test_MintCoin()
334                 if (gCoins[0] == NULL)
335                 {
336                         // No coins: mint some.
337                         Test_MintCoin();
338                         if (gCoins[0] == NULL) {
339                                 return false;
340                         }
341                 }
342
343                 // Accumulate the list of generated coins into a fresh accumulator.
344                 // The first one gets marked as accumulated for a witness, the
345                 // others just get accumulated normally.
346                 Accumulator acc(&g_Params->accumulatorParams);
347                 AccumulatorWitness wAcc(g_Params, acc, gCoins[0]->getPublicCoin());
348
349                 for (uint32_t i = 0; i < TESTS_COINS_TO_ACCUMULATE; i++) {
350                         acc += gCoins[i]->getPublicCoin();
351                         wAcc +=gCoins[i]->getPublicCoin();
352                 }
353
354                 // Now spend the coin
355                 SpendMetaData m(1,1);
356
357                 CoinSpend spend(g_Params, *(gCoins[0]), acc, wAcc, m);
358
359                 // Serialize the proof and deserialize into newSpend
360                 CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
361                 ss << spend;
362                 gProofSize = ss.size();
363                 CoinSpend newSpend(g_Params, ss);
364
365                 // See if we can verify the deserialized proof (return our result)
366                 bool ret =  newSpend.Verify(acc, m);
367                 
368                 // Extract the serial number
369                 Bignum serialNumber = newSpend.getCoinSerialNumber();
370                 gSerialNumberSize = ceil((double)serialNumber.bitSize() / 8.0);
371                 
372                 return ret;
373         } catch (runtime_error &e) {
374                 printf("MintAndSpend exception %s\n", e.what());
375                 return false;
376         }
377
378         return false;
379 }
380
381 void
382 Test_RunAllTests()
383 {
384         printf("ZeroCoin v%s self-test routine\n", ZEROCOIN_VERSION_STRING);
385
386         // Make a new set of parameters from a random RSA modulus
387         //g_Params = new Params(GetTestModulus());
388         g_Params = ZCParams;
389
390         gNumTests = gSuccessfulTests = gProofSize = 0;
391         for (uint32_t i = 0; i < TESTS_COINS_TO_ACCUMULATE; i++) {
392                 gCoins[i] = NULL;
393         }
394
395         // Run through all of the Zerocoin tests
396         LogTestResult("an RSA modulus can be generated", Test_GenRSAModulus);
397         LogTestResult("parameter sizes are correct", Test_CalcParamSizes);
398         LogTestResult("group/field parameters can be generated", Test_GenerateGroupParams);
399         LogTestResult("parameter generation is correct", Test_ParamGen);
400         LogTestResult("coins can be minted", Test_MintCoin);
401         LogTestResult("the accumulator works", Test_Accumulator);
402         LogTestResult("the commitment equality PoK works", Test_EqualityPoK);
403         LogTestResult("a minted coin can be spent", Test_MintAndSpend);
404
405         printf("\nAverage coin size is %d bytes.\n", gCoinSize);
406         printf("Serial number size is %d bytes.\n", gSerialNumberSize);
407         printf("Spend proof size is %d bytes.\n", gProofSize);
408
409         // Summarize test results
410         if (gSuccessfulTests < gNumTests) {
411                 printf("\nERROR: SOME TESTS FAILED\n");
412         }
413
414         // Clear any generated coins
415         for (uint32_t i = 0; i < TESTS_COINS_TO_ACCUMULATE; i++) {
416                 delete gCoins[i];
417         }
418
419         printf("\n%d out of %d tests passed.\n\n", gSuccessfulTests, gNumTests);
420         delete g_Params;
421 }