Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support sending decimals from QT wallet #1843

Merged
merged 12 commits into from
Mar 26, 2019
33 changes: 33 additions & 0 deletions nano/core_test/uint256_union.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,39 @@ TEST (uint128_union, balance_format)
ASSERT_EQ ("12-3456-789+123", nano::amount (nano::Mxrb_ratio * 123456789 + nano::kxrb_ratio * 123).format_balance (nano::Mxrb_ratio, 4, true, std::locale (std::cout.getloc (), new test_punct)));
}

TEST (uint128_union, decode_decimal)
{
nano::amount amount;
ASSERT_FALSE (amount.decode_dec ("340282366920938463463374607431768211455", nano::raw_ratio));
ASSERT_EQ (std::numeric_limits<nano::uint128_t>::max (), amount.number ());
ASSERT_TRUE (amount.decode_dec ("340282366920938463463374607431768211456", nano::raw_ratio));
ASSERT_TRUE (amount.decode_dec ("340282366920938463463374607431768211455.1", nano::raw_ratio));
ASSERT_TRUE (amount.decode_dec ("0.1", nano::raw_ratio));
ASSERT_FALSE (amount.decode_dec ("1", nano::raw_ratio));
ASSERT_EQ (1, amount.number ());
ASSERT_FALSE (amount.decode_dec ("340282366.920938463463374607431768211454", nano::Mxrb_ratio));
ASSERT_EQ (std::numeric_limits<nano::uint128_t>::max () - 1, amount.number ());
ASSERT_TRUE (amount.decode_dec ("340282366.920938463463374607431768211456", nano::Mxrb_ratio));
ASSERT_TRUE (amount.decode_dec ("340282367", nano::Mxrb_ratio));
ASSERT_FALSE (amount.decode_dec ("0.000000000000000000000001", nano::Mxrb_ratio));
ASSERT_EQ (1000000, amount.number ());
ASSERT_FALSE (amount.decode_dec ("0.000000000000000000000000000001", nano::Mxrb_ratio));
ASSERT_EQ (1, amount.number ());
ASSERT_TRUE (amount.decode_dec ("0.0000000000000000000000000000001", nano::Mxrb_ratio));
ASSERT_TRUE (amount.decode_dec (".1", nano::Mxrb_ratio));
ASSERT_TRUE (amount.decode_dec ("0.", nano::Mxrb_ratio));
ASSERT_FALSE (amount.decode_dec ("9.999999999999999999999999999999", nano::Mxrb_ratio));
ASSERT_EQ (nano::uint128_t ("9999999999999999999999999999999"), amount.number ());
ASSERT_FALSE (amount.decode_dec ("170141183460469.231731687303715884105727", nano::xrb_ratio));
ASSERT_EQ (nano::uint128_t ("170141183460469231731687303715884105727"), amount.number ());
ASSERT_FALSE (amount.decode_dec ("2.000000000000000000000002", nano::xrb_ratio));
ASSERT_EQ (2 * nano::xrb_ratio + 2, amount.number ());
ASSERT_FALSE (amount.decode_dec ("2", nano::xrb_ratio));
ASSERT_EQ (2 * nano::xrb_ratio, amount.number ());
ASSERT_FALSE (amount.decode_dec ("1230", nano::Gxrb_ratio));
ASSERT_EQ (1230 * nano::Gxrb_ratio, amount.number ());
}

TEST (unions, identity)
{
ASSERT_EQ (1, nano::uint128_union (1).number ().convert_to<uint8_t> ());
Expand Down
76 changes: 74 additions & 2 deletions nano/lib/numbers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -538,9 +538,9 @@ void nano::uint128_union::encode_dec (std::string & text) const
text = stream.str ();
}

bool nano::uint128_union::decode_dec (std::string const & text)
bool nano::uint128_union::decode_dec (std::string const & text, bool decimal)
{
auto error (text.size () > 39 || (text.size () > 1 && text.front () == '0') || (!text.empty () && text.front () == '-'));
auto error (text.size () > 39 || (text.size () > 1 && text.front () == '0' && !decimal) || (!text.empty () && text.front () == '-'));
if (!error)
{
std::stringstream stream (text);
Expand All @@ -564,6 +564,78 @@ bool nano::uint128_union::decode_dec (std::string const & text)
return error;
}

bool nano::uint128_union::decode_dec (std::string const & text, nano::uint128_t scale)
{
bool error (text.size () > 40 || (!text.empty () && text.front () == '-'));
if (!error)
{
auto delimiter_position (text.find (".")); // Dot delimiter hardcoded until decision for supporting other locales
if (delimiter_position == std::string::npos)
{
nano::uint128_union integer;
error = integer.decode_dec (text);
if (!error)
{
// Overflow check
try
{
auto result (boost::multiprecision::checked_uint128_t (integer.number ()) * boost::multiprecision::checked_uint128_t (scale));
error = (result > std::numeric_limits<nano::uint128_t>::max ());
if (!error)
{
*this = nano::uint128_t (result);
}
}
catch (std::overflow_error &)
{
error = true;
}
}
}
else
{
nano::uint128_union integer_part;
std::string integer_text (text.substr (0, delimiter_position));
error = (integer_text.empty () || integer_part.decode_dec (integer_text));
if (!error)
{
// Overflow check
try
{
error = ((boost::multiprecision::checked_uint128_t (integer_part.number ()) * boost::multiprecision::checked_uint128_t (scale)) > std::numeric_limits<nano::uint128_t>::max ());
}
catch (std::overflow_error &)
{
error = true;
}
if (!error)
{
nano::uint128_union decimal_part;
std::string decimal_text (text.substr (delimiter_position + 1, text.length ()));
error = (decimal_text.empty () || decimal_part.decode_dec (decimal_text, true));
if (!error)
{
// Overflow check
auto scale_length (scale.convert_to<std::string> ().length ());
error = (scale_length <= decimal_text.length ());
if (!error)
{
auto result (integer_part.number () * scale + decimal_part.number () * boost::multiprecision::pow (boost::multiprecision::cpp_int (10), (scale_length - decimal_text.length () - 1)));
// Overflow check
error = (result > std::numeric_limits<nano::uint128_t>::max ());
if (!error)
{
*this = nano::uint128_t (result);
}
}
}
}
}
}
}
return error;
}

void format_frac (std::ostringstream & stream, nano::uint128_t value, nano::uint128_t scale, int precision)
{
auto reduce = scale;
Expand Down
3 changes: 2 additions & 1 deletion nano/lib/numbers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ union uint128_union
void encode_hex (std::string &) const;
bool decode_hex (std::string const &);
void encode_dec (std::string &) const;
bool decode_dec (std::string const &);
bool decode_dec (std::string const &, bool = false);
bool decode_dec (std::string const &, nano::uint128_t);
std::string format_balance (nano::uint128_t scale, int precision, bool group_digits);
std::string format_balance (nano::uint128_t scale, int precision, bool group_digits, const std::locale & locale);
nano::uint128_t number () const;
Expand Down
111 changes: 43 additions & 68 deletions nano/qt/qt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1133,75 +1133,54 @@ void nano_qt::wallet::start ()
show_line_ok (*this_l->send_count);
show_line_ok (*this_l->send_account);
nano::amount amount;
if (!amount.decode_dec (this_l->send_count->text ().toStdString ()))
if (!amount.decode_dec (this_l->send_count->text ().toStdString (), this_l->rendering_ratio))
{
nano::uint128_t actual (amount.number () * this_l->rendering_ratio);
if (actual / this_l->rendering_ratio == amount.number ())
nano::uint128_t actual (amount.number ());
QString account_text (this_l->send_account->text ());
std::string account_text_narrow (account_text.toLocal8Bit ());
nano::account account_l;
auto parse_error (account_l.decode_account (account_text_narrow));
if (!parse_error)
{
QString account_text (this_l->send_account->text ());
std::string account_text_narrow (account_text.toLocal8Bit ());
nano::account account_l;
auto parse_error (account_l.decode_account (account_text_narrow));
if (!parse_error)
auto balance (this_l->node.balance (this_l->account));
if (actual <= balance)
{
auto balance (this_l->node.balance (this_l->account));
if (actual <= balance)
auto transaction (this_l->wallet_m->wallets.tx_begin_read ());
if (this_l->wallet_m->store.valid_password (transaction))
{
auto transaction (this_l->wallet_m->wallets.tx_begin_read ());
if (this_l->wallet_m->store.valid_password (transaction))
{
this_l->send_blocks_send->setEnabled (false);
this_l->node.background ([this_w, account_l, actual]() {
if (auto this_l = this_w.lock ())
{
this_l->wallet_m->send_async (this_l->account, account_l, actual, [this_w](std::shared_ptr<nano::block> block_a) {
if (auto this_l = this_w.lock ())
{
auto succeeded (block_a != nullptr);
this_l->application.postEvent (&this_l->processor, new eventloop_event ([this_w, succeeded]() {
if (auto this_l = this_w.lock ())
this_l->send_blocks_send->setEnabled (false);
this_l->node.background ([this_w, account_l, actual]() {
if (auto this_l = this_w.lock ())
{
this_l->wallet_m->send_async (this_l->account, account_l, actual, [this_w](std::shared_ptr<nano::block> block_a) {
if (auto this_l = this_w.lock ())
{
auto succeeded (block_a != nullptr);
this_l->application.postEvent (&this_l->processor, new eventloop_event ([this_w, succeeded]() {
if (auto this_l = this_w.lock ())
{
this_l->send_blocks_send->setEnabled (true);
if (succeeded)
{
this_l->send_blocks_send->setEnabled (true);
if (succeeded)
{
this_l->send_count->clear ();
this_l->send_account->clear ();
this_l->accounts.refresh ();
}
else
{
show_line_error (*this_l->send_count);
}
this_l->send_count->clear ();
this_l->send_account->clear ();
this_l->accounts.refresh ();
}
}));
}
});
}
});
}
else
{
show_button_error (*this_l->send_blocks_send);
this_l->send_blocks_send->setText ("Wallet is locked, unlock it to send");
this_l->node.alarm.add (std::chrono::steady_clock::now () + std::chrono::seconds (5), [this_w]() {
if (auto this_l = this_w.lock ())
{
this_l->application.postEvent (&this_l->processor, new eventloop_event ([this_w]() {
if (auto this_l = this_w.lock ())
{
show_button_ok (*this_l->send_blocks_send);
this_l->send_blocks_send->setText ("Send");
}
}));
}
});
}
else
{
show_line_error (*this_l->send_count);
}
}
}));
}
});
}
});
}
else
{
show_line_error (*this_l->send_count);
show_button_error (*this_l->send_blocks_send);
this_l->send_blocks_send->setText ("Not enough balance");
this_l->send_blocks_send->setText ("Wallet is locked, unlock it to send");
this_l->node.alarm.add (std::chrono::steady_clock::now () + std::chrono::seconds (5), [this_w]() {
if (auto this_l = this_w.lock ())
{
Expand All @@ -1218,9 +1197,9 @@ void nano_qt::wallet::start ()
}
else
{
show_line_error (*this_l->send_account);
show_line_error (*this_l->send_count);
show_button_error (*this_l->send_blocks_send);
this_l->send_blocks_send->setText ("Bad destination account");
this_l->send_blocks_send->setText ("Not enough balance");
this_l->node.alarm.add (std::chrono::steady_clock::now () + std::chrono::seconds (5), [this_w]() {
if (auto this_l = this_w.lock ())
{
Expand All @@ -1237,19 +1216,15 @@ void nano_qt::wallet::start ()
}
else
{
show_line_error (*this_l->send_count);
show_line_error (*this_l->send_account);
show_button_error (*this_l->send_blocks_send);
this_l->send_blocks_send->setText ("Amount too big");
this_l->send_blocks_send->setText ("Bad destination account");
this_l->node.alarm.add (std::chrono::steady_clock::now () + std::chrono::seconds (5), [this_w]() {
if (auto this_l = this_w.lock ())
{
show_line_ok (*this_l->send_account);
show_button_ok (*this_l->send_blocks_send);
this_l->send_blocks_send->setText ("Send");
this_l->application.postEvent (&this_l->processor, new eventloop_event ([this_w]() {
if (auto this_l = this_w.lock ())
{
show_line_ok (*this_l->send_account);
show_button_ok (*this_l->send_blocks_send);
this_l->send_blocks_send->setText ("Send");
}
Expand Down Expand Up @@ -1457,7 +1432,7 @@ void nano_qt::wallet::change_rendering_ratio (nano::uint128_t const & rendering_

std::string nano_qt::wallet::format_balance (nano::uint128_t const & balance) const
{
auto balance_str = nano::amount (balance).format_balance (rendering_ratio, 0, false);
auto balance_str = nano::amount (balance).format_balance (rendering_ratio, 3, false);
auto unit = std::string ("NANO");
if (rendering_ratio == nano::kxrb_ratio)
{
Expand Down
5 changes: 3 additions & 2 deletions nano/qt_test/qt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,17 +265,18 @@ TEST (wallet, send)
auto account (nano::test_genesis_key.pub);
auto wallet (std::make_shared<nano_qt::wallet> (*test_application, processor, *system.nodes[0], system.wallet (0), account));
wallet->start ();
ASSERT_NE (wallet->rendering_ratio, nano::raw_ratio);
QTest::mouseClick (wallet->send_blocks, Qt::LeftButton);
QTest::keyClicks (wallet->send_account, key1.to_account ().c_str ());
QTest::keyClicks (wallet->send_count, "2");
QTest::keyClicks (wallet->send_count, "2.03");
QTest::mouseClick (wallet->send_blocks_send, Qt::LeftButton);
system.deadline_set (10s);
while (wallet->node.balance (key1).is_zero ())
{
ASSERT_NO_ERROR (system.poll ());
}
nano::uint128_t amount (wallet->node.balance (key1));
ASSERT_EQ (2 * wallet->rendering_ratio, amount);
ASSERT_EQ (2 * wallet->rendering_ratio + (3 * wallet->rendering_ratio / 100), amount);
QTest::mouseClick (wallet->send_blocks_back, Qt::LeftButton);
QTest::mouseClick (wallet->show_advanced, Qt::LeftButton);
QTest::mouseClick (wallet->advanced.show_ledger, Qt::LeftButton);
Expand Down