diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 9758a7e3..f68dff84 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -20,6 +20,11 @@ jobs: if: startsWith(matrix.os, 'ubuntu') run: | sudo apt-get install libgmp-dev libboost-python-dev libpython3.8-dev libboost-system-dev build-essential -y + sudo fallocate -l 16G /swapfile + sudo chmod 600 /swapfile + sudo mkswap /swapfile + sudo swapon /swapfile + swapon -s cd src make ${{ matrix.config }} -f Makefile.vdf-client diff --git a/src/2weso_test.cpp b/src/2weso_test.cpp index 39019f66..196d28c0 100644 --- a/src/2weso_test.cpp +++ b/src/2weso_test.cpp @@ -15,7 +15,7 @@ void CheckProof(integer& D, Proof& proof, uint64_t iteration) { std::vector bytes; bytes.insert(bytes.end(), proof.y.begin(), proof.y.end()); bytes.insert(bytes.end(), proof.proof.begin(), proof.proof.end()); - if (CheckProofOfTimeNWesolowski(D, DEFAULT_ELEMENT, bytes.data(), bytes.size(), iteration, 1024, proof.witness_type)) { + if (CheckProofOfTimeNWesolowski(D, DEFAULT_ELEMENT, bytes.data(), bytes.size(), iteration, proof.witness_type)) { std::cout << "Correct proof\n"; } else { std::cout << "Incorrect proof\n"; diff --git a/src/prover_test.cpp b/src/prover_test.cpp index 3f7bdece..16705e9c 100644 --- a/src/prover_test.cpp +++ b/src/prover_test.cpp @@ -14,7 +14,7 @@ Proof CreateProof(integer D, ProverManager& pm, uint64_t iteration) { std::vector bytes; bytes.insert(bytes.end(), proof.y.begin(), proof.y.end()); bytes.insert(bytes.end(), proof.proof.begin(), proof.proof.end()); - if (CheckProofOfTimeNWesolowski(D, DEFAULT_ELEMENT, bytes.data(), bytes.size(), iteration, 1024, proof.witness_type)) { + if (CheckProofOfTimeNWesolowski(D, DEFAULT_ELEMENT, bytes.data(), bytes.size(), iteration, proof.witness_type)) { std::cout << "Correct proof"; } else { std::cout << "Incorrect proof\n"; diff --git a/src/python_bindings/fastvdf.cpp b/src/python_bindings/fastvdf.cpp index 583f194c..b8c6a0a5 100644 --- a/src/python_bindings/fastvdf.cpp +++ b/src/python_bindings/fastvdf.cpp @@ -43,7 +43,34 @@ PYBIND11_MODULE(chiavdf, m) { uint8_t *proof_blob_ptr = reinterpret_cast(proof_blob_str.data()); int proof_blob_size = proof_blob.size(); - return CheckProofOfTimeNWesolowski(integer(discriminant), (const uint8_t *)x_s.data(), proof_blob_ptr, proof_blob_size, num_iterations, disc_size_bits, recursion); + return CheckProofOfTimeNWesolowski(integer(discriminant), (const uint8_t *)x_s.data(), proof_blob_ptr, proof_blob_size, num_iterations, recursion); + }); + + // Checks an N wesolowski proof, given y is given by 'GetB()' instead of a form. + m.def("verify_n_wesolowski_with_b", [] (const string& discriminant, + const string& B, + const string& x_s, + const string& proof_blob, + const uint64_t num_iterations, const uint64_t recursion) { + std::string proof_blob_str(proof_blob); + uint8_t *proof_blob_ptr = reinterpret_cast(proof_blob_str.data()); + int proof_blob_size = proof_blob.size(); + std::pair> result; + result = CheckProofOfTimeNWesolowskiWithB(integer(discriminant), integer(B), (const uint8_t *)x_s.data(), proof_blob_ptr, proof_blob_size, num_iterations, recursion); + py::bytes res_bytes = py::bytes(reinterpret_cast(result.second.data()), result.second.size()); + py::tuple res_tuple = py::make_tuple(result.first, res_bytes); + return res_tuple; + }); + + m.def("get_b_from_n_wesolowski", [] (const string& discriminant, + const string& x_s, + const string& proof_blob, + const uint64_t num_iterations, const uint64_t recursion) { + std::string proof_blob_str(proof_blob); + uint8_t *proof_blob_ptr = reinterpret_cast(proof_blob_str.data()); + int proof_blob_size = proof_blob.size(); + integer B = GetBFromProof(integer(discriminant), (const uint8_t *)x_s.data(), proof_blob_ptr, proof_blob_size, num_iterations, recursion); + return B.to_string(); }); m.def("prove", [] (const py::bytes& challenge_hash, const string& x_s, int discriminant_size_bits, uint64_t num_iterations) { diff --git a/src/verifier.h b/src/verifier.h index 293ea706..d15ad63a 100644 --- a/src/verifier.h +++ b/src/verifier.h @@ -11,7 +11,7 @@ const uint8_t DEFAULT_ELEMENT[] = { 0x08 }; -int VerifyWesoSegment(integer &D, form x, form proof, integer &B, uint64_t iters, form &out_y) +int VerifyWesoSegment(integer &D, form x, form proof, integer &B, uint64_t iters, bool skip_check, form &out_y) { PulmarkReducer reducer; integer L = root(-D, 4); @@ -19,7 +19,10 @@ int VerifyWesoSegment(integer &D, form x, form proof, integer &B, uint64_t iters form f1 = FastPowFormNucomp(proof, D, B, L, reducer); form f2 = FastPowFormNucomp(x, D, r, L, reducer); out_y = f1 * f2; - + // Optimize to only get `out_y` without verification, when not needed. + if (skip_check) { + return 0; + } return B == GetB(D, x, out_y) ? 0 : -1; } @@ -41,30 +44,39 @@ void VerifyWesolowskiProof(integer &D, form x, form y, form proof, uint64_t iter } } -bool CheckProofOfTimeNWesolowski(integer D, const uint8_t* x_s, const uint8_t* proof_blob, int32_t proof_blob_len, uint64_t iterations, uint64 disc_size_bits, int32_t depth) -{ +bool CheckProofOfTimeNWesolowskiCommon(integer& D, form& x, const uint8_t* proof_blob, int32_t proof_blob_len, uint64_t& iterations, int last_segment, bool skip_check = false) { int form_size = BQFC_FORM_SIZE; int segment_len = 8 + B_bytes + form_size; int i = proof_blob_len - segment_len; - form x = DeserializeForm(D, x_s, form_size); - if (proof_blob_len != 2 * form_size + depth * segment_len) - return false; - - // Loop depth times - bool is_valid = false; - for (; i >= 2 * form_size; i -= segment_len) { + for (; i >= last_segment; i -= segment_len) { uint64_t segment_iters = BytesToInt64(&proof_blob[i]); form proof = DeserializeForm(D, &proof_blob[i + 8 + B_bytes], form_size); integer B(&proof_blob[i + 8], B_bytes); form xnew; - if (VerifyWesoSegment(D, x, proof, B, segment_iters, xnew)) + if (VerifyWesoSegment(D, x, proof, B, segment_iters, skip_check, xnew)) return false; x = xnew; + if (segment_iters > iterations) { + return false; + } iterations -= segment_iters; } + return true; +} +bool CheckProofOfTimeNWesolowski(integer D, const uint8_t* x_s, const uint8_t* proof_blob, int32_t proof_blob_len, uint64_t iterations, int32_t depth) { + int form_size = BQFC_FORM_SIZE; + int segment_len = 8 + B_bytes + form_size; + form x = DeserializeForm(D, x_s, form_size); + if (proof_blob_len != 2 * form_size + depth * segment_len) { + return false; + } + bool is_valid = CheckProofOfTimeNWesolowskiCommon(D, x, proof_blob, proof_blob_len, iterations, 2 * form_size); + if (is_valid == false) { + return false; + } VerifyWesolowskiProof(D, x, DeserializeForm(D, proof_blob, form_size), DeserializeForm(D, &proof_blob[form_size], form_size), @@ -73,4 +85,42 @@ bool CheckProofOfTimeNWesolowski(integer D, const uint8_t* x_s, const uint8_t* p return is_valid; } +std::pair> CheckProofOfTimeNWesolowskiWithB(integer D, integer B, const uint8_t* x_s, const uint8_t* proof_blob, int32_t proof_blob_len, uint64_t iterations, int32_t depth) { + int form_size = BQFC_FORM_SIZE; + int segment_len = 8 + B_bytes + form_size; + form x = DeserializeForm(D, x_s, form_size); + std::vector result; + if (proof_blob_len != form_size + depth * segment_len) { + return {false, result}; + } + bool is_valid = CheckProofOfTimeNWesolowskiCommon(D, x, proof_blob, proof_blob_len, iterations, form_size); + if (is_valid == false) { + return {false, result}; + } + form proof = DeserializeForm(D, proof_blob, form_size); + form y_result; + if (VerifyWesoSegment(D, x, proof, B, iterations, /*skip_check=*/false, y_result) == -1) { + return {false, result}; + } + int d_bits = D.num_bits(); + result = SerializeForm(y_result, d_bits); + return {true, result}; +} + +// TODO: Perhaps move? +integer GetBFromProof(integer D, const uint8_t* x_s, const uint8_t* proof_blob, int32_t proof_blob_len, uint64_t iterations, int32_t depth) { + int form_size = BQFC_FORM_SIZE; + int segment_len = 8 + B_bytes + form_size; + form x = DeserializeForm(D, x_s, form_size); + if (proof_blob_len != 2 * form_size + depth * segment_len) { + throw std::runtime_error("Invalid proof."); + } + bool is_valid = CheckProofOfTimeNWesolowskiCommon(D, x, proof_blob, proof_blob_len, iterations, 2 * form_size, true); + if (is_valid == false) { + throw std::runtime_error("Invalid proof."); + } + form y = DeserializeForm(D, proof_blob, form_size); + return GetB(D, x, y); +} + #endif // VERIFIER_H diff --git a/src/verifier_test.cpp b/src/verifier_test.cpp index 93d36792..7c96b656 100644 --- a/src/verifier_test.cpp +++ b/src/verifier_test.cpp @@ -34,7 +34,6 @@ std::copy(result.begin(), result.end(), arr); arr, result.size(), 33554432, - 1024, 2); auto challenge_hash1 = HexToBytes(string("a4bb1461ade74ac602e9ae511af68bb254dfe65d61b7faf9fab82d0b4364a30b").data()); diff --git a/tests/test_n_weso_verifier.py b/tests/test_n_weso_verifier.py new file mode 100644 index 00000000..0701409b --- /dev/null +++ b/tests/test_n_weso_verifier.py @@ -0,0 +1,148 @@ +import secrets + +from chiavdf import ( + create_discriminant, + prove, + verify_wesolowski, + verify_n_wesolowski, + verify_n_wesolowski_with_b, + get_b_from_n_wesolowski, +) + + +def prove_n_weso(discriminant_challenge, x, discriminant_size, form_size, iters, witness, wrong_segm): + iters_chunk = iters // (witness + 1) + partials = [] + discriminant = create_discriminant(discriminant_challenge, discriminant_size) + for _ in range(witness): + result = prove(discriminant_challenge, x, discriminant_size, iters_chunk) + y = result[:form_size] + proof = result[form_size : 2 * form_size] + partials.append((x, y, proof)) + x = y + iters -= iters_chunk * witness + result = prove(discriminant_challenge, x, discriminant_size, iters) + y_result = result[:form_size] + y_proof = result[form_size : 2 * form_size] + assert verify_wesolowski(discriminant, x, y_result, y_proof, iters) + b_hex = get_b_from_n_wesolowski(discriminant, x, y_result + y_proof, iters, 0) + is_valid, y_from_compression = verify_n_wesolowski_with_b( + discriminant, + b_hex, + x, + y_proof, + iters, + 0, + ) + assert is_valid + assert y_from_compression == y_result + inner_proof = b"" + for x, y, proof in reversed(partials): + b_hex = get_b_from_n_wesolowski(discriminant, x, y + proof, iters_chunk, 0) + b = int(b_hex, 16) + assert verify_wesolowski(discriminant, x, y, proof, iters_chunk) + is_valid, y_from_compression = verify_n_wesolowski_with_b( + discriminant, + b_hex, + x, + proof, + iters_chunk, + 0, + ) + assert is_valid + assert y == y_from_compression + if not wrong_segm: + inner_proof += iters_chunk.to_bytes(8, byteorder='big') + else: + iters_wrong = iters_chunk + 1 + inner_proof += iters_wrong.to_bytes(8, byteorder='big') + wrong_segm = False + inner_proof += b.to_bytes(33, byteorder='big') + inner_proof += proof + return y_result, y_proof + inner_proof + + +def test_prove_n_weso_and_verify(): + discriminant_challenge = secrets.token_bytes(10) + discriminant_size = 512 + discriminant = create_discriminant(discriminant_challenge, discriminant_size) + form_size = 100 + initial_el = b"\x08" + (b"\x00" * 99) + + for iters in [1000000, 5000000, 10000000]: + y, proof = prove_n_weso(discriminant_challenge, initial_el, discriminant_size, form_size, iters, 5, False) + is_valid = verify_n_wesolowski( + str(discriminant), + initial_el, + y + proof, + iters, + discriminant_size, + 5, + ) + assert is_valid + is_valid = verify_n_wesolowski( + str(discriminant), + initial_el, + y + proof, + iters + 1, + discriminant_size, + 5, + ) + assert not is_valid + y, proof_wrong = prove_n_weso(discriminant_challenge, initial_el, discriminant_size, form_size, iters, 10, True) + is_valid = verify_n_wesolowski( + str(discriminant), + initial_el, + y + proof_wrong, + iters, + discriminant_size, + 10, + ) + assert not is_valid + b_hex = get_b_from_n_wesolowski(discriminant, initial_el, y + proof, iters, 5) + is_valid, y_from_compression = verify_n_wesolowski_with_b( + discriminant, + b_hex, + initial_el, + proof, + iters, + 5, + ) + assert is_valid + assert y_from_compression == y + B = str(int(b_hex, 16)) + is_valid, y_from_compression = verify_n_wesolowski_with_b( + discriminant, + B, + initial_el, + proof, + iters, + 5, + ) + assert is_valid + assert y_from_compression == y + B_wrong = str(int(b_hex, 16) + 1) + is_valid, y_from_compression = verify_n_wesolowski_with_b( + discriminant, + B_wrong, + initial_el, + proof, + iters, + 5, + ) + assert not is_valid + assert y_from_compression == b"" + is_valid, y_from_compression = verify_n_wesolowski_with_b( + discriminant, + B, + initial_el, + proof_wrong, + iters, + 10, + ) + assert not is_valid + assert y_from_compression == b"" + initial_el = y + + +test_prove_n_weso_and_verify()