Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compress y. #87

Merged
merged 17 commits into from Aug 9, 2021
5 changes: 5 additions & 0 deletions .github/workflows/test.yaml
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion src/2weso_test.cpp
Expand Up @@ -15,7 +15,7 @@ void CheckProof(integer& D, Proof& proof, uint64_t iteration) {
std::vector<unsigned char> 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";
Expand Down
2 changes: 1 addition & 1 deletion src/prover_test.cpp
Expand Up @@ -14,7 +14,7 @@ Proof CreateProof(integer D, ProverManager& pm, uint64_t iteration) {
std::vector<unsigned char> 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";
Expand Down
29 changes: 28 additions & 1 deletion src/python_bindings/fastvdf.cpp
Expand Up @@ -43,7 +43,34 @@ PYBIND11_MODULE(chiavdf, m) {
uint8_t *proof_blob_ptr = reinterpret_cast<uint8_t *>(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<uint8_t *>(proof_blob_str.data());
int proof_blob_size = proof_blob.size();
std::pair<bool, std::vector<uint8_t>> 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<char*>(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<uint8_t *>(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) {
Expand Down
74 changes: 62 additions & 12 deletions src/verifier.h
Expand Up @@ -11,15 +11,18 @@

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);
integer r = FastPow(2, iters, B);
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;
}

Expand All @@ -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),
Expand All @@ -73,4 +85,42 @@ bool CheckProofOfTimeNWesolowski(integer D, const uint8_t* x_s, const uint8_t* p
return is_valid;
}

std::pair<bool, std::vector<uint8_t>> 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<uint8_t> 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
1 change: 0 additions & 1 deletion src/verifier_test.cpp
Expand Up @@ -34,7 +34,6 @@ std::copy(result.begin(), result.end(), arr);
arr,
result.size(),
33554432,
1024,
2);

auto challenge_hash1 = HexToBytes(string("a4bb1461ade74ac602e9ae511af68bb254dfe65d61b7faf9fab82d0b4364a30b").data());
Expand Down
148 changes: 148 additions & 0 deletions 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()