From b6e84b460e0b9493083dcedfde9212fdeef56ea5 Mon Sep 17 00:00:00 2001 From: sowle Date: Tue, 2 Jul 2024 20:49:51 +0200 Subject: [PATCH] added decimal point support to currency::parse_amount() + minor refactoring --- src/currency_core/currency_format_utils.cpp | 11 +- src/currency_core/currency_format_utils.h | 2 +- src/simplewallet/simplewallet.cpp | 4 +- src/wallet/wallets_manager.cpp | 2 +- tests/unit_tests/amounts_tests.cpp | 117 +++++++++++++++++++- tests/unit_tests/test_format_utils.cpp | 45 -------- 6 files changed, 121 insertions(+), 60 deletions(-) diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index 2c8e5c2a..ce80baf3 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -793,8 +793,7 @@ namespace currency } } //--------------------------------------------------------------- - // TODO: reverse order of arguments - bool parse_amount(uint64_t& amount, const std::string& str_amount_) + bool parse_amount(const std::string& str_amount_, uint64_t& amount, const size_t decimal_point /* = CURRENCY_DISPLAY_DECIMAL_POINT */) { std::string str_amount = str_amount_; boost::algorithm::trim(str_amount); @@ -804,12 +803,12 @@ namespace currency if (std::string::npos != point_index) { fraction_size = str_amount.size() - point_index - 1; - while (CURRENCY_DISPLAY_DECIMAL_POINT < fraction_size && '0' == str_amount.back()) + while (decimal_point < fraction_size && '0' == str_amount.back()) { str_amount.erase(str_amount.size() - 1, 1); --fraction_size; } - if (CURRENCY_DISPLAY_DECIMAL_POINT < fraction_size) + if (decimal_point < fraction_size) return false; str_amount.erase(point_index, 1); } @@ -821,9 +820,9 @@ namespace currency if (str_amount.empty()) return false; - if (fraction_size < CURRENCY_DISPLAY_DECIMAL_POINT) + if (fraction_size < decimal_point) { - str_amount.append(CURRENCY_DISPLAY_DECIMAL_POINT - fraction_size, '0'); + str_amount.append(decimal_point - fraction_size, '0'); } return string_tools::get_xtype_from_string(amount, str_amount); diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index 5c4ea929..66fcce6a 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -402,7 +402,7 @@ namespace currency uint64_t get_outs_money_amount(const transaction& tx, const currency::account_keys& acc_keys_for_hidden_amounts = currency::null_acc_keys); bool check_inputs_types_supported(const transaction& tx); bool check_outs_valid(const transaction& tx); - bool parse_amount(uint64_t& amount, const std::string& str_amount); + bool parse_amount(const std::string& str_amount, uint64_t& amount, const size_t decimal_point = CURRENCY_DISPLAY_DECIMAL_POINT); bool parse_tracking_seed(const std::string& tracking_seed, account_public_address& address, crypto::secret_key& view_sec_key, uint64_t& creation_timestamp); diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 3935cb4c..ca67420c 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2592,7 +2592,7 @@ bool simple_wallet::sweep_below(const std::vector &args) } uint64_t amount = 0; - r = currency::parse_amount(amount, args[2]); + r = currency::parse_amount(args[2], amount); if (!r || amount == 0) { fail_msg_writer() << "incorrect amount: " << args[2]; @@ -3127,7 +3127,7 @@ int main(int argc, char* argv[]) uint64_t max_amount = 0; CHECK_AND_ASSERT_MES(epee::string_tools::string_to_num_fast(params[0], outs_min) && outs_min > 0 && outs_min < 256, EXIT_FAILURE, "incorrect param: " << params[0]); CHECK_AND_ASSERT_MES(epee::string_tools::string_to_num_fast(params[1], outs_max) && outs_max > 0 && outs_max < 256, EXIT_FAILURE, "incorrect param: " << params[1]); - CHECK_AND_ASSERT_MES(currency::parse_amount(max_amount, params[2]), EXIT_FAILURE, "incorrect param: " << params[2]); + CHECK_AND_ASSERT_MES(currency::parse_amount(params[2], max_amount), EXIT_FAILURE, "incorrect param: " << params[2]); wal.set_defragmentation_tx_settings(true, outs_min, outs_max, max_amount); } } diff --git a/src/wallet/wallets_manager.cpp b/src/wallet/wallets_manager.cpp index b039e86a..7f3ee253 100644 --- a/src/wallet/wallets_manager.cpp +++ b/src/wallet/wallets_manager.cpp @@ -1539,7 +1539,7 @@ std::string wallets_manager::transfer(uint64_t wallet_id, const view::transfer_p } - if(!currency::parse_amount(dsts.back().amount, d.amount)) + if(!currency::parse_amount(d.amount, dsts.back().amount)) { return API_RETURN_CODE_BAD_ARG_WRONG_AMOUNT; } diff --git a/tests/unit_tests/amounts_tests.cpp b/tests/unit_tests/amounts_tests.cpp index 300b92a7..24aa5b84 100644 --- a/tests/unit_tests/amounts_tests.cpp +++ b/tests/unit_tests/amounts_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2022 Zano Project +// Copyright (c) 2022-2024 Zano Project // Copyright (c) 2012-2013 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -10,23 +10,23 @@ using namespace currency; namespace { - void do_pos_test(uint64_t expected, const std::string& str) + void do_pos_test(uint64_t expected, const std::string& str, size_t decimal_point = CURRENCY_DISPLAY_DECIMAL_POINT) { uint64_t val; std::string number_str = str; std::replace(number_str.begin(), number_str.end(), '_', '.'); number_str.erase(std::remove(number_str.begin(), number_str.end(), '~'), number_str.end()); - ASSERT_TRUE(parse_amount(val, number_str)); + ASSERT_TRUE(parse_amount(number_str, val, decimal_point)); ASSERT_EQ(expected, val); } - void do_neg_test(const std::string& str) + void do_neg_test(const std::string& str, size_t decimal_point = CURRENCY_DISPLAY_DECIMAL_POINT) { uint64_t val; std::string number_str = str; std::replace(number_str.begin(), number_str.end(), '_', '.'); number_str.erase(std::remove(number_str.begin(), number_str.end(), '~'), number_str.end()); - ASSERT_FALSE(parse_amount(val, number_str)); + ASSERT_FALSE(parse_amount(number_str, val, decimal_point)); } } @@ -48,6 +48,23 @@ namespace do_neg_test(#str); \ } +#define TEST_pos_dp(expected, str, decimal_point) \ + TEST(parse_amount, handles_pos_ ## str ## _dp ## decimal_point) \ + { \ + do_pos_test(UINT64_C(expected), #str, decimal_point); \ + } + +#define TEST_neg_dp(str, decimal_point) \ + TEST(parse_amount, handles_neg_ ## str ## _dp ## decimal_point) \ + { \ + do_neg_test(#str, decimal_point); \ + } + +#define TEST_neg_n_dp(str, name, decimal_point) \ + TEST(parse_amount, handles_neg_ ## name ## _dp ## decimal_point) \ + { \ + do_neg_test(#str, decimal_point); \ + } TEST_pos(0, 0); TEST_pos(0, 00); @@ -96,6 +113,44 @@ TEST_pos(18446744073700000000, 18446744_0737000000000); TEST_pos(18446744073700000000, 18446744_07370000000000000000000); TEST_pos(18446744073709551615, 18446744_073709551615); +// non-standard decimal point +TEST_pos_dp(0, 0_0, 3); +TEST_pos_dp(0, 00_0, 3); +TEST_pos_dp(0, 00_00, 3); +TEST_pos_dp(0, 00000000_00, 3); +TEST_pos_dp(0, 00_000000000, 3); +TEST_pos_dp(0, 00_00000000000000000000000000000000, 3); + +TEST_pos_dp( 65535, 65535, 0); +TEST_pos_dp( 6553500, 65535, 2); +TEST_pos_dp( 65535000000, 65535, 6); +TEST_pos_dp( 18000000000000000000, 18, 18); +TEST_pos_dp( 1, 0_1, 1); +TEST_pos_dp( 10, 0_1, 2); +TEST_pos_dp( 100, 0_1, 3); +TEST_pos_dp( 10000000000000000000, 0_1, 20); +TEST_pos_dp( 1, 0_001, 3); +TEST_pos_dp( 123, 0_123, 3); +TEST_pos_dp( 1230, 0_123, 4); +TEST_pos_dp( 12300, 0_123, 5); +TEST_pos_dp( 123000, 0_123, 6); + +TEST_pos_dp(18446744073709551615, 18446744073709551615, 0); +TEST_pos_dp(18446744073709551615, 18446744073709551615_0, 0); + +TEST_pos_dp(18446744073709551615, 1844674407370955161_5, 1); +TEST_pos_dp(18446744073709551615, 1844674407370955161_50, 1); + +TEST_pos_dp(18446744073709551615, 18446744073709551_615, 3); +TEST_pos_dp(18446744073709551615, 18446744073709551_615000, 3); + +TEST_pos_dp(18446744073709551615, 1_8446744073709551615, 19); +TEST_pos_dp(18446744073709551615, 1_844674407370955161500, 19); + +TEST_pos_dp(18446744073709551615, 0_18446744073709551615, 20); +TEST_pos_dp(18446744073709551615, 0_1844674407370955161500, 20); + + // Invalid numbers TEST_neg_n(~, empty_string); TEST_neg_n(-0, minus_0); @@ -109,10 +164,17 @@ TEST_neg(0_0000000000001); TEST_neg(0_0000000000009); TEST_neg(18446744_0737000000001); +TEST_neg_dp(00_184467440737095516150001, 20); +TEST_neg_dp(00_184467440737095516151, 20); +TEST_neg_dp(1_2, 0); + // Overflow TEST_neg(184467440737_09551616); TEST_neg(184467440738); TEST_neg(18446744073709551616); +TEST_neg_dp(18446744073709551616, 0); +TEST_neg_dp(1844674407370955161_60, 1); +TEST_neg_dp(0_18446744073709551616, 20); // Two or more points TEST_neg(__); @@ -123,6 +185,51 @@ TEST_neg(0_0_); TEST_neg(_0_0); TEST_neg(0_0_0); +// moved from test_format_utils.cpp +TEST(validate_parse_amount_case, validate_parse_amount) +{ + uint64_t res = 0; + bool r = currency::parse_amount("0.0001", res); + ASSERT_TRUE(r); + ASSERT_EQ(res, 100000000); + + r = currency::parse_amount("100.0001", res); + ASSERT_TRUE(r); + ASSERT_EQ(res, 100000100000000); + + r = currency::parse_amount("000.0000", res); + ASSERT_TRUE(r); + ASSERT_EQ(res, 0); + + r = currency::parse_amount("0", res); + ASSERT_TRUE(r); + ASSERT_EQ(res, 0); + + + r = currency::parse_amount(" 100.0001 ", res); + ASSERT_TRUE(r); + ASSERT_EQ(res, 100000100000000); + + r = currency::parse_amount(" 100.0000 ", res); + ASSERT_TRUE(r); + ASSERT_EQ(res, 100000000000000); + + r = currency::parse_amount(" 100. 0000 ", res); + ASSERT_FALSE(r); + + r = currency::parse_amount("100. 0000", res); + ASSERT_FALSE(r); + + r = currency::parse_amount("100 . 0000", res); + ASSERT_FALSE(r); + + r = currency::parse_amount("100.00 00", res); + ASSERT_FALSE(r); + + r = currency::parse_amount("1 00.00 00", res); + ASSERT_FALSE(r); +} + //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// diff --git a/tests/unit_tests/test_format_utils.cpp b/tests/unit_tests/test_format_utils.cpp index 0e277222..f12c149f 100644 --- a/tests/unit_tests/test_format_utils.cpp +++ b/tests/unit_tests/test_format_utils.cpp @@ -125,48 +125,3 @@ void force_random(forced_to_pod_t& o) // } // // } - - -TEST(validate_parse_amount_case, validate_parse_amount) -{ - uint64_t res = 0; - bool r = currency::parse_amount(res, "0.0001"); - ASSERT_TRUE(r); - ASSERT_EQ(res, 100000000); - - r = currency::parse_amount(res, "100.0001"); - ASSERT_TRUE(r); - ASSERT_EQ(res, 100000100000000); - - r = currency::parse_amount(res, "000.0000"); - ASSERT_TRUE(r); - ASSERT_EQ(res, 0); - - r = currency::parse_amount(res, "0"); - ASSERT_TRUE(r); - ASSERT_EQ(res, 0); - - - r = currency::parse_amount(res, " 100.0001 "); - ASSERT_TRUE(r); - ASSERT_EQ(res, 100000100000000); - - r = currency::parse_amount(res, " 100.0000 "); - ASSERT_TRUE(r); - ASSERT_EQ(res, 100000000000000); - - r = currency::parse_amount(res, " 100. 0000 "); - ASSERT_FALSE(r); - - r = currency::parse_amount(res, "100. 0000"); - ASSERT_FALSE(r); - - r = currency::parse_amount(res, "100 . 0000"); - ASSERT_FALSE(r); - - r = currency::parse_amount(res, "100.00 00"); - ASSERT_FALSE(r); - - r = currency::parse_amount(res, "1 00.00 00"); - ASSERT_FALSE(r); -}