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. #111

Merged
merged 4 commits into from May 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 31 additions & 0 deletions src/python_bindings/fastvdf.cpp
Expand Up @@ -65,4 +65,35 @@ PYBIND11_MODULE(chiavdf, m) {
py::bytes ret = py::bytes(reinterpret_cast<char*>(result.data()), result.size());
return ret;
});

// 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::pair<bool, std::vector<uint8_t>> result;
{
py::gil_scoped_release release;
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();
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) {
py::gil_scoped_release release;
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();
});
}
69 changes: 69 additions & 0 deletions src/verifier.h
Expand Up @@ -76,4 +76,73 @@ bool CheckProofOfTimeNWesolowski(integer D, const uint8_t* x_s, const uint8_t* p
return is_valid;
}

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;
PulmarkReducer reducer;

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 (!skip_check) {
if (VerifyWesoSegment(D, x, proof, B, segment_iters, xnew))
return false;
} else {
integer L = root(-D, 4);
integer r = FastPow(2, segment_iters, B);
form f1 = FastPowFormNucomp(proof, D, B, L, reducer);
form f2 = FastPowFormNucomp(x, D, r, L, reducer);
xnew = f1 * f2;
}

x = xnew;
if (segment_iters > iterations) {
return false;
}
iterations -= segment_iters;
}
return true;
}

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, 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
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()