Skip to content

Commit 5478032

Browse files
authored
Compress y. (#111)
* Initial commit compress y. * Fix VerifyWesoSegment arguments. * One more fix. * Move result outside GIL scope.
1 parent 54ed20f commit 5478032

File tree

3 files changed

+248
-0
lines changed

3 files changed

+248
-0
lines changed

src/python_bindings/fastvdf.cpp

+31
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,35 @@ PYBIND11_MODULE(chiavdf, m) {
6565
py::bytes ret = py::bytes(reinterpret_cast<char*>(result.data()), result.size());
6666
return ret;
6767
});
68+
69+
// Checks an N wesolowski proof, given y is given by 'GetB()' instead of a form.
70+
m.def("verify_n_wesolowski_with_b", [] (const string& discriminant,
71+
const string& B,
72+
const string& x_s,
73+
const string& proof_blob,
74+
const uint64_t num_iterations, const uint64_t recursion) {
75+
std::pair<bool, std::vector<uint8_t>> result;
76+
{
77+
py::gil_scoped_release release;
78+
std::string proof_blob_str(proof_blob);
79+
uint8_t *proof_blob_ptr = reinterpret_cast<uint8_t *>(proof_blob_str.data());
80+
int proof_blob_size = proof_blob.size();
81+
result = CheckProofOfTimeNWesolowskiWithB(integer(discriminant), integer(B), (const uint8_t *)x_s.data(), proof_blob_ptr, proof_blob_size, num_iterations, recursion);
82+
}
83+
py::bytes res_bytes = py::bytes(reinterpret_cast<char*>(result.second.data()), result.second.size());
84+
py::tuple res_tuple = py::make_tuple(result.first, res_bytes);
85+
return res_tuple;
86+
});
87+
88+
m.def("get_b_from_n_wesolowski", [] (const string& discriminant,
89+
const string& x_s,
90+
const string& proof_blob,
91+
const uint64_t num_iterations, const uint64_t recursion) {
92+
py::gil_scoped_release release;
93+
std::string proof_blob_str(proof_blob);
94+
uint8_t *proof_blob_ptr = reinterpret_cast<uint8_t *>(proof_blob_str.data());
95+
int proof_blob_size = proof_blob.size();
96+
integer B = GetBFromProof(integer(discriminant), (const uint8_t *)x_s.data(), proof_blob_ptr, proof_blob_size, num_iterations, recursion);
97+
return B.to_string();
98+
});
6899
}

src/verifier.h

+69
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,73 @@ bool CheckProofOfTimeNWesolowski(integer D, const uint8_t* x_s, const uint8_t* p
7676
return is_valid;
7777
}
7878

79+
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) {
80+
int form_size = BQFC_FORM_SIZE;
81+
int segment_len = 8 + B_bytes + form_size;
82+
int i = proof_blob_len - segment_len;
83+
PulmarkReducer reducer;
84+
85+
for (; i >= last_segment; i -= segment_len) {
86+
uint64_t segment_iters = BytesToInt64(&proof_blob[i]);
87+
form proof = DeserializeForm(D, &proof_blob[i + 8 + B_bytes], form_size);
88+
integer B(&proof_blob[i + 8], B_bytes);
89+
form xnew;
90+
if (!skip_check) {
91+
if (VerifyWesoSegment(D, x, proof, B, segment_iters, xnew))
92+
return false;
93+
} else {
94+
integer L = root(-D, 4);
95+
integer r = FastPow(2, segment_iters, B);
96+
form f1 = FastPowFormNucomp(proof, D, B, L, reducer);
97+
form f2 = FastPowFormNucomp(x, D, r, L, reducer);
98+
xnew = f1 * f2;
99+
}
100+
101+
x = xnew;
102+
if (segment_iters > iterations) {
103+
return false;
104+
}
105+
iterations -= segment_iters;
106+
}
107+
return true;
108+
}
109+
110+
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) {
111+
int form_size = BQFC_FORM_SIZE;
112+
int segment_len = 8 + B_bytes + form_size;
113+
form x = DeserializeForm(D, x_s, form_size);
114+
std::vector<uint8_t> result;
115+
if (proof_blob_len != form_size + depth * segment_len) {
116+
return {false, result};
117+
}
118+
bool is_valid = CheckProofOfTimeNWesolowskiCommon(D, x, proof_blob, proof_blob_len, iterations, form_size);
119+
if (is_valid == false) {
120+
return {false, result};
121+
}
122+
form proof = DeserializeForm(D, proof_blob, form_size);
123+
form y_result;
124+
if (VerifyWesoSegment(D, x, proof, B, iterations, y_result) == -1) {
125+
return {false, result};
126+
}
127+
int d_bits = D.num_bits();
128+
result = SerializeForm(y_result, d_bits);
129+
return {true, result};
130+
}
131+
132+
// TODO: Perhaps move?
133+
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) {
134+
int form_size = BQFC_FORM_SIZE;
135+
int segment_len = 8 + B_bytes + form_size;
136+
form x = DeserializeForm(D, x_s, form_size);
137+
if (proof_blob_len != 2 * form_size + depth * segment_len) {
138+
throw std::runtime_error("Invalid proof.");
139+
}
140+
bool is_valid = CheckProofOfTimeNWesolowskiCommon(D, x, proof_blob, proof_blob_len, iterations, 2 * form_size, true);
141+
if (is_valid == false) {
142+
throw std::runtime_error("Invalid proof.");
143+
}
144+
form y = DeserializeForm(D, proof_blob, form_size);
145+
return GetB(D, x, y);
146+
}
147+
79148
#endif // VERIFIER_H

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)