Skip to content

Commit

Permalink
Compress y. (#87)
Browse files Browse the repository at this point in the history
* Checkpoint compress y and pybinds.

* Compile errors.

* Also fix error in pybind.

* Add test, fix bug.

* Flake8 test.

* Checkpoint return intermediates.

* Add sanity check.

* First attempt code review.

* Typo.

* return 0 not true.

* Add more tests for false proofs.

* Typo...

* Try passing test similar to chiapos.

* Remove useless variables.

* Put back disc_size_bits in n-weso verify.

* Try to fix test.

* Try to remove size_bits from C++.
  • Loading branch information
fchirica committed Aug 9, 2021
1 parent abae640 commit 0cd6424
Show file tree
Hide file tree
Showing 7 changed files with 245 additions and 16 deletions.
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()

0 comments on commit 0cd6424

Please sign in to comment.