Skip to content

Commit 6eb1669

Browse files
committed
Simplify UHS ID
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]>
1 parent 7cd7036 commit 6eb1669

15 files changed

+143
-252
lines changed

src/uhs/atomizer/shard/shard.cpp

+1-6
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,14 @@ namespace cbdc::shard {
7575

7676
static constexpr auto aux_size = sizeof(out.m_auxiliary);
7777
static constexpr auto rng_size = sizeof(out.m_range);
78-
static constexpr auto cst_size = sizeof(out.m_consistency);
7978

80-
std::array<char, aux_size + rng_size + cst_size>
81-
proofs_arr{};
79+
std::array<char, aux_size + rng_size> proofs_arr{};
8280
std::memcpy(proofs_arr.data(),
8381
out.m_auxiliary.data(),
8482
aux_size);
8583
std::memcpy(proofs_arr.data() + aux_size,
8684
out.m_range.data(),
8785
out.m_range.size());
88-
std::memcpy(proofs_arr.data() + aux_size + rng_size,
89-
out.m_consistency.data(),
90-
cst_size);
9186

9287
leveldb::Slice ProofVal(proofs_arr.data(),
9388
proofs_arr.size());

src/uhs/client/client.cpp

+1-3
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,7 @@ namespace cbdc {
7070
}
7171

7272
void client::sign_transaction(transaction::full_tx& tx) {
73-
auto keys = m_wallet.spending_keys(tx);
74-
assert(keys.has_value());
75-
m_wallet.sign(tx, keys.value());
73+
m_wallet.sign(tx);
7674
}
7775

7876
void client::register_pending_tx(const transaction::full_tx& tx) {

src/uhs/transaction/messages.cpp

+4-8
Original file line numberDiff line numberDiff line change
@@ -23,27 +23,23 @@ namespace cbdc {
2323
auto operator<<(serializer& packet, const transaction::output& out)
2424
-> serializer& {
2525
return packet << out.m_witness_program_commitment << out.m_id
26-
<< out.m_nonce << out.m_auxiliary << out.m_range
27-
<< out.m_consistency;
26+
<< out.m_auxiliary << out.m_range;
2827
}
2928

3029
auto operator>>(serializer& packet, transaction::output& out)
3130
-> serializer& {
3231
return packet >> out.m_witness_program_commitment >> out.m_id
33-
>> out.m_nonce >> out.m_auxiliary >> out.m_range
34-
>> out.m_consistency;
32+
>> out.m_auxiliary >> out.m_range;
3533
}
3634

3735
auto operator<<(serializer& packet, const transaction::compact_output& out)
3836
-> serializer& {
39-
return packet << out.m_id << out.m_auxiliary << out.m_range
40-
<< out.m_consistency;
37+
return packet << out.m_id << out.m_auxiliary << out.m_range;
4138
}
4239

4340
auto operator>>(serializer& packet, transaction::compact_output& out)
4441
-> serializer& {
45-
return packet >> out.m_id >> out.m_auxiliary >> out.m_range
46-
>> out.m_consistency;
42+
return packet >> out.m_id >> out.m_auxiliary >> out.m_range;
4743
}
4844

4945
auto operator<<(serializer& packet, const transaction::spend_data& spnd)

src/uhs/transaction/transaction.cpp

+38-54
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,8 @@ namespace cbdc::transaction {
2525

2626
auto output::operator==(const output& rhs) const -> bool {
2727
return m_witness_program_commitment == rhs.m_witness_program_commitment
28-
&& m_id == rhs.m_id && m_nonce == rhs.m_nonce
29-
&& m_auxiliary == rhs.m_auxiliary && m_range == rhs.m_range
30-
&& m_consistency == rhs.m_consistency;
28+
&& m_id == rhs.m_id && m_auxiliary == rhs.m_auxiliary
29+
&& m_range == rhs.m_range;
3130
}
3231

3332
auto output::operator!=(const output& rhs) const -> bool {
@@ -37,21 +36,18 @@ namespace cbdc::transaction {
3736
compact_output::compact_output(const output& put)
3837
: m_id(put.m_id),
3938
m_auxiliary(put.m_auxiliary),
40-
m_range(put.m_range),
41-
m_consistency(put.m_consistency) {}
39+
m_range(put.m_range) {}
4240

4341
compact_output::compact_output(const hash_t& id,
4442
const commitment_t& aux,
45-
const rangeproof_t<>& range,
46-
const signature_t& consist)
43+
const rangeproof_t<>& range)
4744
: m_id(id),
4845
m_auxiliary(aux),
49-
m_range(range),
50-
m_consistency(consist) {}
46+
m_range(range) {}
5147

5248
auto compact_output::operator==(const compact_output& rhs) const -> bool {
5349
return m_id == rhs.m_id && m_auxiliary == rhs.m_auxiliary
54-
&& m_range == rhs.m_range && m_consistency == rhs.m_consistency;
50+
&& m_range == rhs.m_range;
5551
}
5652

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

150+
auto output_nested_hash(const out_point& point, const output& put)
151+
-> hash_t {
152+
auto buf = output_preimage(point, put);
153+
CSHA256 sha;
154+
sha.Write(buf.data(), buf.size());
155+
156+
hash_t res{};
157+
sha.Finalize(res.data());
158+
159+
return res;
160+
}
161+
154162
auto output_randomness(
155163
std::array<unsigned char,
156164
sizeof(compact_tx::m_id) + sizeof(out_point::m_index)
@@ -195,6 +203,21 @@ namespace cbdc::transaction {
195203
return calculate_uhs_id(ctx, rng, buf, value);
196204
}
197205

206+
auto calculate_uhs_id(const out_point& point,
207+
const output& put,
208+
const commitment_t& value) -> hash_t {
209+
auto buf = output_nested_hash(point, put);
210+
211+
CSHA256 sha;
212+
sha.Write(buf.data(), buf.size());
213+
sha.Write(value.data(), value.size());
214+
215+
hash_t id{};
216+
sha.Finalize(id.data());
217+
218+
return id;
219+
}
220+
198221
auto roll_auxiliaries(secp256k1_context* ctx,
199222
random_source& rng,
200223
const std::vector<hash_t>& blinds,
@@ -261,43 +284,10 @@ namespace cbdc::transaction {
261284
const out_point& point,
262285
const spend_data& out_spend_data,
263286
const secp256k1_pedersen_commitment* auxiliary) -> bool {
264-
const auto out_preimage = output_preimage(point, put);
265-
auto [uhs, nonce]
266-
= calculate_uhs_id(ctx, rng, out_preimage, out_spend_data.m_value);
267-
268-
put.m_id = uhs;
269-
put.m_nonce = nonce;
270-
271-
// manually derive the secret key
272-
auto esk = output_randomness(out_preimage, nonce);
273-
auto rprime = out_spend_data.m_blind;
274-
[[maybe_unused]] auto ret
275-
= secp256k1_ec_seckey_negate(ctx, rprime.data());
276-
// fails when rprime == 0 (which is fine)
277-
ret = secp256k1_ec_seckey_tweak_add(ctx, esk.data(), rprime.data());
278-
assert(ret == 1);
279-
280-
secp256k1_keypair kp{};
281-
ret = secp256k1_keypair_create(ctx, &kp, esk.data());
282-
assert(ret == 1);
283-
284-
auto consistency = signature_t{};
285-
std::array<unsigned char, hash_size> consist_contents{
286-
"consistent proof"};
287-
ret = secp256k1_schnorrsig_sign(ctx,
288-
consistency.data(),
289-
consist_contents.data(),
290-
&kp,
291-
nullptr,
292-
nullptr);
293-
assert(ret == 1);
294-
295-
put.m_consistency = consistency;
296-
297287
rangeproof_t<> range{};
298288
size_t rangelen = range.size();
299289
static constexpr auto upper_bound = 64; // 2^64 - 1
300-
ret = secp256k1_bulletproofs_rangeproof_uncompressed_prove(
290+
[[maybe_unused]] auto ret = secp256k1_bulletproofs_rangeproof_uncompressed_prove(
301291
ctx,
302292
gens,
303293
secp256k1_generator_h,
@@ -318,6 +308,9 @@ namespace cbdc::transaction {
318308
put.m_range = range;
319309
put.m_auxiliary = serialize_commitment(ctx, *auxiliary);
320310

311+
auto uhs = calculate_uhs_id(point, put, put.m_auxiliary);
312+
put.m_id = uhs;
313+
321314
return true;
322315
}
323316

@@ -354,8 +347,7 @@ namespace cbdc::transaction {
354347
add_proof(secp256k1_context* ctx,
355348
secp256k1_bulletproofs_generators* gens,
356349
random_source& rng,
357-
full_tx& tx,
358-
const std::vector<std::pair<privkey_t, pubkey_t>>& spending_keys)
350+
full_tx& tx)
359351
-> bool {
360352
std::vector<hash_t> blinds{};
361353
for(const auto& inp : tx.m_inputs) {
@@ -405,14 +397,6 @@ namespace cbdc::transaction {
405397
// inp.m_spend_data = std::nullopt;
406398
//}
407399

408-
std::vector<unsigned char> nonces{};
409-
for(const auto& put : tx.m_outputs) {
410-
const auto& nonce = put.m_nonce;
411-
std::copy(nonce.begin(), nonce.end(), std::back_inserter(nonces));
412-
}
413-
414-
tx.m_tx_proofs = {sign_nonces(ctx, nonces, spending_keys)};
415-
416400
return true;
417401
}
418402

src/uhs/transaction/transaction.hpp

+6-11
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,10 @@ namespace cbdc::transaction {
5555
hash_t m_witness_program_commitment{};
5656
/// The UHS ID for the output
5757
hash_t m_id{};
58-
/// The nonce used to compress the Pedersen Commitment to 32 bytes
59-
hash_t m_nonce{};
6058
/// An auxiliary value used to prove preservation of balance
6159
commitment_t m_auxiliary{};
6260
/// The rangeproof guaranteeing that the output is greater than 0
6361
rangeproof_t<> m_range{};
64-
/// The signature proving consistency
65-
signature_t m_consistency{};
6662

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

87+
auto calculate_uhs_id(const out_point& point,
88+
const output& put,
89+
const commitment_t& value) -> hash_t;
90+
9191
/// \brief Additional information a spender needs to spend an input
9292
struct spend_data {
9393
/// The blinding factor for the auxiliary commitment
@@ -172,15 +172,12 @@ namespace cbdc::transaction {
172172
commitment_t m_auxiliary{};
173173
/// The rangeproof guaranteeing that the output is greater than 0
174174
rangeproof_t<> m_range{};
175-
/// The signature proving consistency
176-
signature_t m_consistency{};
177175

178176
explicit compact_output(const output& put);
179177

180178
compact_output(const hash_t& id,
181179
const commitment_t& aux,
182-
const rangeproof_t<>& range,
183-
const signature_t& consist);
180+
const rangeproof_t<>& range);
184181
compact_output() = default;
185182

186183
auto operator==(const compact_output& rhs) const -> bool;
@@ -302,9 +299,7 @@ namespace cbdc::transaction {
302299
add_proof(secp256k1_context* ctx,
303300
secp256k1_bulletproofs_generators* gens,
304301
random_source& rng,
305-
full_tx& tx,
306-
const std::vector<std::pair<privkey_t, pubkey_t>>& spending_keys)
307-
-> bool;
302+
full_tx& tx) -> bool;
308303

309304
/// \brief Calculates the unique hash of a full transaction
310305
///

src/uhs/transaction/validation.cpp

+1-44
Original file line numberDiff line numberDiff line change
@@ -267,57 +267,14 @@ namespace cbdc::transaction::validation {
267267
= secp256k1_scratch_space_create(ctx, scratch_size);
268268
std::vector<secp256k1_pedersen_commitment> auxiliaries{};
269269
for(const auto& proof : tx.m_outputs) {
270-
std::array<secp256k1_pubkey, 2> points{};
271-
272270
auto maybe_aux = deserialize_commitment(ctx, proof.m_auxiliary);
273271
if(!maybe_aux.has_value()) {
274272
return proof_error{proof_error_code::invalid_auxiliary};
275273
}
276274
auto aux = maybe_aux.value();
277275
auxiliaries.push_back(aux);
278276

279-
secp256k1_pedersen_commitment_as_key(&aux, &points[0]);
280-
auto ret = secp256k1_ec_pubkey_negate(ctx, &points[0]);
281-
assert(ret == 1);
282-
283-
hash_t uhs_id = proof.m_id;
284-
auto maybe_comm = expand_xonly_commitment(ctx, uhs_id);
285-
if(!maybe_comm.has_value()) {
286-
return proof_error{proof_error_code::invalid_uhs_id};
287-
}
288-
auto comm = maybe_comm.value();
289-
290-
secp256k1_pedersen_commitment_as_key(&comm, &points[1]);
291-
292-
std::array<secp256k1_pubkey*, 2> pks{&points[0], &points[1]};
293-
294-
secp256k1_pubkey fullepk{};
295-
ret = secp256k1_ec_pubkey_combine(ctx, &fullepk, pks.data(), 2);
296-
if(ret != 1) {
297-
return proof_error{proof_error_code::invalid_signature_key};
298-
}
299-
300-
secp256k1_xonly_pubkey epk{};
301-
[[maybe_unused]] int parity{};
302-
ret = secp256k1_xonly_pubkey_from_pubkey(ctx,
303-
&epk,
304-
&parity,
305-
&fullepk);
306-
if(ret != 1) {
307-
return proof_error{proof_error_code::invalid_signature_key};
308-
}
309-
310-
std::array<unsigned char, hash_size> consist_contents{
311-
"consistent proof"};
312-
ret = secp256k1_schnorrsig_verify(ctx,
313-
proof.m_consistency.data(),
314-
consist_contents.data(),
315-
&epk);
316-
if(ret != 1) {
317-
return proof_error{proof_error_code::inconsistent_value};
318-
}
319-
320-
ret = secp256k1_bulletproofs_rangeproof_uncompressed_verify(
277+
[[maybe_unused]] auto ret = secp256k1_bulletproofs_rangeproof_uncompressed_verify(
321278
ctx,
322279
scratch,
323280
generators.get(),

0 commit comments

Comments
 (0)