Electroneum
Toggle main menu visibility
Loading...
Searching...
No Matches
output_selection.cpp
Go to the documentation of this file.
1
// Copyrights(c) 2017-2021, The Electroneum Project
2
// Copyrights(c) 2014-2019, The Monero Project
3
//
4
// All rights reserved.
5
//
6
// Redistribution and use in source and binary forms, with or without modification, are
7
// permitted provided that the following conditions are met:
8
//
9
// 1. Redistributions of source code must retain the above copyright notice, this list of
10
// conditions and the following disclaimer.
11
//
12
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
13
// of conditions and the following disclaimer in the documentation and/or other
14
// materials provided with the distribution.
15
//
16
// 3. Neither the name of the copyright holder nor the names of its contributors may be
17
// used to endorse or promote products derived from this software without specific
18
// prior written permission.
19
//
20
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
21
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30
// FIXME: move this into a full wallet2 unit test suite, if possible
31
32
#include "gtest/gtest.h"
33
34
#include "
wallet/wallet2.h
"
35
#include <string>
36
37
static
tools::wallet2::transfer_container
make_transfers_container(
size_t
N)
38
{
39
tools::wallet2::transfer_container
transfers;
40
for
(
size_t
n = 0; n < N; ++n)
41
{
42
transfers.push_back(
AUTO_VAL_INIT
(
tools::wallet2::transfer_details
()));
43
tools::wallet2::transfer_details
&td = transfers.back();
44
td.
m_block_height
= 1000;
45
td.
m_spent
=
false
;
46
td.
m_txid
= crypto::null_hash;
47
td.
m_txid
.data[0] = n & 0xff;
48
td.
m_txid
.data[1] = (n >> 8) & 0xff;
49
td.
m_txid
.data[2] = (n >> 16) & 0xff;
50
td.
m_txid
.data[3] = (n >> 24) & 0xff;
51
}
52
return
transfers;
53
}
54
55
#define SELECT(idx) \
56
do { \
57
auto i = std::find(unused_indices.begin(), unused_indices.end(), idx); \
58
ASSERT_TRUE(i != unused_indices.end()); \
59
unused_indices.erase(i); \
60
selected.push_back(idx); \
61
} while(0)
62
63
#define PICK(expected) \
64
do { \
65
size_t idx = w.pop_best_value_from(transfers, unused_indices, selected); \
66
ASSERT_EQ(expected, idx); \
67
selected.push_back(idx); \
68
} while(0)
69
70
TEST
(select_outputs, one_out_of_N)
71
{
72
tools::wallet2
w;
73
74
// check that if there are N-1 outputs of the same height, one of them
75
// already selected, the next one selected is the one that's from a
76
// different height
77
tools::wallet2::transfer_container
transfers = make_transfers_container(10);
78
transfers[6].m_block_height = 700;
79
std::vector<size_t> unused_indices({0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
80
std::vector<size_t> selected;
81
SELECT
(2);
82
PICK
(6);
83
}
84
85
TEST
(select_outputs, order)
86
{
87
tools::wallet2
w;
88
89
// check that most unrelated heights are picked in order
90
tools::wallet2::transfer_container
transfers = make_transfers_container(5);
91
transfers[0].m_block_height = 700;
92
transfers[1].m_block_height = 700;
93
transfers[2].m_block_height = 704;
94
transfers[3].m_block_height = 716;
95
transfers[4].m_block_height = 701;
96
std::vector<size_t> unused_indices({0, 1, 2, 3, 4});
97
std::vector<size_t> selected;
98
SELECT
(0);
99
PICK
(3);
// first the one that's far away
100
PICK
(2);
// then the one that's close
101
PICK
(4);
// then the one that's adjacent
102
PICK
(1);
// then the one that's on the same height
103
}
104
105
#define MKOFFSETS(N, n) \
106
offsets.resize(N); \
107
size_t n_outs = 0; \
108
for (auto &offset: offsets) \
109
{ \
110
offset = n_outs += (n); \
111
}
112
113
TEST
(select_outputs, gamma)
114
{
115
std::vector<uint64_t> offsets;
116
117
MKOFFSETS
(300000, 1);
118
tools::gamma_picker
picker(offsets);
119
std::vector<double> ages(100000);
120
double
age_scale = 120. * (offsets.size() / (
double
)n_outs);
121
for
(
size_t
i = 0; i < ages.size(); )
122
{
123
uint64_t
o = picker.
pick
();
124
if
(o >= n_outs)
125
continue
;
126
ages[i] = (n_outs - 1 - o) * age_scale;
127
ASSERT_GE
(ages[i], 0);
128
ASSERT_LE
(ages[i], offsets.size() * 120);
129
++i;
130
}
131
double
median =
epee::misc_utils::median
(ages);
132
MDEBUG
(
"median age: "
<< median / 86400. <<
" days"
);
133
ASSERT_GE
(median, 1.3 * 86400);
134
ASSERT_LE
(median, 1.4 * 86400);
135
}
136
137
TEST
(select_outputs, density)
138
{
139
static
const
size_t
NPICKS = 1000000;
140
std::vector<uint64_t> offsets;
141
142
MKOFFSETS
(300000, 1 + (rand() & 0x1f));
143
tools::gamma_picker
picker(offsets);
144
145
std::vector<int> picks(
/*n_outs*/
offsets.size(), 0);
146
for
(
int
i = 0; i < NPICKS; )
147
{
148
uint64_t
o = picker.
pick
();
149
if
(o >= n_outs)
150
continue
;
151
auto
it = std::lower_bound(offsets.begin(), offsets.end(), o);
152
auto
idx = std::distance(offsets.begin(), it);
153
ASSERT_LT
(idx, picks.size());
154
++picks[idx];
155
++i;
156
}
157
158
for
(
int
d = 1; d < 0x20; ++d)
159
{
160
// count the number of times an output in a block of d outputs was selected
161
// count how many outputs are in a block of d outputs
162
size_t
count_selected = 0, count_chain = 0;
163
for
(
size_t
i = 0; i < offsets.size(); ++i)
164
{
165
size_t
n_outputs = offsets[i] - (i == 0 ? 0 : offsets[i - 1]);
166
if
(n_outputs == d)
167
{
168
count_selected += picks[i];
169
count_chain += d;
170
}
171
}
172
float
selected_ratio = count_selected / (float)NPICKS;
173
float
chain_ratio = count_chain / (float)n_outs;
174
MDEBUG
(count_selected <<
"/"
<< NPICKS <<
" outputs selected in blocks of density "
<< d <<
", "
<< 100.0f * selected_ratio <<
"%"
);
175
MDEBUG
(count_chain <<
"/"
<< offsets.size() <<
" outputs in blocks of density "
<< d <<
", "
<< 100.0f * chain_ratio <<
"%"
);
176
ASSERT_LT
(fabsf(selected_ratio - chain_ratio), 0.025f);
177
}
178
}
179
180
TEST
(select_outputs, same_distribution)
181
{
182
static
const
size_t
NPICKS = 1000000;
183
std::vector<uint64_t> offsets;
184
185
MKOFFSETS
(300000, 1 + (rand() & 0x1f));
186
tools::gamma_picker
picker(offsets);
187
188
std::vector<int> chain_picks(offsets.size(), 0);
189
std::vector<int> output_picks(n_outs, 0);
190
for
(
int
i = 0; i < NPICKS; )
191
{
192
uint64_t
o = picker.
pick
();
193
if
(o >= n_outs)
194
continue
;
195
auto
it = std::lower_bound(offsets.begin(), offsets.end(), o);
196
auto
idx = std::distance(offsets.begin(), it);
197
ASSERT_LT
(idx, chain_picks.size());
198
++chain_picks[idx];
199
++output_picks[o];
200
++i;
201
}
202
203
// scale them both to 0-100
204
std::vector<int> chain_norm(100, 0), output_norm(100, 0);
205
for
(
size_t
i = 0; i < output_picks.size(); ++i)
206
output_norm[i * 100 / output_picks.size()] += output_picks[i];
207
for
(
size_t
i = 0; i < chain_picks.size(); ++i)
208
chain_norm[i * 100 / chain_picks.size()] += chain_picks[i];
209
210
double
max_dev = 0.0, avg_dev = 0.0;
211
for
(
size_t
i = 0; i < 100; ++i)
212
{
213
const
double
diff = (double)output_norm[i] - (
double
)chain_norm[i];
214
double
dev = fabs(2.0 * diff / (output_norm[i] + chain_norm[i]));
215
ASSERT_LT
(dev, 0.1);
216
avg_dev += dev;
217
}
218
avg_dev /= 100;
219
MDEBUG
(
"avg_dev: "
<< avg_dev);
220
ASSERT_LT
(avg_dev, 0.015);
221
}
tools::gamma_picker
Definition
wallet2.h:88
tools::gamma_picker::pick
uint64_t pick()
Definition
wallet2.cpp:1013
tools::wallet2
Definition
wallet2.h:210
tools::wallet2::transfer_container
std::vector< transfer_details > transfer_container
Definition
wallet2.h:449
ASSERT_LE
#define ASSERT_LE(val1, val2)
Definition
gtest.h:1964
TEST
#define TEST(test_case_name, test_name)
Definition
gtest.h:2187
ASSERT_GE
#define ASSERT_GE(val1, val2)
Definition
gtest.h:1972
ASSERT_LT
#define ASSERT_LT(val1, val2)
Definition
gtest.h:1968
AUTO_VAL_INIT
#define AUTO_VAL_INIT(v)
Definition
misc_language.h:53
MDEBUG
#define MDEBUG(x)
Definition
misc_log_ex.h:76
epee::misc_utils::median
type_vec_type median(std::vector< type_vec_type > &v)
Definition
misc_language.h:110
PICK
#define PICK(expected)
Definition
output_selection.cpp:63
SELECT
#define SELECT(idx)
Definition
output_selection.cpp:55
MKOFFSETS
#define MKOFFSETS(N, n)
Definition
output_selection.cpp:105
uint64_t
unsigned __int64 uint64_t
Definition
stdint.h:136
tools::wallet2::transfer_details
Definition
wallet2.h:302
tools::wallet2::transfer_details::m_spent
bool m_spent
Definition
wallet2.h:308
tools::wallet2::transfer_details::m_block_height
uint64_t m_block_height
Definition
wallet2.h:303
tools::wallet2::transfer_details::m_txid
crypto::hash m_txid
Definition
wallet2.h:305
wallet2.h
tests
unit_tests
output_selection.cpp
Generated on
for Electroneum by
1.17.0