Skip to content

Commit 0cd6424

Browse files
authored
Compress y. (#87)
* 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++.
1 parent abae640 commit 0cd6424

File tree

7 files changed

+245
-16
lines changed

7 files changed

+245
-16
lines changed

.github/workflows/test.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ jobs:
2020
if: startsWith(matrix.os, 'ubuntu')
2121
run: |
2222
sudo apt-get install libgmp-dev libboost-python-dev libpython3.8-dev libboost-system-dev build-essential -y
23+
sudo fallocate -l 16G /swapfile
24+
sudo chmod 600 /swapfile
25+
sudo mkswap /swapfile
26+
sudo swapon /swapfile
27+
swapon -s
2328
cd src
2429
make ${{ matrix.config }} -f Makefile.vdf-client
2530

src/2weso_test.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ void CheckProof(integer& D, Proof& proof, uint64_t iteration) {
1515
std::vector<unsigned char> bytes;
1616
bytes.insert(bytes.end(), proof.y.begin(), proof.y.end());
1717
bytes.insert(bytes.end(), proof.proof.begin(), proof.proof.end());
18-
if (CheckProofOfTimeNWesolowski(D, DEFAULT_ELEMENT, bytes.data(), bytes.size(), iteration, 1024, proof.witness_type)) {
18+
if (CheckProofOfTimeNWesolowski(D, DEFAULT_ELEMENT, bytes.data(), bytes.size(), iteration, proof.witness_type)) {
1919
std::cout << "Correct proof\n";
2020
} else {
2121
std::cout << "Incorrect proof\n";

src/prover_test.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Proof CreateProof(integer D, ProverManager& pm, uint64_t iteration) {
1414
std::vector<unsigned char> bytes;
1515
bytes.insert(bytes.end(), proof.y.begin(), proof.y.end());
1616
bytes.insert(bytes.end(), proof.proof.begin(), proof.proof.end());
17-
if (CheckProofOfTimeNWesolowski(D, DEFAULT_ELEMENT, bytes.data(), bytes.size(), iteration, 1024, proof.witness_type)) {
17+
if (CheckProofOfTimeNWesolowski(D, DEFAULT_ELEMENT, bytes.data(), bytes.size(), iteration, proof.witness_type)) {
1818
std::cout << "Correct proof";
1919
} else {
2020
std::cout << "Incorrect proof\n";

src/python_bindings/fastvdf.cpp

+28-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,34 @@ PYBIND11_MODULE(chiavdf, m) {
4343
uint8_t *proof_blob_ptr = reinterpret_cast<uint8_t *>(proof_blob_str.data());
4444
int proof_blob_size = proof_blob.size();
4545

46-
return CheckProofOfTimeNWesolowski(integer(discriminant), (const uint8_t *)x_s.data(), proof_blob_ptr, proof_blob_size, num_iterations, disc_size_bits, recursion);
46+
return CheckProofOfTimeNWesolowski(integer(discriminant), (const uint8_t *)x_s.data(), proof_blob_ptr, proof_blob_size, num_iterations, recursion);
47+
});
48+
49+
// Checks an N wesolowski proof, given y is given by 'GetB()' instead of a form.
50+
m.def("verify_n_wesolowski_with_b", [] (const string& discriminant,
51+
const string& B,
52+
const string& x_s,
53+
const string& proof_blob,
54+
const uint64_t num_iterations, const uint64_t recursion) {
55+
std::string proof_blob_str(proof_blob);
56+
uint8_t *proof_blob_ptr = reinterpret_cast<uint8_t *>(proof_blob_str.data());
57+
int proof_blob_size = proof_blob.size();
58+
std::pair<bool, std::vector<uint8_t>> result;
59+
result = CheckProofOfTimeNWesolowskiWithB(integer(discriminant), integer(B), (const uint8_t *)x_s.data(), proof_blob_ptr, proof_blob_size, num_iterations, recursion);
60+
py::bytes res_bytes = py::bytes(reinterpret_cast<char*>(result.second.data()), result.second.size());
61+
py::tuple res_tuple = py::make_tuple(result.first, res_bytes);
62+
return res_tuple;
63+
});
64+
65+
m.def("get_b_from_n_wesolowski", [] (const string& discriminant,
66+
const string& x_s,
67+
const string& proof_blob,
68+
const uint64_t num_iterations, const uint64_t recursion) {
69+
std::string proof_blob_str(proof_blob);
70+
uint8_t *proof_blob_ptr = reinterpret_cast<uint8_t *>(proof_blob_str.data());
71+
int proof_blob_size = proof_blob.size();
72+
integer B = GetBFromProof(integer(discriminant), (const uint8_t *)x_s.data(), proof_blob_ptr, proof_blob_size, num_iterations, recursion);
73+
return B.to_string();
4774
});
4875

4976
m.def("prove", [] (const py::bytes& challenge_hash, const string& x_s, int discriminant_size_bits, uint64_t num_iterations) {

src/verifier.h

+62-12
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,18 @@
1111

1212
const uint8_t DEFAULT_ELEMENT[] = { 0x08 };
1313

14-
int VerifyWesoSegment(integer &D, form x, form proof, integer &B, uint64_t iters, form &out_y)
14+
int VerifyWesoSegment(integer &D, form x, form proof, integer &B, uint64_t iters, bool skip_check, form &out_y)
1515
{
1616
PulmarkReducer reducer;
1717
integer L = root(-D, 4);
1818
integer r = FastPow(2, iters, B);
1919
form f1 = FastPowFormNucomp(proof, D, B, L, reducer);
2020
form f2 = FastPowFormNucomp(x, D, r, L, reducer);
2121
out_y = f1 * f2;
22-
22+
// Optimize to only get `out_y` without verification, when not needed.
23+
if (skip_check) {
24+
return 0;
25+
}
2326
return B == GetB(D, x, out_y) ? 0 : -1;
2427
}
2528

@@ -41,30 +44,39 @@ void VerifyWesolowskiProof(integer &D, form x, form y, form proof, uint64_t iter
4144
}
4245
}
4346

44-
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)
45-
{
47+
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) {
4648
int form_size = BQFC_FORM_SIZE;
4749
int segment_len = 8 + B_bytes + form_size;
4850
int i = proof_blob_len - segment_len;
49-
form x = DeserializeForm(D, x_s, form_size);
5051

51-
if (proof_blob_len != 2 * form_size + depth * segment_len)
52-
return false;
53-
54-
// Loop depth times
55-
bool is_valid = false;
56-
for (; i >= 2 * form_size; i -= segment_len) {
52+
for (; i >= last_segment; i -= segment_len) {
5753
uint64_t segment_iters = BytesToInt64(&proof_blob[i]);
5854
form proof = DeserializeForm(D, &proof_blob[i + 8 + B_bytes], form_size);
5955
integer B(&proof_blob[i + 8], B_bytes);
6056
form xnew;
61-
if (VerifyWesoSegment(D, x, proof, B, segment_iters, xnew))
57+
if (VerifyWesoSegment(D, x, proof, B, segment_iters, skip_check, xnew))
6258
return false;
6359

6460
x = xnew;
61+
if (segment_iters > iterations) {
62+
return false;
63+
}
6564
iterations -= segment_iters;
6665
}
66+
return true;
67+
}
6768

69+
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) {
70+
int form_size = BQFC_FORM_SIZE;
71+
int segment_len = 8 + B_bytes + form_size;
72+
form x = DeserializeForm(D, x_s, form_size);
73+
if (proof_blob_len != 2 * form_size + depth * segment_len) {
74+
return false;
75+
}
76+
bool is_valid = CheckProofOfTimeNWesolowskiCommon(D, x, proof_blob, proof_blob_len, iterations, 2 * form_size);
77+
if (is_valid == false) {
78+
return false;
79+
}
6880
VerifyWesolowskiProof(D, x,
6981
DeserializeForm(D, proof_blob, form_size),
7082
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
7385
return is_valid;
7486
}
7587

88+
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) {
89+
int form_size = BQFC_FORM_SIZE;
90+
int segment_len = 8 + B_bytes + form_size;
91+
form x = DeserializeForm(D, x_s, form_size);
92+
std::vector<uint8_t> result;
93+
if (proof_blob_len != form_size + depth * segment_len) {
94+
return {false, result};
95+
}
96+
bool is_valid = CheckProofOfTimeNWesolowskiCommon(D, x, proof_blob, proof_blob_len, iterations, form_size);
97+
if (is_valid == false) {
98+
return {false, result};
99+
}
100+
form proof = DeserializeForm(D, proof_blob, form_size);
101+
form y_result;
102+
if (VerifyWesoSegment(D, x, proof, B, iterations, /*skip_check=*/false, y_result) == -1) {
103+
return {false, result};
104+
}
105+
int d_bits = D.num_bits();
106+
result = SerializeForm(y_result, d_bits);
107+
return {true, result};
108+
}
109+
110+
// TODO: Perhaps move?
111+
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) {
112+
int form_size = BQFC_FORM_SIZE;
113+
int segment_len = 8 + B_bytes + form_size;
114+
form x = DeserializeForm(D, x_s, form_size);
115+
if (proof_blob_len != 2 * form_size + depth * segment_len) {
116+
throw std::runtime_error("Invalid proof.");
117+
}
118+
bool is_valid = CheckProofOfTimeNWesolowskiCommon(D, x, proof_blob, proof_blob_len, iterations, 2 * form_size, true);
119+
if (is_valid == false) {
120+
throw std::runtime_error("Invalid proof.");
121+
}
122+
form y = DeserializeForm(D, proof_blob, form_size);
123+
return GetB(D, x, y);
124+
}
125+
76126
#endif // VERIFIER_H

src/verifier_test.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ std::copy(result.begin(), result.end(), arr);
3434
arr,
3535
result.size(),
3636
33554432,
37-
1024,
3837
2);
3938

4039
auto challenge_hash1 = HexToBytes(string("a4bb1461ade74ac602e9ae511af68bb254dfe65d61b7faf9fab82d0b4364a30b").data());

tests/test_n_weso_verifier.py

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import secrets
2+
3+
from chiavdf import (
4+
create_discriminant,
5+
prove,
6+
verify_wesolowski,
7+
verify_n_wesolowski,
8+
verify_n_wesolowski_with_b,
9+
get_b_from_n_wesolowski,
10+
)
11+
12+
13+
def prove_n_weso(discriminant_challenge, x, discriminant_size, form_size, iters, witness, wrong_segm):
14+
iters_chunk = iters // (witness + 1)
15+
partials = []
16+
discriminant = create_discriminant(discriminant_challenge, discriminant_size)
17+
for _ in range(witness):
18+
result = prove(discriminant_challenge, x, discriminant_size, iters_chunk)
19+
y = result[:form_size]
20+
proof = result[form_size : 2 * form_size]
21+
partials.append((x, y, proof))
22+
x = y
23+
iters -= iters_chunk * witness
24+
result = prove(discriminant_challenge, x, discriminant_size, iters)
25+
y_result = result[:form_size]
26+
y_proof = result[form_size : 2 * form_size]
27+
assert verify_wesolowski(discriminant, x, y_result, y_proof, iters)
28+
b_hex = get_b_from_n_wesolowski(discriminant, x, y_result + y_proof, iters, 0)
29+
is_valid, y_from_compression = verify_n_wesolowski_with_b(
30+
discriminant,
31+
b_hex,
32+
x,
33+
y_proof,
34+
iters,
35+
0,
36+
)
37+
assert is_valid
38+
assert y_from_compression == y_result
39+
inner_proof = b""
40+
for x, y, proof in reversed(partials):
41+
b_hex = get_b_from_n_wesolowski(discriminant, x, y + proof, iters_chunk, 0)
42+
b = int(b_hex, 16)
43+
assert verify_wesolowski(discriminant, x, y, proof, iters_chunk)
44+
is_valid, y_from_compression = verify_n_wesolowski_with_b(
45+
discriminant,
46+
b_hex,
47+
x,
48+
proof,
49+
iters_chunk,
50+
0,
51+
)
52+
assert is_valid
53+
assert y == y_from_compression
54+
if not wrong_segm:
55+
inner_proof += iters_chunk.to_bytes(8, byteorder='big')
56+
else:
57+
iters_wrong = iters_chunk + 1
58+
inner_proof += iters_wrong.to_bytes(8, byteorder='big')
59+
wrong_segm = False
60+
inner_proof += b.to_bytes(33, byteorder='big')
61+
inner_proof += proof
62+
return y_result, y_proof + inner_proof
63+
64+
65+
def test_prove_n_weso_and_verify():
66+
discriminant_challenge = secrets.token_bytes(10)
67+
discriminant_size = 512
68+
discriminant = create_discriminant(discriminant_challenge, discriminant_size)
69+
form_size = 100
70+
initial_el = b"\x08" + (b"\x00" * 99)
71+
72+
for iters in [1000000, 5000000, 10000000]:
73+
y, proof = prove_n_weso(discriminant_challenge, initial_el, discriminant_size, form_size, iters, 5, False)
74+
is_valid = verify_n_wesolowski(
75+
str(discriminant),
76+
initial_el,
77+
y + proof,
78+
iters,
79+
discriminant_size,
80+
5,
81+
)
82+
assert is_valid
83+
is_valid = verify_n_wesolowski(
84+
str(discriminant),
85+
initial_el,
86+
y + proof,
87+
iters + 1,
88+
discriminant_size,
89+
5,
90+
)
91+
assert not is_valid
92+
y, proof_wrong = prove_n_weso(discriminant_challenge, initial_el, discriminant_size, form_size, iters, 10, True)
93+
is_valid = verify_n_wesolowski(
94+
str(discriminant),
95+
initial_el,
96+
y + proof_wrong,
97+
iters,
98+
discriminant_size,
99+
10,
100+
)
101+
assert not is_valid
102+
b_hex = get_b_from_n_wesolowski(discriminant, initial_el, y + proof, iters, 5)
103+
is_valid, y_from_compression = verify_n_wesolowski_with_b(
104+
discriminant,
105+
b_hex,
106+
initial_el,
107+
proof,
108+
iters,
109+
5,
110+
)
111+
assert is_valid
112+
assert y_from_compression == y
113+
B = str(int(b_hex, 16))
114+
is_valid, y_from_compression = verify_n_wesolowski_with_b(
115+
discriminant,
116+
B,
117+
initial_el,
118+
proof,
119+
iters,
120+
5,
121+
)
122+
assert is_valid
123+
assert y_from_compression == y
124+
B_wrong = str(int(b_hex, 16) + 1)
125+
is_valid, y_from_compression = verify_n_wesolowski_with_b(
126+
discriminant,
127+
B_wrong,
128+
initial_el,
129+
proof,
130+
iters,
131+
5,
132+
)
133+
assert not is_valid
134+
assert y_from_compression == b""
135+
is_valid, y_from_compression = verify_n_wesolowski_with_b(
136+
discriminant,
137+
B,
138+
initial_el,
139+
proof_wrong,
140+
iters,
141+
10,
142+
)
143+
assert not is_valid
144+
assert y_from_compression == b""
145+
initial_el = y
146+
147+
148+
test_prove_n_weso_and_verify()

0 commit comments

Comments
 (0)