forked from lthn/blockchain
decompose_amount_randomly + unit test
This commit is contained in:
parent
5bfbb8091d
commit
df11160e7a
4 changed files with 124 additions and 10 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue