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