Skip to content

Commit bd59057

Browse files
HalosGhostAlexRamRamwadagso-gertjaapmetalicjamesmaurermi
committed
fix: reconcile tests and benchmarks for proofs
This commit represents the updating of the rest of the testing and benchmarking code to function as-expected with the new transaction format and protocol. From this point forward, you should expect CI to pass. This is an omnibus update and includes help from multiple authors, I have attempted to capture contributors below as co-authors. Co-authored-by: Alexander Jung <[email protected]> Co-authored-by: Gert-Jaap Glasbergen <[email protected]> Co-authored-by: James Lovejoy <[email protected]> Co-authored-by: Michael Maurer <[email protected]> Signed-off-by: Sam Stuewe <[email protected]>
1 parent 9ecd34a commit bd59057

35 files changed

+757
-350
lines changed

benchmarks/CMakeLists.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ project(benchmarks)
33
include_directories(. ../src ../tools/watchtower ../3rdparty ../3rdparty/secp256k1/include)
44
set(SECP256K1_LIBRARY $<TARGET_FILE:secp256k1>)
55

6-
add_executable(run_benchmarks low_level.cpp
6+
add_executable(run_benchmarks audits.cpp
7+
low_level.cpp
78
transactions.cpp
89
uhs_leveldb.cpp
910
uhs_set.cpp

benchmarks/audits.cpp

+210
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
// Copyright (c) 2021 MIT Digital Currency Initiative,
2+
// Federal Reserve Bank of Boston
3+
// Distributed under the MIT software license, see the accompanying
4+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
6+
#include "uhs/transaction/transaction.hpp"
7+
#include "uhs/twophase/locking_shard/locking_shard.hpp"
8+
#include "util/common/hash.hpp"
9+
#include "util/common/hashmap.hpp"
10+
#include "util/common/keys.hpp"
11+
#include "util/common/config.hpp"
12+
#include "util/common/random_source.hpp"
13+
#include "util/common/snapshot_map.hpp"
14+
15+
#include <secp256k1_bppp.h>
16+
#include <benchmark/benchmark.h>
17+
#include <gtest/gtest.h>
18+
#include <unordered_map>
19+
#include <random>
20+
#include <unordered_set>
21+
22+
#define SWEEP_MAX 100000
23+
#define EPOCH 1000
24+
25+
using namespace cbdc;
26+
using secp256k1_context_destroy_type = void (*)(secp256k1_context*);
27+
using uhs_element = locking_shard::locking_shard::uhs_element;
28+
29+
struct GensDeleter {
30+
explicit GensDeleter(secp256k1_context* ctx) : m_ctx(ctx) {}
31+
32+
void operator()(secp256k1_bppp_generators* gens) const {
33+
secp256k1_bppp_generators_destroy(m_ctx, gens);
34+
}
35+
36+
secp256k1_context* m_ctx;
37+
};
38+
39+
static std::default_random_engine m_shuffle;
40+
41+
static const inline auto rnd
42+
= std::make_unique<random_source>(config::random_source);
43+
44+
static std::unique_ptr<secp256k1_context, secp256k1_context_destroy_type>
45+
secp{secp256k1_context_create(SECP256K1_CONTEXT_NONE),
46+
&secp256k1_context_destroy};
47+
48+
/// should be set to exactly `floor(log_base(value)) + 1`
49+
///
50+
/// We use n_bits = 64, base = 16, so this should always be 24.
51+
static const inline auto generator_count = 16 + 8;
52+
53+
static std::unique_ptr<secp256k1_bppp_generators, GensDeleter>
54+
generators{
55+
secp256k1_bppp_generators_create(secp.get(),
56+
generator_count),
57+
GensDeleter(secp.get())};
58+
59+
static auto gen_map(uint64_t map_size, bool deleted = false) -> snapshot_map<hash_t, uhs_element> {
60+
std::uniform_int_distribution<uint64_t> dist(EPOCH - 100, EPOCH + 100);
61+
auto uhs = snapshot_map<hash_t, uhs_element>();
62+
63+
auto comm = commit(secp.get(), 10, hash_t{}).value();
64+
auto rng
65+
= transaction::prove(secp.get(),
66+
generators.get(),
67+
*rnd,
68+
{hash_t{}, 10},
69+
&comm);
70+
auto commitment = serialize_commitment(secp.get(), comm);
71+
72+
for(uint64_t i = 1; i <= map_size; i++) {
73+
transaction::compact_output out{commitment, rng, rnd->random_hash()};
74+
auto del = deleted ? std::optional<uint64_t>{dist(m_shuffle)} : std::nullopt;
75+
uhs_element el0{out, 0, del};
76+
auto key = transaction::calculate_uhs_id(out);
77+
uhs.emplace(key, el0);
78+
}
79+
return uhs;
80+
}
81+
82+
static auto audit(snapshot_map<hash_t, uhs_element>& uhs,
83+
snapshot_map<hash_t, uhs_element>& locked,
84+
snapshot_map<hash_t, uhs_element>& spent)
85+
-> std::optional<commitment_t> {
86+
87+
{
88+
uhs.snapshot();
89+
locked.snapshot();
90+
spent.snapshot();
91+
}
92+
93+
bool failed = false;
94+
uint64_t epoch = EPOCH;
95+
96+
static constexpr auto scratch_size = 8192UL * 1024UL;
97+
[[maybe_unused]] secp256k1_scratch_space* scratch
98+
= secp256k1_scratch_space_create(secp.get(), scratch_size);
99+
100+
static constexpr size_t threshold = 100000;
101+
size_t cursor = 0;
102+
std::vector<commitment_t> comms{};
103+
auto* range_batch = secp256k1_bppp_rangeproof_batch_create(secp.get(), 34 * (threshold + 1));
104+
auto summarize
105+
= [&](const snapshot_map<hash_t, uhs_element>& m) {
106+
for(const auto& [id, elem] : m) {
107+
if(failed) {
108+
break;
109+
}
110+
if(elem.m_creation_epoch <= epoch
111+
&& (!elem.m_deletion_epoch.has_value()
112+
|| (elem.m_deletion_epoch.value() > epoch))) {
113+
114+
auto uhs_id
115+
= transaction::calculate_uhs_id(elem.m_out);
116+
if(uhs_id != id) {
117+
failed = true;
118+
}
119+
auto comm = elem.m_out.m_value_commitment;
120+
auto c = deserialize_commitment(secp.get(), comm).value();
121+
auto r = transaction::validation::range_batch_add(
122+
*range_batch,
123+
scratch,
124+
elem.m_out.m_range,
125+
c
126+
);
127+
if(!r.has_value()) {
128+
++cursor;
129+
}
130+
comms.push_back(comm);
131+
}
132+
if(cursor >= threshold) {
133+
failed = transaction::validation::check_range_batch(*range_batch).has_value();
134+
[[maybe_unused]] auto res = secp256k1_bppp_rangeproof_batch_clear(secp.get(), range_batch);
135+
cursor = 0;
136+
}
137+
}
138+
if(cursor > 0) {
139+
failed = transaction::validation::check_range_batch(*range_batch).has_value();
140+
[[maybe_unused]] auto res = secp256k1_bppp_rangeproof_batch_clear(secp.get(), range_batch);
141+
cursor = 0;
142+
}
143+
};
144+
145+
summarize(uhs);
146+
summarize(locked);
147+
summarize(spent);
148+
[[maybe_unused]] auto res = secp256k1_bppp_rangeproof_batch_destroy(secp.get(), range_batch);
149+
free(range_batch);
150+
151+
secp256k1_scratch_space_destroy(secp.get(), scratch);
152+
153+
// std::vector<commitment_t> comms{};
154+
// comms.reserve(pool.size());
155+
//
156+
// for(auto& f : pool) {
157+
// auto c = f.get();
158+
// failed = !c.has_value();
159+
// if(failed) {
160+
// break;
161+
// }
162+
// comms.emplace_back(std::move(c.value()));
163+
//k }
164+
165+
{
166+
uhs.release_snapshot();
167+
locked.release_snapshot();
168+
spent.release_snapshot();
169+
}
170+
171+
if(failed) {
172+
return std::nullopt;
173+
}
174+
175+
return sum_commitments(secp.get(), comms);
176+
}
177+
178+
static void audit_routine(benchmark::State& state) {
179+
auto key_count = state.range(0);
180+
181+
auto seed = std::chrono::high_resolution_clock::now()
182+
.time_since_epoch()
183+
.count();
184+
seed %= std::numeric_limits<uint32_t>::max();
185+
m_shuffle.seed(static_cast<uint32_t>(seed));
186+
187+
uint32_t locked_sz{};
188+
uint32_t spent_sz{};
189+
{
190+
std::uniform_int_distribution<uint32_t> locked(0, key_count);
191+
locked_sz = locked(m_shuffle);
192+
std::uniform_int_distribution<uint32_t> spent(0, key_count - locked_sz);
193+
spent_sz = spent(m_shuffle);
194+
}
195+
196+
snapshot_map<hash_t, uhs_element> uhs = gen_map(key_count - (locked_sz + spent_sz));
197+
snapshot_map<hash_t, uhs_element> locked = gen_map(locked_sz);
198+
snapshot_map<hash_t, uhs_element> spent = gen_map(spent_sz, true);
199+
for(auto _ : state) {
200+
auto res = audit(uhs, locked, spent);
201+
ASSERT_NE(res, std::nullopt);
202+
}
203+
}
204+
205+
BENCHMARK(audit_routine)
206+
->RangeMultiplier(10)
207+
->Range(10, SWEEP_MAX)
208+
->Complexity(benchmark::oAuto);
209+
210+
BENCHMARK_MAIN();

benchmarks/low_level.cpp

+1-9
Original file line numberDiff line numberDiff line change
@@ -166,15 +166,7 @@ BENCHMARK_F(low_level, no_inputs)(benchmark::State& state) {
166166
BENCHMARK_F(low_level, calculate_uhs_id)(benchmark::State& state) {
167167
m_valid_tx = wallet1.send_to(2, wallet2.generate_key(), true).value();
168168
auto cp_tx = cbdc::transaction::compact_tx(m_valid_tx);
169-
auto engine = std::default_random_engine();
170169
for(auto _ : state) {
171-
state.PauseTiming();
172-
uint64_t i = (uint64_t)engine();
173-
state.ResumeTiming();
174-
cbdc::transaction::uhs_id_from_output(cp_tx.m_id,
175-
i,
176-
m_valid_tx.m_outputs[0]);
170+
cbdc::transaction::calculate_uhs_id(cp_tx.m_outputs[0]);
177171
}
178172
}
179-
180-
BENCHMARK_MAIN();

benchmarks/uhs_leveldb.cpp

+18-16
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,12 @@ static void uhs_leveldb_put_new(benchmark::State& state) {
100100
db.wallet2.confirm_transaction(db.m_valid_tx);
101101

102102
db.m_cp_tx = cbdc::transaction::compact_tx(db.m_valid_tx);
103-
std::array<char, sizeof(db.m_cp_tx.m_uhs_outputs)> out_arr{};
103+
std::array<char, sizeof(db.m_cp_tx.m_outputs)> out_arr{};
104104
std::memcpy(out_arr.data(),
105-
db.m_cp_tx.m_uhs_outputs.data(),
106-
db.m_cp_tx.m_uhs_outputs.size());
105+
db.m_cp_tx.m_outputs.data(),
106+
db.m_cp_tx.m_outputs.size());
107107
leveldb::Slice OutPointKey(out_arr.data(),
108-
db.m_cp_tx.m_uhs_outputs.size());
108+
db.m_cp_tx.m_outputs.size());
109109

110110
// actual storage
111111
state.ResumeTiming();
@@ -121,12 +121,12 @@ static void uhs_leveldb_item_delete(benchmark::State& state) {
121121
auto db = db_container();
122122

123123
db.m_cp_tx = cbdc::transaction::compact_tx(db.m_valid_tx);
124-
std::array<char, sizeof(db.m_cp_tx.m_uhs_outputs)> out_arr{};
124+
std::array<char, sizeof(db.m_cp_tx.m_outputs)> out_arr{};
125125
std::memcpy(out_arr.data(),
126-
db.m_cp_tx.m_uhs_outputs.data(),
127-
db.m_cp_tx.m_uhs_outputs.size());
126+
db.m_cp_tx.m_outputs.data(),
127+
db.m_cp_tx.m_outputs.size());
128128
leveldb::Slice OutPointKey(out_arr.data(),
129-
db.m_cp_tx.m_uhs_outputs.size());
129+
db.m_cp_tx.m_outputs.size());
130130

131131
for(auto _ : state) {
132132
state.PauseTiming();
@@ -148,10 +148,11 @@ static void uhs_leveldb_shard_sim(benchmark::State& state) {
148148
leveldb::WriteBatch batch;
149149
state.ResumeTiming();
150150
for(const auto& tx : db.block) {
151-
for(const auto& out : tx.m_uhs_outputs) {
152-
std::array<char, sizeof(out)> out_arr{};
153-
std::memcpy(out_arr.data(), out.data(), out.size());
154-
leveldb::Slice OutPointKey(out_arr.data(), out.size());
151+
for(const auto& out : tx.m_outputs) {
152+
auto id = calculate_uhs_id(out);
153+
std::array<char, sizeof(id)> out_arr{};
154+
std::memcpy(out_arr.data(), id.data(), id.size());
155+
leveldb::Slice OutPointKey(out_arr.data(), id.size());
155156
batch.Put(OutPointKey, leveldb::Slice());
156157
}
157158
for(const auto& inp : tx.m_inputs) {
@@ -176,10 +177,11 @@ static void uhs_leveldb_shard_sim_brief(benchmark::State& state) {
176177
leveldb::WriteBatch batch;
177178
state.ResumeTiming();
178179
for(const auto& tx : db.block_abridged) {
179-
for(const auto& out : tx.m_uhs_outputs) {
180-
std::array<char, sizeof(out)> out_arr{};
181-
std::memcpy(out_arr.data(), out.data(), out.size());
182-
leveldb::Slice OutPointKey(out_arr.data(), out.size());
180+
for(const auto& out : tx.m_outputs) {
181+
auto id = calculate_uhs_id(out);
182+
std::array<char, sizeof(id)> out_arr{};
183+
std::memcpy(out_arr.data(), id.data(), id.size());
184+
leveldb::Slice OutPointKey(out_arr.data(), id.size());
183185
batch.Put(OutPointKey, leveldb::Slice());
184186
}
185187
for(const auto& inp : tx.m_inputs) {

benchmarks/uhs_set.cpp

+33-23
Original file line numberDiff line numberDiff line change
@@ -6,59 +6,69 @@
66
#include "uhs/transaction/transaction.hpp"
77
#include "uhs/transaction/validation.hpp"
88
#include "uhs/transaction/wallet.hpp"
9+
#include "uhs/twophase/locking_shard/locking_shard.hpp"
910
#include "util/common/hash.hpp"
1011
#include "util/common/hashmap.hpp"
12+
#include "util/common/snapshot_map.hpp"
1113

1214
#include <benchmark/benchmark.h>
1315
#include <gtest/gtest.h>
1416
#include <unordered_set>
1517
#include <variant>
1618

1719
class uhs_set : public ::benchmark::Fixture {
20+
public:
21+
uhs_set() {
22+
Iterations(10000);
23+
}
24+
1825
protected:
1926
void SetUp(const ::benchmark::State&) override {
27+
m_uhs.snapshot();
28+
2029
auto mint_tx1 = wallet1.mint_new_coins(1, 100);
2130
wallet1.confirm_transaction(mint_tx1);
31+
auto cp_1 = cbdc::transaction::compact_tx(mint_tx1);
32+
auto id_1 = cbdc::transaction::calculate_uhs_id(cp_1.m_outputs[0]);
33+
uhs_element el_1 {cp_1.m_outputs[0], epoch++, std::nullopt};
34+
m_uhs.emplace(id_1, el_1);
35+
2236
auto mint_tx2 = wallet2.mint_new_coins(1, 100);
2337
wallet2.confirm_transaction(mint_tx2);
24-
m_cp_tx = cbdc::transaction::compact_tx(m_valid_tx);
38+
auto cp_2 = cbdc::transaction::compact_tx(mint_tx2);
39+
auto id_2 = cbdc::transaction::calculate_uhs_id(cp_2.m_outputs[0]);
40+
uhs_element el_2 {cp_2.m_outputs[0], epoch++, std::nullopt};
41+
m_uhs.emplace(id_2, el_2);
2542
}
2643

2744
cbdc::transaction::wallet wallet1;
2845
cbdc::transaction::wallet wallet2;
2946

3047
cbdc::transaction::full_tx m_valid_tx{};
31-
cbdc::transaction::compact_tx m_cp_tx;
48+
cbdc::transaction::compact_tx m_cp_tx{};
3249

33-
std::unordered_set<cbdc::hash_t, cbdc::hashing::null> set;
50+
using uhs_element = cbdc::locking_shard::locking_shard::uhs_element;
51+
cbdc::snapshot_map<cbdc::hash_t, uhs_element> m_uhs{};
52+
size_t epoch{0};
3453
};
3554

36-
// benchmark how long it takes to emplace new values into an unordered set
37-
BENCHMARK_F(uhs_set, emplace_new)(benchmark::State& state) {
55+
// minimal tx execution
56+
BENCHMARK_F(uhs_set, swap)(benchmark::State& state) {
3857
for(auto _ : state) {
3958
m_valid_tx = wallet1.send_to(2, wallet1.generate_key(), true).value();
4059
wallet1.confirm_transaction(m_valid_tx);
4160
m_cp_tx = cbdc::transaction::compact_tx(m_valid_tx);
4261

4362
state.ResumeTiming();
44-
set.emplace(m_cp_tx.m_id);
45-
state.PauseTiming();
46-
47-
m_cp_tx = cbdc::transaction::compact_tx(m_valid_tx);
48-
}
49-
}
50-
51-
// benchmark how long it takes to remove values from an unordered set
52-
BENCHMARK_F(uhs_set, erase_item)(benchmark::State& state) {
53-
for(auto _ : state) {
54-
m_valid_tx = wallet1.send_to(2, wallet1.generate_key(), true).value();
55-
wallet1.confirm_transaction(m_valid_tx);
56-
m_cp_tx = cbdc::transaction::compact_tx(m_valid_tx);
57-
set.emplace(m_cp_tx.m_id);
58-
state.ResumeTiming();
59-
set.erase(m_cp_tx.m_id);
63+
for(const auto& inp : m_cp_tx.m_inputs) {
64+
m_uhs.erase(inp);
65+
}
66+
for(const auto& outp : m_cp_tx.m_outputs) {
67+
const auto& uhs_id = cbdc::transaction::calculate_uhs_id(outp);
68+
uhs_element el{outp, epoch, std::nullopt};
69+
m_uhs.emplace(uhs_id, el);
70+
}
6071
state.PauseTiming();
61-
62-
m_cp_tx = cbdc::transaction::compact_tx(m_valid_tx);
72+
epoch++;
6373
}
6474
}

0 commit comments

Comments
 (0)