From d3ac57d0fc4b1cde688c63b6381ff5b4d4b9f616 Mon Sep 17 00:00:00 2001 From: Guilherme Lawless Date: Tue, 5 Jan 2021 19:17:15 +0000 Subject: [PATCH] Fix qt wallet hitting a debug_assert when creating blocks (#3018) This changes the block creation path of the Qt wallet to use the appropriate difficulty for work generation. By doing so, it fixes the assert reported in #3017. The normal sending process (clicking Send in the wallet) and receive process (automatically) use the node wallet and thus the appropriate difficulty. A test is added to make some epoch_2 validation where difficulty is different across block types. There is a similar test for the node internal wallet. --- nano/qt/qt.cpp | 31 ++++++++++++++---- nano/qt_test/qt.cpp | 78 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 6 deletions(-) diff --git a/nano/qt/qt.cpp b/nano/qt/qt.cpp index 97d1972b..a7f31ac7 100644 --- a/nano/qt/qt.cpp +++ b/nano/qt/qt.cpp @@ -2265,7 +2265,11 @@ void nano_qt::block_creation::create_send () (void)error; debug_assert (!error); nano::state_block send (account_l, info.head, info.representative, balance - amount_l.number (), destination_l, key, account_l, 0); - if (wallet.node.work_generate_blocking (send).is_initialized ()) + nano::block_details details; + details.is_send = true; + details.epoch = info.epoch (); + auto const required_difficulty{ nano::work_threshold (send.work_version (), details) }; + if (wallet.node.work_generate_blocking (send, required_difficulty).is_initialized ()) { std::string block_l; send.serialize_json (block_l); @@ -2275,6 +2279,7 @@ void nano_qt::block_creation::create_send () } else { + debug_assert (required_difficulty <= wallet.node.max_work_generate_difficulty (send.work_version ())); show_label_error (*status); if (wallet.node.work_generation_enabled ()) { @@ -2328,7 +2333,7 @@ void nano_qt::block_creation::create_receive () auto block_l (wallet.node.store.block_get (block_transaction, source_l)); if (block_l != nullptr) { - auto destination (wallet.node.ledger.block_destination (block_transaction, *block_l)); + auto const & destination (wallet.node.ledger.block_destination (block_transaction, *block_l)); if (!destination.is_zero ()) { nano::pending_key pending_key (destination, source_l); @@ -2344,7 +2349,11 @@ void nano_qt::block_creation::create_receive () if (!error) { nano::state_block receive (pending_key.account, info.head, info.representative, info.balance.number () + pending.amount.number (), source_l, key, pending_key.account, 0); - if (wallet.node.work_generate_blocking (receive).is_initialized ()) + nano::block_details details; + details.is_receive = true; + details.epoch = std::max (info.epoch (), pending.epoch); + auto required_difficulty{ nano::work_threshold (receive.work_version (), details) }; + if (wallet.node.work_generate_blocking (receive, required_difficulty).is_initialized ()) { std::string block_l; receive.serialize_json (block_l); @@ -2354,6 +2363,7 @@ void nano_qt::block_creation::create_receive () } else { + debug_assert (required_difficulty <= wallet.node.max_work_generate_difficulty (receive.work_version ())); show_label_error (*status); if (wallet.node.work_generation_enabled ()) { @@ -2423,7 +2433,10 @@ void nano_qt::block_creation::create_change () if (!error) { nano::state_block change (account_l, info.head, representative_l, info.balance, 0, key, account_l, 0); - if (wallet.node.work_generate_blocking (change).is_initialized ()) + nano::block_details details; + details.epoch = info.epoch (); + auto const required_difficulty{ nano::work_threshold (change.work_version (), details) }; + if (wallet.node.work_generate_blocking (change, required_difficulty).is_initialized ()) { std::string block_l; change.serialize_json (block_l); @@ -2433,6 +2446,7 @@ void nano_qt::block_creation::create_change () } else { + debug_assert (required_difficulty <= wallet.node.max_work_generate_difficulty (change.work_version ())); show_label_error (*status); if (wallet.node.work_generation_enabled ()) { @@ -2484,7 +2498,7 @@ void nano_qt::block_creation::create_open () auto block_l (wallet.node.store.block_get (block_transaction, source_l)); if (block_l != nullptr) { - auto destination (wallet.node.ledger.block_destination (block_transaction, *block_l)); + auto const & destination (wallet.node.ledger.block_destination (block_transaction, *block_l)); if (!destination.is_zero ()) { nano::pending_key pending_key (destination, source_l); @@ -2500,7 +2514,11 @@ void nano_qt::block_creation::create_open () if (!error) { nano::state_block open (pending_key.account, 0, representative_l, pending.amount, source_l, key, pending_key.account, 0); - if (wallet.node.work_generate_blocking (open).is_initialized ()) + nano::block_details details; + details.is_receive = true; + details.epoch = pending.epoch; + auto const required_difficulty{ nano::work_threshold (open.work_version (), details) }; + if (wallet.node.work_generate_blocking (open, required_difficulty).is_initialized ()) { std::string block_l; open.serialize_json (block_l); @@ -2510,6 +2528,7 @@ void nano_qt::block_creation::create_open () } else { + debug_assert (required_difficulty <= wallet.node.max_work_generate_difficulty (open.work_version ())); show_label_error (*status); if (wallet.node.work_generation_enabled ()) { diff --git a/nano/qt_test/qt.cpp b/nano/qt_test/qt.cpp index 1cdf9eb5..47e1ae4a 100644 --- a/nano/qt_test/qt.cpp +++ b/nano/qt_test/qt.cpp @@ -894,3 +894,81 @@ TEST (wallet, DISABLED_synchronizing) test_application->processEvents (); } } + +TEST (wallet, epoch_2_validation) +{ + nano_qt::eventloop_processor processor; + nano::system system (1); + auto & node = system.nodes[0]; + + // Upgrade the genesis account to epoch 2 + ASSERT_NE (nullptr, system.upgrade_genesis_epoch (*node, nano::epoch::epoch_1)); + ASSERT_NE (nullptr, system.upgrade_genesis_epoch (*node, nano::epoch::epoch_2)); + + system.wallet (0)->insert_adhoc (nano::dev_genesis_key.prv); + + auto account (nano::dev_genesis_key.pub); + auto wallet (std::make_shared (*test_application, processor, *node, system.wallet (0), account)); + wallet->start (); + wallet->client_window->show (); + + QTest::mouseClick (wallet->show_advanced, Qt::LeftButton); + QTest::mouseClick (wallet->advanced.create_block, Qt::LeftButton); + + auto create_and_process = [&]() -> nano::block_hash { + wallet->block_creation.create->click (); + std::string json (wallet->block_creation.block->toPlainText ().toStdString ()); + EXPECT_FALSE (json.empty ()); + boost::property_tree::ptree tree1; + std::stringstream istream (json); + boost::property_tree::read_json (istream, tree1); + bool error (false); + nano::state_block block (error, tree1); + EXPECT_FALSE (error); + EXPECT_EQ (nano::process_result::progress, node->process (block).code); + return block.hash (); + }; + + auto do_send = [&](nano::public_key const & destination) -> nano::block_hash { + wallet->block_creation.send->click (); + wallet->block_creation.account->setText (nano::dev_genesis_key.pub.to_account ().c_str ()); + wallet->block_creation.amount->setText ("1"); + wallet->block_creation.destination->setText (destination.to_account ().c_str ()); + return create_and_process (); + }; + + auto do_open = [&](nano::block_hash const & source, nano::public_key const & account) -> nano::block_hash { + wallet->block_creation.open->click (); + wallet->block_creation.source->setText (source.to_string ().c_str ()); + wallet->block_creation.representative->setText (account.to_account ().c_str ()); + return create_and_process (); + }; + + auto do_receive = [&](nano::block_hash const & source) -> nano::block_hash { + wallet->block_creation.receive->click (); + wallet->block_creation.source->setText (source.to_string ().c_str ()); + return create_and_process (); + }; + + auto do_change = [&](nano::public_key const & account, nano::public_key const & representative) -> nano::block_hash { + wallet->block_creation.change->click (); + wallet->block_creation.account->setText (account.to_account ().c_str ()); + wallet->block_creation.representative->setText (representative.to_account ().c_str ()); + return create_and_process (); + }; + + // An epoch 2 receive (open) block should be generated with lower difficulty with high probability + auto tries = 0; + auto max_tries = 20; + + while (++tries < max_tries) + { + nano::keypair key; + system.wallet (0)->insert_adhoc (key.prv); + auto send1 = do_send (key.pub); + do_open (send1, key.pub); + auto send2 = do_send (key.pub); + do_receive (send2); + do_change (key.pub, nano::dev_genesis_key.pub); + } +}