Created
June 10, 2018 13:22
-
-
Save narodnik/cbf89daab440e5d09e3e4441cd8f3635 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
g++ -o rp rp.cpp $(pkg-config --cflags --libs libbitcoin) | |
./rp | |
*/ | |
#include <bitcoin/bitcoin.hpp> | |
typedef uint64_t rangevalue_type; | |
constexpr size_t commitment_bitsize = sizeof(rangevalue_type) * 8; | |
typedef std::array<bc::ec_point, commitment_bitsize> commitment_list; | |
const bc::ec_point generator_H = bc::ec_compressed({ | |
0x03, 0x52, 0x30, 0xc0, 0x27, 0xdf, 0x76, 0x0d, | |
0x61, 0x07, 0x47, 0xc4, 0x7a, 0x31, 0xa0, 0xc6, | |
0xf0, 0x10, 0xe9, 0x6d, 0xb1, 0x25, 0xc9, 0xe9, | |
0xee, 0x2b, 0xd7, 0xdf, 0xd4, 0x88, 0xa1, 0x89, 0x70 | |
}); | |
struct rangeproof_type | |
{ | |
commitment_list commitments; | |
bc::ring_signature signature; | |
}; | |
// Gives the binary value at an index i (or 2^i) | |
rangevalue_type binary_value(size_t i) | |
{ | |
return static_cast<rangevalue_type>(std::pow(2, i)); | |
} | |
// Returns either 0 or the binary value at i (2^i) | |
rangevalue_type binary_digit(rangevalue_type value, size_t i) | |
{ | |
BITCOIN_ASSERT(i <= commitment_bitsize); | |
return value & binary_value(i); | |
} | |
bc::ec_secret random_secret() | |
{ | |
bc::data_chunk data(bc::ec_secret_size); | |
bc::pseudo_random_fill(data); | |
bc::ec_secret result; | |
std::copy(data.begin(), data.end(), result.begin()); | |
return result; | |
} | |
// Calculates bG + vH | |
bc::ec_point compute_commitment(const bc::ec_scalar& blind, | |
const rangevalue_type value) | |
{ | |
BITCOIN_ASSERT(blind); | |
const auto bG = blind * bc::ec_point::G; | |
if (!value) | |
return bG; | |
const bc::ec_scalar v = value; | |
const auto result = bG + v * generator_H; | |
BITCOIN_ASSERT(result); | |
return result; | |
} | |
// Calculates commitment - vH | |
bc::ec_point compute_key(const bc::ec_compressed& commitment, size_t i) | |
{ | |
const auto value = binary_value(i); | |
BITCOIN_ASSERT(value > 0); | |
const bc::ec_scalar v = value; | |
const auto result = commitment - v * generator_H; | |
BITCOIN_ASSERT(result); | |
return result; | |
} | |
// Generates rings for (0, 2^i) | |
bc::key_rings rings_from_commitments(const commitment_list& commitments) | |
{ | |
bc::key_rings rings; | |
rings.reserve(commitments.size()); | |
for (size_t i = 0; i < commitments.size(); ++i) | |
{ | |
const auto& commitment = commitments[i]; | |
const auto zero = commitment; | |
const auto nonzero = compute_key(commitment, i); | |
rings.push_back({ zero, nonzero }); | |
} | |
return rings; | |
} | |
const bc::hash_digest prepare_digest(const bc::key_rings& rings) | |
{ | |
const bc::data_chunk message = { 0xde, 0xad, 0xbe, 0xef }; | |
return bc::digest(message, rings); | |
} | |
rangeproof_type construct_rangeproof(rangevalue_type amount) | |
{ | |
std::cout << "Using generator H: " | |
<< bc::encode_base16(generator_H.point()) << std::endl; | |
commitment_list commitments; | |
bc::secret_list secrets; | |
secrets.reserve(commitments.size()); | |
// Compute commitments (used to create pubkey rings) and secrets | |
for (size_t i = 0; i < commitments.size(); ++i) | |
{ | |
auto blind = random_secret(); | |
auto value = binary_digit(amount, i); | |
std::cout << i << ": (" << bc::encode_base16(blind) | |
<< ", " << value << ")" << std::endl; | |
commitments[i] = compute_commitment(blind, value); | |
secrets.push_back(blind); | |
} | |
std::cout << std::endl; | |
const auto rings = rings_from_commitments(commitments); | |
bc::ring_signature signature; | |
signature.proofs.reserve(commitments.size()); | |
bc::secret_list salts; | |
salts.reserve(commitments.size()); | |
// Random values used in signing | |
for (const auto& commitment: commitments) | |
{ | |
signature.proofs.push_back({ random_secret(), random_secret() }); | |
salts.push_back(random_secret()); | |
} | |
const auto digest = prepare_digest(rings); | |
// Create signature | |
bool rc = bc::sign(signature, secrets, rings, digest, salts); | |
BITCOIN_ASSERT(rc); | |
// Rangeproof result | |
return { commitments, signature }; | |
} | |
bool verify_rangeproof(const rangeproof_type& rangeproof) | |
{ | |
const auto rings = rings_from_commitments(rangeproof.commitments); | |
const auto digest = prepare_digest(rings); | |
return bc::verify(rings, digest, rangeproof.signature); | |
} | |
int main() | |
{ | |
auto rangeproof = construct_rangeproof(110); | |
const auto verify_success = verify_rangeproof(rangeproof); | |
BITCOIN_ASSERT(verify_success); | |
std::cout << std::endl; | |
std::cout << "OK" << std::endl; | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment