1 // Copyright 2018 Chia Network Inc
2 
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 
7 //    http://www.apache.org/licenses/LICENSE-2.0
8 
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <set>
16 #include <ctime>
17 
18 #include "../lib/include/picosha2.hpp"
19 #include "../lib/include/cxxopts.hpp"
20 
21 #include "plotter_disk.hpp"
22 #include "prover_disk.hpp"
23 #include "verifier.hpp"
24 
HexToBytes(const string & hex,uint8_t * result)25 void HexToBytes(const string& hex, uint8_t* result) {
26     for (uint32_t i = 0; i < hex.length(); i += 2) {
27         string byteString = hex.substr(i, 2);
28         uint8_t byte = (uint8_t) strtol(byteString.c_str(), NULL, 16);
29         result[i/2] = byte;
30   }
31 }
32 
intToBytes(uint32_t paramInt,uint32_t numBytes)33 vector<unsigned char> intToBytes(uint32_t paramInt, uint32_t numBytes) {
34     vector<unsigned char> arrayOfByte(numBytes, 0);
35     for (uint32_t i = 0; paramInt > 0; i++) {
36         arrayOfByte[numBytes - i - 1] = paramInt & 0xff;
37         paramInt >>= 8;
38     }
39     return arrayOfByte;
40 }
41 
Strip0x(const string & hex)42 string Strip0x(const string& hex) {
43     if (hex.substr(0, 2) == "0x" || hex.substr(0, 2) == "0X") {
44         return hex.substr(2);
45     }
46     return hex;
47 }
48 
HelpAndQuit(cxxopts::Options options)49 void HelpAndQuit(cxxopts::Options options) {
50     cout << options.help({""}) << endl;
51     cout << "./ProofOfSpace generate" << endl;
52     cout << "./ProofOfSpace prove <challenge>" << endl;
53     cout << "./ProofOfSpace verify <challenge> <proof>" << endl;
54     cout << "./ProofOfSpace check" << endl;
55     exit(0);
56 }
57 
main(int argc,char * argv[])58 int main(int argc, char *argv[]) {
59     try {
60         cxxopts::Options options("ProofOfSpace", "Utility for plotting, generating and verifying proofs of space.");
61         options.positional_help("(generate/prove/verify/check) param1 param2 ")
62                .show_positional_help();
63 
64         // Default values
65         uint8_t k = 20;
66         string filename = "plot.dat";
67         string operation = "help";
68         string memo  = "0102030405";
69         string id = "022fb42c08c12de3a6af053880199806532e79515f94e83461612101f9412f9e";
70 
71         options.allow_unrecognised_options()
72                .add_options()
73                 ("k, size", "Plot size", cxxopts::value<uint8_t>(k))
74                 ("f, file", "Filename", cxxopts::value<string>(filename))
75                 ("m, memo", "Memo to insert into the plot", cxxopts::value<string>(memo))
76                 ("i, id", "Unique 32-byte id for the plot", cxxopts::value<string>(id))
77                 ("help", "Print help");
78 
79         auto result = options.parse(argc, argv);
80 
81         if (result.count("help") || argc < 2) {
82             HelpAndQuit(options);
83         }
84         operation = argv[1];
85 
86         if (operation == "help") {
87             HelpAndQuit(options);
88         } else if (operation == "generate") {
89             cout << "Generating plot for k=" << static_cast<int>(k) << " filename="
90                  << filename << " id=" << id << endl << endl;
91             if (id.size() != 64) {
92                 cout << "Invalid ID, should be 32 bytes" << endl;
93                 exit(1);
94             }
95             memo = Strip0x(memo);
96             id = Strip0x(id);
97             uint8_t memo_bytes[memo.size() / 2];
98             uint8_t id_bytes[32];
99 
100             HexToBytes(memo, memo_bytes);
101             HexToBytes(id, id_bytes);
102 
103             DiskPlotter plotter = DiskPlotter();
104             plotter.CreatePlotDisk(filename, k, memo_bytes, 5, id_bytes, 32);
105         } else if (operation == "prove") {
106             if (argc < 3) {
107                 HelpAndQuit(options);
108             }
109             cout << "Proving using filename=" << filename << " challenge=" << argv[2] << endl << endl;
110             string challenge = Strip0x(argv[2]);
111             if (challenge.size() != 64) {
112                 cout << "Invalid challenge, should be 32 bytes" << endl;
113                 exit(1);
114             }
115             uint8_t challenge_bytes[32];
116             HexToBytes(challenge, challenge_bytes);
117 
118             DiskProver prover(filename);
119             vector<LargeBits> qualities = prover.GetQualitiesForChallenge(challenge_bytes);
120             for (uint32_t i = 0; i < qualities.size(); i++) {
121                 k = qualities[i].GetSize() / 2;
122                 uint8_t proof_data[8 * k];
123                 LargeBits proof = prover.GetFullProof(challenge_bytes, i);
124                 proof.ToBytes(proof_data);
125                 cout << "Proof: 0x" << Util::HexStr(proof_data, k * 8) << endl;
126             }
127             if (qualities.empty()) {
128                 cout << "No proofs found." << endl;
129                 exit(1);
130             }
131         } else if (operation == "verify") {
132             if (argc < 4) {
133                 HelpAndQuit(options);
134             }
135             cout << "Verifying proof=" << argv[2] << " for challenge=" << argv[3] << " and k="
136                  << static_cast<int>(k) << endl << endl;
137             Verifier verifier = Verifier();
138 
139             id = Strip0x(id);
140             string proof = Strip0x(argv[2]);
141             string challenge = Strip0x(argv[3]);
142             if (id.size() != 64) {
143                 cout << "Invalid ID, should be 32 bytes" << endl;
144                 exit(1);
145             }
146             if (challenge.size() != 64) {
147                 cout << "Invalid challenge, should be 32 bytes" << endl;
148                 exit(1);
149             }
150             uint8_t id_bytes[32];
151             uint8_t challenge_bytes[32];
152             uint8_t proof_bytes[proof.size() / 2];
153             HexToBytes(id, id_bytes);
154             HexToBytes(challenge, challenge_bytes);
155             HexToBytes(proof, proof_bytes);
156 
157             LargeBits quality = verifier.ValidateProof(id_bytes, k, challenge_bytes, proof_bytes, k*8);
158             if (quality.GetSize() == 2*k) {
159                 cout << "Proof verification suceeded. Quality: " << quality << endl;
160             } else {
161                 cout << "Proof verification failed." << endl;
162                 exit(1);
163             }
164         } else if (operation == "check") {
165             uint32_t iterations = 1000;
166             if (argc == 3) {
167                 iterations = stoi(argv[2]);
168             }
169 
170             DiskProver prover(filename);
171             Verifier verifier = Verifier();
172 
173             uint32_t success = 0;
174             id = Strip0x(id);
175             uint8_t id_bytes[32];
176             HexToBytes(id, id_bytes);
177 
178             for (uint32_t num = 0; num < iterations; num++) {
179                 vector<unsigned char> hash_input = intToBytes(num, 4);
180                 vector<unsigned char> hash(picosha2::k_digest_size);
181                 picosha2::hash256(hash_input.begin(), hash_input.end(), hash.begin(), hash.end());
182 
183                 vector<LargeBits> qualities = prover.GetQualitiesForChallenge(hash.data());
184                 for (uint32_t i = 0; i < qualities.size(); i++) {
185                     k = qualities[i].GetSize() / 2;
186                     LargeBits proof = prover.GetFullProof(hash.data(), i);
187                     uint8_t proof_data[proof.GetSize() / 8];
188                     proof.ToBytes(proof_data);
189                     cout << "i: " << num << " Proof: 0x" << Util::HexStr(proof_data, k * 8) << endl;
190                     LargeBits quality = verifier.ValidateProof(id_bytes, k, hash.data(), proof_data, k*8);
191                     if (quality.GetSize() == 2*k) {
192                         cout << "Proof verification suceeded. k = " << static_cast<int>(k)
193                              << " Quality: " << quality << endl;
194                         success++;
195                     } else {
196                         cout << "Proof verification failed." << endl;
197                         exit(1);
198                     }
199                 }
200             }
201             std::cout << "Total success: " << success << "/" << iterations << ", " <<
202                          (success/static_cast<double>(iterations)) << "%." << std::endl;
203         } else {
204             cout << "Invalid operation. Use generate/prove/verify/check" << endl;
205         }
206         exit(0);
207     } catch (const cxxopts::OptionException& e) {
208         cout << "error parsing options: " << e.what() << endl;
209         exit(1);
210     }
211 }
212