Skip to content

Instantly share code, notes, and snippets.

@narodnik
Created June 10, 2018 13:22
Show Gist options
  • Save narodnik/cbf89daab440e5d09e3e4441cd8f3635 to your computer and use it in GitHub Desktop.
Save narodnik/cbf89daab440e5d09e3e4441cd8f3635 to your computer and use it in GitHub Desktop.
/*
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