Skip to content

Commit b38f027

Browse files
committed
WIP: Preliminary update of the audit-tool for cryptographic commitments
Signed-off-by: Sam Stuewe <[email protected]>
1 parent 437a93b commit b38f027

File tree

10 files changed

+152
-51
lines changed

10 files changed

+152
-51
lines changed

src/uhs/atomizer/shard/controller.cpp

+11-8
Original file line numberDiff line numberDiff line change
@@ -235,15 +235,18 @@ namespace cbdc::shard {
235235
m_audit_thread.join();
236236
}
237237
m_audit_thread = std::thread([this, s = std::move(snp), height]() {
238-
auto maybe_total = m_shard.audit(s);
239-
if(!maybe_total.has_value()) {
240-
m_logger->fatal("Error running audit at height", height);
238+
auto range_summaries = m_shard.audit(s);
239+
auto buf = cbdc::buffer();
240+
buf.extend(sizeof(commitment_t));
241+
for(const auto& [bucket, summary] : range_summaries) {
242+
buf.clear();
243+
buf.append(summary.data(), summary.size());
244+
m_audit_log
245+
<< height << " "
246+
<< static_cast<int>(bucket) << " "
247+
<< buf.to_hex() << std::endl;
241248
}
242-
m_audit_log << height << " " << maybe_total.value() << std::endl;
243-
m_logger->info("Audit completed for",
244-
height,
245-
maybe_total.value(),
246-
"coins total");
249+
m_logger->info("Audit completed for", height);
247250
});
248251
}
249252
}

src/uhs/atomizer/shard/shard.cpp

+27-16
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
55

66
#include "shard.hpp"
7+
#include "uhs/transaction/messages.hpp"
78

89
#include <utility>
910

@@ -205,34 +206,44 @@ namespace cbdc::shard {
205206
}
206207

207208
auto shard::audit(const std::shared_ptr<const leveldb::Snapshot>& snp)
208-
-> std::optional<uint64_t> {
209+
-> std::unordered_map<unsigned char, commitment_t> {
210+
std::unordered_map<unsigned char, std::vector<commitment_t>> comms{};
209211
auto opts = leveldb::ReadOptions();
210212
opts.snapshot = snp.get();
211213
auto it = std::shared_ptr<leveldb::Iterator>(m_db->NewIterator(opts));
212214
it->SeekToFirst();
213215
// Skip best block height key
214216
it->Next();
215-
uint64_t tot{};
216217
for(; it->Valid(); it->Next()) {
217218
auto key = it->key();
218-
auto buf = cbdc::buffer();
219-
buf.extend(key.size());
220-
std::memcpy(buf.data(), key.data(), key.size());
221-
auto maybe_uhs_element
222-
= cbdc::from_buffer<transaction::uhs_element>(buf);
223-
if(!maybe_uhs_element.has_value()) {
224-
return std::nullopt;
219+
auto val = it->value();
220+
221+
transaction::compact_output outp{};
222+
std::memcpy(outp.m_id.data(), key.data(), key.size());
223+
std::memcpy(outp.m_auxiliary.data(), val.data(), outp.m_auxiliary.size());
224+
std::memcpy(outp.m_range.data(), val.data() + sizeof(outp.m_auxiliary), outp.m_range.size());
225+
std::memcpy(outp.m_provenance.data(), val.data() + sizeof(outp.m_auxiliary) + sizeof(outp.m_range), outp.m_provenance.size());
226+
if(!transaction::validate_uhs_id(outp)) {
227+
continue;
228+
}
229+
auto bucket = outp.m_id[0];
230+
if(comms.find(bucket) == comms.end()) {
231+
std::vector<commitment_t> commits{};
232+
commits.reserve(1);
233+
comms.emplace(bucket, std::move(commits));
225234
}
226-
auto& uhs_element = maybe_uhs_element.value();
227-
if(transaction::calculate_uhs_id(uhs_element.m_data,
228-
uhs_element.m_value)
229-
!= uhs_element.m_id) {
230-
return std::nullopt;
235+
comms[bucket].emplace_back(std::move(outp.m_auxiliary));
236+
}
237+
238+
std::unordered_map<unsigned char, commitment_t> summaries{};
239+
for(auto& [k, v] : comms) {
240+
auto summary = sum_commitments(m_secp.get(), v);
241+
if (summary.has_value()) {
242+
summaries[k] = summary.value();
231243
}
232-
tot += uhs_element.m_value;
233244
}
234245

235-
return tot;
246+
return summaries;
236247
}
237248

238249
auto shard::get_snapshot() -> std::shared_ptr<const leveldb::Snapshot> {

src/uhs/atomizer/shard/shard.hpp

+9-4
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,9 @@ namespace cbdc::shard {
7272
/// Audit the supply of coins in this shard's UHS and check UHS IDs
7373
/// match the nested data and value stored in the UHS.
7474
/// \param snp LevelDB snapshot upon which to calculate the audit.
75-
/// \return total value of all UHS elements in the snapshot or
76-
/// std::nullopt if any of the UHS elements do not match their
77-
/// UHS ID.
75+
/// \return per-range summary commitments to value
7876
auto audit(const std::shared_ptr<const leveldb::Snapshot>& snp)
79-
-> std::optional<uint64_t>;
77+
-> std::unordered_map<unsigned char, commitment_t>;
8078

8179
private:
8280
[[nodiscard]] auto is_output_on_shard(const hash_t& uhs_hash) const
@@ -96,6 +94,13 @@ namespace cbdc::shard {
9694

9795
const std::string m_best_block_height_key;
9896

97+
static const inline auto m_secp
98+
= std::unique_ptr<secp256k1_context,
99+
decltype(&secp256k1_context_destroy)>(
100+
secp256k1_context_create(SECP256K1_CONTEXT_SIGN
101+
| SECP256K1_CONTEXT_VERIFY),
102+
&secp256k1_context_destroy);
103+
99104
std::pair<uint8_t, uint8_t> m_prefix_range;
100105
};
101106
}

src/uhs/transaction/transaction.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,17 @@ namespace cbdc::transaction {
182182
return id;
183183
}
184184

185+
auto validate_uhs_id(const compact_output& output) -> bool {
186+
CSHA256 sha;
187+
sha.Write(output.m_provenance.data(), output.m_provenance.size());
188+
sha.Write(output.m_auxiliary.data(), output.m_auxiliary.size());
189+
190+
hash_t check{};
191+
sha.Finalize(check.data());
192+
193+
return output.m_id == check;
194+
}
195+
185196
auto roll_auxiliaries(secp256k1_context* ctx,
186197
random_source& rng,
187198
const std::vector<hash_t>& blinds,

src/uhs/transaction/transaction.hpp

+9-4
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,6 @@ namespace cbdc::transaction {
8484
const out_point& point,
8585
const output& put) -> std::pair<hash_t, hash_t>;
8686

87-
auto calculate_uhs_id(const out_point& point,
88-
const output& put,
89-
const commitment_t& value) -> hash_t;
90-
9187
/// \brief Additional information a spender needs to spend an input
9288
struct spend_data {
9389
/// The blinding factor for the auxiliary commitment
@@ -179,6 +175,15 @@ namespace cbdc::transaction {
179175
auto operator!=(const compact_output& rhs) const -> bool;
180176
};
181177

178+
/// \brief Validates that a compact_output's UHS ID matches its contents
179+
///
180+
/// Simply rehashes the provenance nested-hash and value commitment,
181+
/// and checks to see the result is identical to the ID.
182+
///
183+
/// \param output the compact_output itself
184+
/// \returns true if the rehash matches the included ID, false otherwise
185+
auto validate_uhs_id(const compact_output& output) -> bool;
186+
182187
/// \brief A condensed, hash-only transaction representation
183188
///
184189
/// The minimum amount of data necessary for the transaction processor to

src/util/common/commitment.cpp

+37
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,41 @@ namespace cbdc {
6161

6262
return commitment;
6363
}
64+
65+
auto sum_commitments(const secp256k1_context* ctx,
66+
std::vector<commitment_t> commitments)
67+
-> std::optional<commitment_t> {
68+
if(commitments.size() == 0) {
69+
return std::nullopt;
70+
} else if(commitments.size() == 1) {
71+
return {commitments[0]};
72+
}
73+
74+
std::vector<secp256k1_pubkey> as_keys{};
75+
for(auto& c : commitments) {
76+
auto maybe_pc = deserialize_commitment(ctx, c);
77+
if(!maybe_pc.has_value()) {
78+
return std::nullopt;
79+
}
80+
81+
auto pc = maybe_pc.value();
82+
secp256k1_pubkey k{};
83+
secp256k1_pedersen_commitment_as_key(&pc, &k);
84+
as_keys.emplace_back(k);
85+
}
86+
87+
secp256k1_pubkey k{};
88+
auto res =
89+
secp256k1_ec_pubkey_combine(ctx, &k,
90+
reinterpret_cast<const secp256k1_pubkey* const*>(as_keys.data()),
91+
as_keys.size());
92+
if(res != 1) {
93+
return std::nullopt;
94+
}
95+
96+
secp256k1_pedersen_commitment summary{};
97+
secp256k1_pubkey_as_pedersen_commitment(ctx, &k, &summary);
98+
99+
return serialize_commitment(ctx, summary);
100+
}
64101
}

src/util/common/commitment.hpp

+10
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ namespace cbdc {
5555
auto deserialize_commitment(const secp256k1_context* ctx,
5656
commitment_t comm)
5757
-> std::optional<secp256k1_pedersen_commitment>;
58+
59+
/// Attempts to sum a list of Pedersen commitments
60+
///
61+
/// \param ctx secp256k1 context initialized for signing and commitment
62+
/// \param commitments the vector of commitments to sum
63+
/// \return std::nullopt if conversion or summing failed; the summed
64+
/// commitment otherwise
65+
auto sum_commitments(const secp256k1_context* ctx,
66+
std::vector<commitment_t> commitments)
67+
-> std::optional<commitment_t>;
5868
}
5969

6070
#endif // OPENCBDC_TX_SRC_COMMON_COMMITMENT_H_

tests/integration/atomizer_end_to_end_test.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ class atomizer_end_to_end_test : public ::testing::Test {
8888
audit_entries.emplace_back(epoch, value);
8989
}
9090
ASSERT_FALSE(audit_entries.empty());
91-
ASSERT_EQ(audit_entries.back().second, 100ul);
91+
//ASSERT_EQ(audit_entries.back().second, 100ul);
92+
// todo: sum_commitments with circulation-commitment
9293

9394
std::filesystem::remove_all("archiver0_db");
9495
std::filesystem::remove_all("atomizer_raft_log_0");

tools/audit/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@ include_directories(../../src)
44

55
add_executable(audit audit.cpp)
66
target_link_libraries(audit common
7+
util
8+
serialization
79
crypto)

tools/audit/audit.cpp

+34-18
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
// Distributed under the MIT software license, see the accompanying
44
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
55

6-
#include "common/config.hpp"
6+
#include "util/common/config.hpp"
7+
#include "util/serialization/util.hpp"
8+
#include "util/serialization/format.hpp"
9+
#include "util/common/commitment.hpp"
710

811
#include <unordered_map>
912

@@ -24,13 +27,9 @@ auto main(int argc, char** argv) -> int {
2427
}
2528
auto cfg = std::get<cbdc::config::options>(cfg_or_err);
2629

27-
struct total {
28-
uint64_t m_total_value{};
29-
size_t m_shard_count{};
30-
};
31-
32-
auto totals = std::unordered_map<uint64_t, total>();
30+
auto audits = std::unordered_map<uint64_t, std::unordered_map<unsigned char, cbdc::commitment_t>>();
3331

32+
// todo: ensure/detect whether or not the most recent audit has finished
3433
for(auto& audit_file : cfg.m_shard_audit_logs) {
3534
auto f = std::ifstream(audit_file);
3635
if(!f.good()) {
@@ -39,22 +38,39 @@ auto main(int argc, char** argv) -> int {
3938
}
4039

4140
uint64_t epoch{};
42-
uint64_t total_value{};
43-
while(f >> epoch >> total_value) {
44-
auto it = totals.find(epoch);
45-
if(it != totals.end()) {
46-
it->second.m_total_value += total_value;
47-
it->second.m_shard_count++;
41+
std::string bucket_str{};
42+
std::string commit_hex{};
43+
while(f >> epoch >> bucket_str >> commit_hex) {
44+
auto bucket = static_cast<unsigned char>(std::stoul(bucket_str));
45+
46+
auto commitbuf = cbdc::buffer::from_hex(commit_hex);
47+
auto commit = cbdc::from_buffer<cbdc::commitment_t>(commitbuf.value()).value();
48+
49+
auto it = audits.find(epoch);
50+
if(it != audits.end()) {
51+
auto& audit = it->second;
52+
auto entry = audit.find(bucket);
53+
if(entry != audit.end()) {
54+
if(entry->second != commit) {
55+
std::cerr << "Audit failed at epoch " << epoch
56+
<< "; inconsistency in range "
57+
<< bucket_str << std::endl;
58+
return 1;
59+
}
60+
} else {
61+
audit[bucket] = commit;
62+
}
4863
} else {
49-
totals[epoch] = total{total_value, 1};
64+
auto entries = std::unordered_map<unsigned char, cbdc::commitment_t>();
65+
entries.emplace(bucket, std::move(commit));
66+
audits[epoch] = std::move(entries);
5067
}
5168
}
5269
}
5370

54-
for(auto& [epoch, tot] : totals) {
55-
std::cout << "epoch: " << epoch
56-
<< ", total_value: " << tot.m_total_value
57-
<< ", shard_count: " << tot.m_shard_count << std::endl;
71+
// todo: per-epoch: get a vector of all commitments, push_back circulation_commitment, check sum = 1
72+
for(auto& [epoch, entries] : audits) {
73+
std::cout << "epoch: " << epoch << std::endl;
5874
}
5975

6076
return 0;

0 commit comments

Comments
 (0)