1
0
Fork 0
forked from lthn/blockchain

decompose_amount_randomly + unit test

This commit is contained in:
sowle 2022-09-29 21:54:15 +02:00
parent 5bfbb8091d
commit df11160e7a
No known key found for this signature in database
GPG key ID: C07A24B2D89D49FC
4 changed files with 124 additions and 10 deletions

View file

@ -62,6 +62,7 @@
#define WALLET_MAX_ALLOWED_OUTPUT_AMOUNT ((uint64_t)0xffffffffffffffffLL)
#define CURRENCY_MINER_TX_MAX_OUTS CURRENCY_TX_MAX_ALLOWED_OUTS
#define CURRENCY_TX_OUTS_RND_SPLIT_DIGITS_TO_KEEP 3
#define DIFFICULTY_STARTER 1
#define DIFFICULTY_POS_TARGET 120 // seconds

View file

@ -207,16 +207,7 @@ namespace currency
if (tx_version > TRANSACTION_VERSION_PRE_HF4)
{
// randomly split into CURRENCY_TX_MIN_ALLOWED_OUTS outputs
// TODO: consider refactoring
uint64_t amount_remaining = block_reward;
for(size_t i = 1; i < CURRENCY_TX_MIN_ALLOWED_OUTS; ++i) // starting from 1 for one less iteration
{
uint64_t amount = crypto::rand<uint64_t>() % amount_remaining;
amount_remaining -= amount;
out_amounts.push_back(amount);
}
out_amounts.push_back(amount_remaining);
// std::shuffle(out_amounts.begin(), out_amounts.end(), crypto::uniform_random_bit_generator());
decompose_amount_randomly(block_reward, [&](uint64_t a){ out_amounts.push_back(a); }, CURRENCY_TX_MIN_ALLOWED_OUTS);
}
else
{

View file

@ -699,6 +699,49 @@ namespace currency
{
decompose_amount_into_digits(amount, dust_threshold, chunk_handler, dust_handler, max_output_allowed, CURRENCY_TX_MAX_ALLOWED_OUTS, 0);
}
// num_digits_to_keep -- how many digits to keep in chunks, 0 means all digits
// Ex.: num_digits_to_keep == 3, number_of_outputs == 2 then 1.0 may be decomposed into 0.183 + 0.817
// num_digits_to_keep == 0, number_of_outputs == 2 then 1.0 may be decomposed into 0.183374827362 + 0.816625172638
template<typename chunk_handler_t>
void decompose_amount_randomly(uint64_t amount, chunk_handler_t chunk_cb, size_t number_of_outputs = CURRENCY_TX_MIN_ALLOWED_OUTS, size_t num_digits_to_keep = CURRENCY_TX_OUTS_RND_SPLIT_DIGITS_TO_KEEP)
{
if (amount < number_of_outputs)
return;
uint64_t boundary = 1000;
if (num_digits_to_keep != CURRENCY_TX_OUTS_RND_SPLIT_DIGITS_TO_KEEP)
{
boundary = 1;
for(size_t i = 0; i < num_digits_to_keep; ++i)
boundary *= 10;
}
auto trim_digits_and_add_variance = [boundary, num_digits_to_keep](uint64_t& v){
if (num_digits_to_keep != 0 && v > 1)
{
uint64_t multiplier = 1;
while(v >= boundary)
{
v /= 10;
multiplier *= 10;
}
v = v / 2 + crypto::rand<uint64_t>() % (v + 1);
v *= multiplier;
}
};
uint64_t amount_remaining = amount;
for(size_t i = 1; i < number_of_outputs && amount_remaining > 1; ++i) // starting from 1 for one less iteration
{
uint64_t chunk_amount = amount_remaining / (number_of_outputs - i + 1);
trim_digits_and_add_variance(chunk_amount);
amount_remaining -= chunk_amount;
chunk_cb(chunk_amount);
}
chunk_cb(amount_remaining);
}
//---------------------------------------------------------------
inline size_t get_input_expected_signatures_count(const txin_v& tx_in)
{

View file

@ -122,3 +122,82 @@ TEST_neg(0__0);
TEST_neg(0_0_);
TEST_neg(_0_0);
TEST_neg(0_0_0);
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
size_t get_nonzero_digits_count(uint64_t x)
{
size_t result = 0;
while(x != 0)
{
if (x % 10 != 0)
++result;
x /= 10;
}
return result;
}
void foo(uint64_t amount, size_t outputs_count, size_t num_digits_to_keep)
{
std::vector<uint64_t> vec;
decompose_amount_randomly(amount, [&](uint64_t a){ vec.push_back(a); }, outputs_count, num_digits_to_keep);
//std::cout << amount << " -> (" << vec.size() << ") ";
ASSERT_EQ(vec.size(), outputs_count);
for(size_t i = 0; i + 1 < outputs_count; ++i)
{
//std::cout << vec[i] << ",";
ASSERT_LE(get_nonzero_digits_count(vec[i]), num_digits_to_keep);
}
//std::cout << vec.back() << ENDL;
ASSERT_LE(get_nonzero_digits_count(vec.back()), num_digits_to_keep);
}
TEST(decompose_amount_randomly, 1)
{
std::vector<uint64_t> vec;
for(size_t i = 0; i < 1000; ++i)
{
vec.clear();
decompose_amount_randomly(0, [&](uint64_t a){ vec.push_back(a); }, 2, 3);
ASSERT_EQ(vec.size(), 0);
vec.clear();
decompose_amount_randomly(1, [&](uint64_t a){ vec.push_back(a); }, 2, 3);
ASSERT_EQ(vec.size(), 0);
vec.clear();
decompose_amount_randomly(2, [&](uint64_t a){ vec.push_back(a); }, 2, 3);
ASSERT_EQ(vec.size(), 2);
ASSERT_EQ(vec[0], 1);
ASSERT_EQ(vec[1], 1);
vec.clear();
decompose_amount_randomly(4, [&](uint64_t a){ vec.push_back(a); }, 2, 1);
ASSERT_EQ(vec.size(), 2);
ASSERT_LE(vec[0], 3);
ASSERT_GE(vec[0], 1);
ASSERT_LE(vec[1], 3);
ASSERT_GE(vec[1], 1);
vec.clear();
decompose_amount_randomly(3, [&](uint64_t a){ vec.push_back(a); }, 3, 1);
ASSERT_EQ(vec.size(), 3);
ASSERT_EQ(vec[0], 1);
ASSERT_EQ(vec[1], 1);
ASSERT_EQ(vec[2], 1);
foo(1000, 2, 3);
foo(1000, 2, 2);
foo(1000, 2, 1);
foo(10000, 4, 2);
foo(10010, 4, 3);
foo(17283, 4, 4);
foo(100000, 4, 5);
foo(100000, 4, 5);
foo(1000000000000, 2, 3);
}
}