Skip to content

Commit

Permalink
Simplify UHS ID
Browse files Browse the repository at this point in the history
Rather than try to use a pedersen commitment as a UHS ID, prefer
a nested hash (almost identical to the values-in-UHS solution).

This simplifies a lot of the prove/verify procedures, reduces the
amount of code we need overall and makes the security argument
much simpler (because the transaction format is now largely
unchanged).

Signed-off-by: Sam Stuewe <[email protected]>
  • Loading branch information
HalosGhost committed Jun 24, 2022
1 parent 7cd7036 commit 6eb1669
Show file tree
Hide file tree
Showing 15 changed files with 143 additions and 252 deletions.
7 changes: 1 addition & 6 deletions src/uhs/atomizer/shard/shard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,14 @@ namespace cbdc::shard {

static constexpr auto aux_size = sizeof(out.m_auxiliary);
static constexpr auto rng_size = sizeof(out.m_range);
static constexpr auto cst_size = sizeof(out.m_consistency);

std::array<char, aux_size + rng_size + cst_size>
proofs_arr{};
std::array<char, aux_size + rng_size> proofs_arr{};
std::memcpy(proofs_arr.data(),
out.m_auxiliary.data(),
aux_size);
std::memcpy(proofs_arr.data() + aux_size,
out.m_range.data(),
out.m_range.size());
std::memcpy(proofs_arr.data() + aux_size + rng_size,
out.m_consistency.data(),
cst_size);

leveldb::Slice ProofVal(proofs_arr.data(),
proofs_arr.size());
Expand Down
4 changes: 1 addition & 3 deletions src/uhs/client/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,7 @@ namespace cbdc {
}

void client::sign_transaction(transaction::full_tx& tx) {
auto keys = m_wallet.spending_keys(tx);
assert(keys.has_value());
m_wallet.sign(tx, keys.value());
m_wallet.sign(tx);
}

void client::register_pending_tx(const transaction::full_tx& tx) {
Expand Down
12 changes: 4 additions & 8 deletions src/uhs/transaction/messages.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,23 @@ namespace cbdc {
auto operator<<(serializer& packet, const transaction::output& out)
-> serializer& {
return packet << out.m_witness_program_commitment << out.m_id
<< out.m_nonce << out.m_auxiliary << out.m_range
<< out.m_consistency;
<< out.m_auxiliary << out.m_range;
}

auto operator>>(serializer& packet, transaction::output& out)
-> serializer& {
return packet >> out.m_witness_program_commitment >> out.m_id
>> out.m_nonce >> out.m_auxiliary >> out.m_range
>> out.m_consistency;
>> out.m_auxiliary >> out.m_range;
}

auto operator<<(serializer& packet, const transaction::compact_output& out)
-> serializer& {
return packet << out.m_id << out.m_auxiliary << out.m_range
<< out.m_consistency;
return packet << out.m_id << out.m_auxiliary << out.m_range;
}

auto operator>>(serializer& packet, transaction::compact_output& out)
-> serializer& {
return packet >> out.m_id >> out.m_auxiliary >> out.m_range
>> out.m_consistency;
return packet >> out.m_id >> out.m_auxiliary >> out.m_range;
}

auto operator<<(serializer& packet, const transaction::spend_data& spnd)
Expand Down
92 changes: 38 additions & 54 deletions src/uhs/transaction/transaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,8 @@ namespace cbdc::transaction {

auto output::operator==(const output& rhs) const -> bool {
return m_witness_program_commitment == rhs.m_witness_program_commitment
&& m_id == rhs.m_id && m_nonce == rhs.m_nonce
&& m_auxiliary == rhs.m_auxiliary && m_range == rhs.m_range
&& m_consistency == rhs.m_consistency;
&& m_id == rhs.m_id && m_auxiliary == rhs.m_auxiliary
&& m_range == rhs.m_range;
}

auto output::operator!=(const output& rhs) const -> bool {
Expand All @@ -37,21 +36,18 @@ namespace cbdc::transaction {
compact_output::compact_output(const output& put)
: m_id(put.m_id),
m_auxiliary(put.m_auxiliary),
m_range(put.m_range),
m_consistency(put.m_consistency) {}
m_range(put.m_range) {}

compact_output::compact_output(const hash_t& id,
const commitment_t& aux,
const rangeproof_t<>& range,
const signature_t& consist)
const rangeproof_t<>& range)
: m_id(id),
m_auxiliary(aux),
m_range(range),
m_consistency(consist) {}
m_range(range) {}

auto compact_output::operator==(const compact_output& rhs) const -> bool {
return m_id == rhs.m_id && m_auxiliary == rhs.m_auxiliary
&& m_range == rhs.m_range && m_consistency == rhs.m_consistency;
&& m_range == rhs.m_range;
}

auto compact_output::operator!=(const compact_output& rhs) const -> bool {
Expand Down Expand Up @@ -151,6 +147,18 @@ namespace cbdc::transaction {
return buf;
}

auto output_nested_hash(const out_point& point, const output& put)
-> hash_t {
auto buf = output_preimage(point, put);
CSHA256 sha;
sha.Write(buf.data(), buf.size());

hash_t res{};
sha.Finalize(res.data());

return res;
}

auto output_randomness(
std::array<unsigned char,
sizeof(compact_tx::m_id) + sizeof(out_point::m_index)
Expand Down Expand Up @@ -195,6 +203,21 @@ namespace cbdc::transaction {
return calculate_uhs_id(ctx, rng, buf, value);
}

auto calculate_uhs_id(const out_point& point,
const output& put,
const commitment_t& value) -> hash_t {
auto buf = output_nested_hash(point, put);

CSHA256 sha;
sha.Write(buf.data(), buf.size());
sha.Write(value.data(), value.size());

hash_t id{};
sha.Finalize(id.data());

return id;
}

auto roll_auxiliaries(secp256k1_context* ctx,
random_source& rng,
const std::vector<hash_t>& blinds,
Expand Down Expand Up @@ -261,43 +284,10 @@ namespace cbdc::transaction {
const out_point& point,
const spend_data& out_spend_data,
const secp256k1_pedersen_commitment* auxiliary) -> bool {
const auto out_preimage = output_preimage(point, put);
auto [uhs, nonce]
= calculate_uhs_id(ctx, rng, out_preimage, out_spend_data.m_value);

put.m_id = uhs;
put.m_nonce = nonce;

// manually derive the secret key
auto esk = output_randomness(out_preimage, nonce);
auto rprime = out_spend_data.m_blind;
[[maybe_unused]] auto ret
= secp256k1_ec_seckey_negate(ctx, rprime.data());
// fails when rprime == 0 (which is fine)
ret = secp256k1_ec_seckey_tweak_add(ctx, esk.data(), rprime.data());
assert(ret == 1);

secp256k1_keypair kp{};
ret = secp256k1_keypair_create(ctx, &kp, esk.data());
assert(ret == 1);

auto consistency = signature_t{};
std::array<unsigned char, hash_size> consist_contents{
"consistent proof"};
ret = secp256k1_schnorrsig_sign(ctx,
consistency.data(),
consist_contents.data(),
&kp,
nullptr,
nullptr);
assert(ret == 1);

put.m_consistency = consistency;

rangeproof_t<> range{};
size_t rangelen = range.size();
static constexpr auto upper_bound = 64; // 2^64 - 1
ret = secp256k1_bulletproofs_rangeproof_uncompressed_prove(
[[maybe_unused]] auto ret = secp256k1_bulletproofs_rangeproof_uncompressed_prove(
ctx,
gens,
secp256k1_generator_h,
Expand All @@ -318,6 +308,9 @@ namespace cbdc::transaction {
put.m_range = range;
put.m_auxiliary = serialize_commitment(ctx, *auxiliary);

auto uhs = calculate_uhs_id(point, put, put.m_auxiliary);
put.m_id = uhs;

return true;
}

Expand Down Expand Up @@ -354,8 +347,7 @@ namespace cbdc::transaction {
add_proof(secp256k1_context* ctx,
secp256k1_bulletproofs_generators* gens,
random_source& rng,
full_tx& tx,
const std::vector<std::pair<privkey_t, pubkey_t>>& spending_keys)
full_tx& tx)
-> bool {
std::vector<hash_t> blinds{};
for(const auto& inp : tx.m_inputs) {
Expand Down Expand Up @@ -405,14 +397,6 @@ namespace cbdc::transaction {
// inp.m_spend_data = std::nullopt;
//}

std::vector<unsigned char> nonces{};
for(const auto& put : tx.m_outputs) {
const auto& nonce = put.m_nonce;
std::copy(nonce.begin(), nonce.end(), std::back_inserter(nonces));
}

tx.m_tx_proofs = {sign_nonces(ctx, nonces, spending_keys)};

return true;
}

Expand Down
17 changes: 6 additions & 11 deletions src/uhs/transaction/transaction.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,10 @@ namespace cbdc::transaction {
hash_t m_witness_program_commitment{};
/// The UHS ID for the output
hash_t m_id{};
/// The nonce used to compress the Pedersen Commitment to 32 bytes
hash_t m_nonce{};
/// An auxiliary value used to prove preservation of balance
commitment_t m_auxiliary{};
/// The rangeproof guaranteeing that the output is greater than 0
rangeproof_t<> m_range{};
/// The signature proving consistency
signature_t m_consistency{};

auto operator==(const output& rhs) const -> bool;
auto operator!=(const output& rhs) const -> bool;
Expand All @@ -88,6 +84,10 @@ namespace cbdc::transaction {
const out_point& point,
const output& put) -> std::pair<hash_t, hash_t>;

auto calculate_uhs_id(const out_point& point,
const output& put,
const commitment_t& value) -> hash_t;

/// \brief Additional information a spender needs to spend an input
struct spend_data {
/// The blinding factor for the auxiliary commitment
Expand Down Expand Up @@ -172,15 +172,12 @@ namespace cbdc::transaction {
commitment_t m_auxiliary{};
/// The rangeproof guaranteeing that the output is greater than 0
rangeproof_t<> m_range{};
/// The signature proving consistency
signature_t m_consistency{};

explicit compact_output(const output& put);

compact_output(const hash_t& id,
const commitment_t& aux,
const rangeproof_t<>& range,
const signature_t& consist);
const rangeproof_t<>& range);
compact_output() = default;

auto operator==(const compact_output& rhs) const -> bool;
Expand Down Expand Up @@ -302,9 +299,7 @@ namespace cbdc::transaction {
add_proof(secp256k1_context* ctx,
secp256k1_bulletproofs_generators* gens,
random_source& rng,
full_tx& tx,
const std::vector<std::pair<privkey_t, pubkey_t>>& spending_keys)
-> bool;
full_tx& tx) -> bool;

/// \brief Calculates the unique hash of a full transaction
///
Expand Down
45 changes: 1 addition & 44 deletions src/uhs/transaction/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,57 +267,14 @@ namespace cbdc::transaction::validation {
= secp256k1_scratch_space_create(ctx, scratch_size);
std::vector<secp256k1_pedersen_commitment> auxiliaries{};
for(const auto& proof : tx.m_outputs) {
std::array<secp256k1_pubkey, 2> points{};

auto maybe_aux = deserialize_commitment(ctx, proof.m_auxiliary);
if(!maybe_aux.has_value()) {
return proof_error{proof_error_code::invalid_auxiliary};
}
auto aux = maybe_aux.value();
auxiliaries.push_back(aux);

secp256k1_pedersen_commitment_as_key(&aux, &points[0]);
auto ret = secp256k1_ec_pubkey_negate(ctx, &points[0]);
assert(ret == 1);

hash_t uhs_id = proof.m_id;
auto maybe_comm = expand_xonly_commitment(ctx, uhs_id);
if(!maybe_comm.has_value()) {
return proof_error{proof_error_code::invalid_uhs_id};
}
auto comm = maybe_comm.value();

secp256k1_pedersen_commitment_as_key(&comm, &points[1]);

std::array<secp256k1_pubkey*, 2> pks{&points[0], &points[1]};

secp256k1_pubkey fullepk{};
ret = secp256k1_ec_pubkey_combine(ctx, &fullepk, pks.data(), 2);
if(ret != 1) {
return proof_error{proof_error_code::invalid_signature_key};
}

secp256k1_xonly_pubkey epk{};
[[maybe_unused]] int parity{};
ret = secp256k1_xonly_pubkey_from_pubkey(ctx,
&epk,
&parity,
&fullepk);
if(ret != 1) {
return proof_error{proof_error_code::invalid_signature_key};
}

std::array<unsigned char, hash_size> consist_contents{
"consistent proof"};
ret = secp256k1_schnorrsig_verify(ctx,
proof.m_consistency.data(),
consist_contents.data(),
&epk);
if(ret != 1) {
return proof_error{proof_error_code::inconsistent_value};
}

ret = secp256k1_bulletproofs_rangeproof_uncompressed_verify(
[[maybe_unused]] auto ret = secp256k1_bulletproofs_rangeproof_uncompressed_verify(
ctx,
scratch,
generators.get(),
Expand Down
Loading

0 comments on commit 6eb1669

Please sign in to comment.