Skip to content

Commit

Permalink
Reworked proof of work #288
Browse files Browse the repository at this point in the history
  • Loading branch information
vo-nil committed Jul 16, 2024
1 parent 93edae7 commit 5533e75
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 31 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//---------------------------------------------------------------------------//
// Copyright (c) 2023 Elena Tatuzova <e.tatuzova@nil.foundation>
// Copyright (c) 2024 Vasiliy Olekhov <vasiliy.olekhov@nil.foundation>
//
// MIT License
//
Expand Down Expand Up @@ -44,37 +45,38 @@ namespace nil {
using transcript_type = transcript::fiat_shamir_heuristic_sequential<transcript_hash_type>;
using output_type = OutType;

static inline OutType generate(transcript_type &transcript, OutType mask=0xFFFF) {
static inline std::array<std::uint8_t, sizeof(OutType)>
int_be(OutType v) {
std::array<std::uint8_t, sizeof(OutType)> bytes;
for(int i = sizeof(v)-1; i>=0; --i) {
bytes[i] = v & 0xFF;
v >>= 8;
}
return bytes;
}

static inline OutType generate(transcript_type &transcript, std::size_t GrindingBits = 16) {
output_type mask = GrindingBits > 0 ? ( 1ULL << GrindingBits ) - 1 : 0;
output_type proof_of_work = std::rand();
output_type result;
std::vector<std::uint8_t> bytes(4);

while( true ) {
transcript_type tmp_transcript = transcript;
bytes[0] = std::uint8_t((proof_of_work&0xFF000000)>>24);
bytes[1] = std::uint8_t((proof_of_work&0x00FF0000)>>16);
bytes[2] = std::uint8_t((proof_of_work&0x0000FF00)>>8);
bytes[3] = std::uint8_t(proof_of_work&0x000000FF);

tmp_transcript(bytes);
tmp_transcript(int_be(proof_of_work));
result = tmp_transcript.template int_challenge<output_type>();
if ((result & mask) == 0)
break;
proof_of_work++;
}
transcript(bytes);
transcript(int_be(proof_of_work));
result = transcript.template int_challenge<output_type>();
return proof_of_work;
}

static inline bool verify(transcript_type &transcript, output_type proof_of_work, OutType mask=0xFFFF) {
std::vector<std::uint8_t> bytes(4);
bytes[0] = std::uint8_t((proof_of_work&0xFF000000)>>24);
bytes[1] = std::uint8_t((proof_of_work&0x00FF0000)>>16);
bytes[2] = std::uint8_t((proof_of_work&0x0000FF00)>>8);
bytes[3] = std::uint8_t(proof_of_work&0x000000FF);
transcript(bytes);
static inline bool verify(transcript_type &transcript, output_type proof_of_work, std::size_t GrindingBits = 16) {
transcript(int_be(proof_of_work));
output_type result = transcript.template int_challenge<output_type>();
output_type mask = GrindingBits > 0 ? ( 1ULL << GrindingBits ) - 1 : 0;
return ((result & mask) == 0);
}
};
Expand Down
2 changes: 1 addition & 1 deletion libs/zk/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ set(TESTS_NAMES
# "commitment/r1cs_gg_ppzksnark_mpc"
# "commitment/type_traits"
# "commitment/kimchi_pedersen"
# "commitment/proof_of_work"
"commitment/proof_of_work"

"math/expression"

Expand Down
28 changes: 14 additions & 14 deletions libs/zk/test/commitment/proof_of_work.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,18 @@ BOOST_AUTO_TEST_SUITE(proof_of_knowledge_test_suite)
using poseidon = nil::crypto3::hashes::poseidon<policy>;
using pow_type = nil::crypto3::zk::commitments::field_proof_of_work<poseidon, field_type>;

const integral_type expected_mask = integral_type(0xFF80000000000000) << (field_type::modulus_bits - 64);
std::size_t grinding_bits = 9;
nil::crypto3::zk::transcript::fiat_shamir_heuristic_sequential<poseidon> transcript;
auto old_transcript_1 = transcript, old_transcript_2 = transcript;

auto result = pow_type::generate(transcript, 9);
BOOST_ASSERT(pow_type::verify(old_transcript_1, result, 9));
auto result = pow_type::generate(transcript, grinding_bits);
BOOST_ASSERT(pow_type::verify(old_transcript_1, result, grinding_bits));

// manually reimplement verify to ensure that changes in implementation didn't break it
old_transcript_2(result);
auto chal = old_transcript_2.template challenge<field_type>();
const integral_type expected_mask = integral_type( (1 << grinding_bits) - 1 ) << (field_type::modulus_bits - grinding_bits);

BOOST_ASSERT((integral_type(chal.data) & expected_mask) == 0);

using hard_pow_type = nil::crypto3::zk::commitments::field_proof_of_work<poseidon, field_type>;
Expand All @@ -70,28 +72,26 @@ BOOST_AUTO_TEST_SUITE(proof_of_knowledge_test_suite)

BOOST_AUTO_TEST_CASE(pow_basic_test) {
using keccak = nil::crypto3::hashes::keccak_1600<512>;
const std::uint32_t mask = 0xFFFFF000;

const std::uint32_t grinding_bits = 20;
const uint64_t expected_mask = (1ULL << grinding_bits) - 1;

using pow_type = nil::crypto3::zk::commitments::proof_of_work<keccak, std::uint32_t>;

nil::crypto3::zk::transcript::fiat_shamir_heuristic_sequential<keccak> transcript;
auto old_transcript_1 = transcript, old_transcript_2 = transcript;

auto result = pow_type::generate(transcript, mask);
BOOST_ASSERT(pow_type::verify(old_transcript_1, result, mask));
auto result = pow_type::generate(transcript, grinding_bits);
BOOST_ASSERT(pow_type::verify(old_transcript_1, result, grinding_bits));

// manually reimplement verify to ensure that changes in implementation didn't break it
std::array<std::uint8_t, 4> bytes;
bytes[0] = std::uint8_t((result & 0xFF000000) >> 24);
bytes[1] = std::uint8_t((result & 0x00FF0000) >> 16);
bytes[2] = std::uint8_t((result & 0x0000FF00) >> 8);
bytes[3] = std::uint8_t(result & 0x000000FF);
old_transcript_2(bytes);
old_transcript_2(pow_type::int_be(result));
auto chal = old_transcript_2.template int_challenge<std::uint32_t>();
BOOST_ASSERT((chal & mask) == 0);
BOOST_ASSERT( (chal & expected_mask) == 0);

// check that random stuff doesn't pass verify
using hard_pow_type = nil::crypto3::zk::commitments::proof_of_work<keccak, std::uint32_t>;
BOOST_ASSERT(!hard_pow_type::verify(old_transcript_1, result, mask));
BOOST_ASSERT(!hard_pow_type::verify(old_transcript_1, result, grinding_bits));
}

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit 5533e75

Please sign in to comment.