// Copyright 2018 Chia Network Inc // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include "cxxopts.hpp" #include "../lib/include/picosha2.hpp" #include "plotter_disk.hpp" #include "prover_disk.hpp" #include "verifier.hpp" using std::string; using std::vector; using std::endl; using std::cout; void HexToBytes(const string &hex, uint8_t *result) { for (uint32_t i = 0; i < hex.length(); i += 2) { string byteString = hex.substr(i, 2); uint8_t byte = (uint8_t)strtol(byteString.c_str(), NULL, 16); result[i / 2] = byte; } } vector intToBytes(uint32_t paramInt, uint32_t numBytes) { vector arrayOfByte(numBytes, 0); for (uint32_t i = 0; paramInt > 0; i++) { arrayOfByte[numBytes - i - 1] = paramInt & 0xff; paramInt >>= 8; } return arrayOfByte; } string Strip0x(const string &hex) { if (hex.size() > 1 && (hex.substr(0, 2) == "0x" || hex.substr(0, 2) == "0X")) { return hex.substr(2); } return hex; } void HelpAndQuit(cxxopts::Options options) { cout << options.help({""}) << endl; cout << "./ProofOfSpace create" << endl; cout << "./ProofOfSpace prove " << endl; cout << "./ProofOfSpace verify " << endl; cout << "./ProofOfSpace check" << endl; exit(0); } int main(int argc, char *argv[]) try { cxxopts::Options options( "ProofOfSpace", "Utility for plotting, generating and verifying proofs of space."); options.positional_help("(create/prove/verify/check) param1 param2 ") .show_positional_help(); // Default values uint8_t k = 20; uint32_t num_buckets = 0; uint32_t num_stripes = 0; uint8_t num_threads = 0; string filename = "plot.dat"; string tempdir = "."; string tempdir2 = "."; string finaldir = "."; string operation = "help"; string memo = "0102030405"; string id = "022fb42c08c12de3a6af053880199806532e79515f94e83461612101f9412f9e"; bool nobitfield = false; bool show_progress = false; uint32_t buffmegabytes = 0; options.allow_unrecognised_options().add_options()( "k, size", "Plot size", cxxopts::value(k))( "r, threads", "Number of threads", cxxopts::value(num_threads))( "u, buckets", "Number of buckets", cxxopts::value(num_buckets))( "s, stripes", "Size of stripes", cxxopts::value(num_stripes))( "t, tempdir", "Temporary directory", cxxopts::value(tempdir))( "2, tempdir2", "Second Temporary directory", cxxopts::value(tempdir2))( "d, finaldir", "Final directory", cxxopts::value(finaldir))( "f, file", "Filename", cxxopts::value(filename))( "m, memo", "Memo to insert into the plot", cxxopts::value(memo))( "i, id", "Unique 32-byte seed for the plot", cxxopts::value(id))( "e, nobitfield", "Disable bitfield", cxxopts::value(nobitfield))( "b, buffer", "Megabytes to be used as buffer for sorting and plotting", cxxopts::value(buffmegabytes))( "p, progress", "Display progress percentage during plotting", cxxopts::value(show_progress))( "help", "Print help"); auto result = options.parse(argc, argv); if (result.count("help") || argc < 2) { HelpAndQuit(options); } operation = argv[1]; std::cout << "operation: " << operation << std::endl; if (operation == "help") { HelpAndQuit(options); } else if (operation == "create") { cout << "Generating plot for k=" << static_cast(k) << " filename=" << filename << " id=" << id << endl << endl; id = Strip0x(id); if (id.size() != 64) { cout << "Invalid ID, should be 32 bytes (hex)" << endl; exit(1); } memo = Strip0x(memo); if (memo.size() % 2 != 0) { cout << "Invalid memo, should be only whole bytes (hex)" << endl; exit(1); } std::vector memo_bytes(memo.size() / 2); std::array id_bytes; HexToBytes(memo, memo_bytes.data()); HexToBytes(id, id_bytes.data()); DiskPlotter plotter = DiskPlotter(); plotter.CreatePlotDisk( tempdir, tempdir2, finaldir, filename, k, memo_bytes.data(), memo_bytes.size(), id_bytes.data(), id_bytes.size(), buffmegabytes, num_buckets, num_stripes, num_threads, nobitfield, show_progress); } else if (operation == "prove") { if (argc < 3) { HelpAndQuit(options); } cout << "Proving using filename=" << filename << " challenge=" << argv[2] << endl << endl; string challenge = Strip0x(argv[2]); if (challenge.size() != 64) { cout << "Invalid challenge, should be 32 bytes" << endl; exit(1); } uint8_t challenge_bytes[32]; HexToBytes(challenge, challenge_bytes); DiskProver prover(filename); try { vector qualities = prover.GetQualitiesForChallenge(challenge_bytes); for (uint32_t i = 0; i < qualities.size(); i++) { k = prover.GetSize(); uint8_t *proof_data = new uint8_t[8 * k]; LargeBits proof = prover.GetFullProof(challenge_bytes, i); proof.ToBytes(proof_data); cout << "Proof: 0x" << Util::HexStr(proof_data, k * 8) << endl; delete[] proof_data; } if (qualities.empty()) { cout << "No proofs found." << endl; exit(1); } } catch (const std::exception& ex) { std::cout << "Error proving. " << ex.what() << std::endl; exit(1); } catch (...) { std::cout << "Error proving. " << std::endl; exit(1); } } else if (operation == "verify") { if (argc < 4) { HelpAndQuit(options); } Verifier verifier = Verifier(); id = Strip0x(id); string proof = Strip0x(argv[2]); string challenge = Strip0x(argv[3]); if (id.size() != 64) { cout << "Invalid ID, should be 32 bytes" << endl; exit(1); } if (challenge.size() != 64) { cout << "Invalid challenge, should be 32 bytes" << endl; exit(1); } if (proof.size() % 16) { cout << "Invalid proof, should be a multiple of 8 bytes" << endl; exit(1); } k = proof.size() / 16; cout << "Verifying proof=" << argv[2] << " for challenge=" << argv[3] << " and k=" << static_cast(k) << endl << endl; uint8_t id_bytes[32]; uint8_t challenge_bytes[32]; uint8_t *proof_bytes = new uint8_t[proof.size() / 2]; HexToBytes(id, id_bytes); HexToBytes(challenge, challenge_bytes); HexToBytes(proof, proof_bytes); LargeBits quality = verifier.ValidateProof(id_bytes, k, challenge_bytes, proof_bytes, k * 8); if (quality.GetSize() == 256) { cout << "Proof verification suceeded. Quality: " << quality << endl; } else { cout << "Proof verification failed." << endl; exit(1); } delete[] proof_bytes; } else if (operation == "check") { uint32_t iterations = 1000; if (argc == 3) { iterations = std::stoi(argv[2]); } DiskProver prover(filename); Verifier verifier = Verifier(); uint32_t success = 0; uint8_t id_bytes[32]; prover.GetId(id_bytes); k = prover.GetSize(); for (uint32_t num = 0; num < iterations; num++) { vector hash_input = intToBytes(num, 4); hash_input.insert(hash_input.end(), &id_bytes[0], &id_bytes[32]); vector hash(picosha2::k_digest_size); picosha2::hash256(hash_input.begin(), hash_input.end(), hash.begin(), hash.end()); try { vector qualities = prover.GetQualitiesForChallenge(hash.data()); for (uint32_t i = 0; i < qualities.size(); i++) { LargeBits proof = prover.GetFullProof(hash.data(), i); uint8_t *proof_data = new uint8_t[proof.GetSize() / 8]; proof.ToBytes(proof_data); cout << "i: " << num << std::endl; cout << "challenge: 0x" << Util::HexStr(hash.data(), 256 / 8) << endl; cout << "proof: 0x" << Util::HexStr(proof_data, k * 8) << endl; LargeBits quality = verifier.ValidateProof(id_bytes, k, hash.data(), proof_data, k * 8); if (quality.GetSize() == 256 && quality == qualities[i]) { cout << "quality: " << quality << endl; cout << "Proof verification suceeded. k = " << static_cast(k) << endl; success++; } else { cout << "Proof verification failed." << endl; } delete[] proof_data; } } catch (const std::exception& error) { cout << "Threw: " << error.what() << endl; continue; } } std::cout << "Total success: " << success << "/" << iterations << ", " << (success * 100 / static_cast(iterations)) << "%." << std::endl; if (show_progress) { progress(4, 1, 1); } } else { cout << "Invalid operation. Use create/prove/verify/check" << endl; } return 0; } catch (const cxxopts::OptionException &e) { cout << "error parsing options: " << e.what() << endl; return 1; } catch (const std::exception &e) { std::cerr << "Caught exception: " << e.what() << endl; throw e; }