diff --git a/nano/qt/qt.cpp b/nano/qt/qt.cpp index 84a215c451..8665f79927 100644 --- a/nano/qt/qt.cpp +++ b/nano/qt/qt.cpp @@ -530,6 +530,7 @@ class short_text_visitor : public nano::block_visitor if (!amount_l) { type = "Send (pruned)"; + amount = 0; } else { @@ -541,13 +542,21 @@ class short_text_visitor : public nano::block_visitor type = "Receive"; auto account_l = ledger.any.block_account (transaction, block_a.hashables.source); auto amount_l = ledger.any.block_amount (transaction, block_a.hash ()); - if (!account_l || !amount_l) + if (!account_l) { - type = "Receive (pruned)"; + type = "Receive (pruned source)"; } else { account = account_l.value (); + } + if (!amount_l) + { + type = "Receive (pruned)"; + amount = 0; + } + else + { amount = amount_l.value ().number (); } } @@ -558,15 +567,16 @@ class short_text_visitor : public nano::block_visitor { auto account_l = ledger.any.block_account (transaction, block_a.hashables.source); auto amount_l = ledger.any.block_amount (transaction, block_a.hash ()); - if (!account_l || !amount_l) + if (!account_l) { - type = "Receive (pruned)"; + type = "Receive (pruned source)"; } else { account = account_l.value (); - amount = amount_l.value ().number (); } + debug_assert (amount_l); + amount = amount_l.value ().number (); } else { @@ -584,44 +594,78 @@ class short_text_visitor : public nano::block_visitor { auto balance (block_a.hashables.balance.number ()); auto previous_balance = ledger.any.block_balance (transaction, block_a.hashables.previous); - if (!previous_balance) + // Error to receive previous block balance means that previous block was pruned from the ledger + if ((!previous_balance || balance < previous_balance.value ().number ()) && block_a.sideband ().details.is_send) + { + type = "Send"; + account = block_a.hashables.link.as_account (); + if (!previous_balance) + { + type = "Send (pruned)"; + amount = 0; + } + else + { + amount = previous_balance.value ().number () - balance; + } + } + else if (block_a.hashables.link.is_zero () && !block_a.sideband ().details.is_send) { - type = "Unknown (pruned)"; + debug_assert (!block_a.sideband ().details.is_receive && !block_a.sideband ().details.is_epoch); + type = "Change"; + account = block_a.hashables.representative; amount = 0; - account = block_a.hashables.account; + if (!previous_balance) + { + type = "Change (pruned)"; + } + else + { + debug_assert (balance == previous_balance); + } } - else if (balance < previous_balance.value ().number ()) + else if (ledger.is_epoch_link (block_a.hashables.link) && block_a.sideband ().details.is_epoch) { - type = "Send"; - amount = previous_balance.value ().number () - balance; - account = block_a.hashables.link.as_account (); + debug_assert (!previous_balance || balance == previous_balance); + type = "Epoch"; + amount = 0; + if (!previous_balance && !block_a.hashables.previous.is_zero ()) + { + // Epoch block with previous balance error is pruned only if it isn't open block for an account + type = "Epoch (pruned)"; + } + account = ledger.epoch_signer (block_a.hashables.link); } else { - if (block_a.hashables.link.is_zero ()) + debug_assert (block_a.sideband ().details.is_receive); + type = "Receive"; + auto account_l = ledger.any.block_account (transaction, block_a.hashables.link.as_block_hash ()); + if (!account_l) { - type = "Change"; - account = block_a.hashables.representative; + type = "Receive (pruned source)"; } - else if (balance == previous_balance && ledger.is_epoch_link (block_a.hashables.link)) + else { - type = "Epoch"; - account = ledger.epoch_signer (block_a.hashables.link); + account = account_l.value (); } - else + if (!previous_balance) { - type = "Receive"; - auto account_l = ledger.any.block_account (transaction, block_a.hashables.link.as_block_hash ()); - if (!account_l) + if (!block_a.hashables.previous.is_zero ()) { + // Receive block with previous balance error is pruned only if it isn't open block for an account type = "Receive (pruned)"; + amount = 0; } else { - account = account_l.value (); + amount = balance; } } - amount = balance - previous_balance.value ().number (); + else + { + amount = balance - previous_balance.value ().number (); + } } } nano::secure::transaction const & transaction; diff --git a/nano/qt_test/qt.cpp b/nano/qt_test/qt.cpp index dd97b99733..1d09b81ae3 100644 --- a/nano/qt_test/qt.cpp +++ b/nano/qt_test/qt.cpp @@ -573,48 +573,175 @@ TEST (history, pruned_source) ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send1)); auto send2 = std::make_shared (send1->hash (), key.pub, nano::dev::constants.genesis_amount - 200, nano::dev::genesis_key.prv, nano::dev::genesis_key.pub, *system.work.generate (send1->hash ())); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send2)); - auto receive = std::make_shared (send2->hash (), send1->hash (), nano::dev::genesis_key.prv, nano::dev::genesis_key.pub, *system.work.generate (send2->hash ())); - ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive)); + auto receive1 = std::make_shared (send2->hash (), send1->hash (), nano::dev::genesis_key.prv, nano::dev::genesis_key.pub, *system.work.generate (send2->hash ())); + ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive1)); auto open = std::make_shared (send2->hash (), key.pub, key.pub, key.prv, key.pub, *system.work.generate (key.pub)); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, open)); ledger.confirm (transaction, send1->hash ()); ASSERT_EQ (1, ledger.pruning_action (transaction, send1->hash (), 2)); next_pruning = send2->hash (); } + // Set rendering ration to raw units + ASSERT_NE (wallet->rendering_ratio, nano::raw_ratio); + wallet->rendering_ratio = nano::raw_ratio; + // Genesis account pruned values nano_qt::history history1 (ledger, nano::dev::genesis_key.pub, *wallet); history1.refresh (); ASSERT_EQ (2, history1.model->rowCount ()); + // Source block send1 is removed + auto type1 (history1.model->item (0, 0)); + ASSERT_EQ ("Receive (pruned source)", type1->text ().toStdString ()); + // Source block account is unknown + auto account1 (history1.model->item (0, 1)); + ASSERT_EQ (nano::account (0).to_account (), account1->text ().toStdString ()); + // Amount is known because previous block (send2) isn't removed + auto amount1 (history1.model->item (0, 2)); + ASSERT_EQ ("100 raw", amount1->text ().toStdString ()); + // Last not pruned block in chain (send2) + auto type2 (history1.model->item (1, 0)); + ASSERT_EQ ("Send (pruned)", type2->text ().toStdString ()); + auto account2 (history1.model->item (1, 1)); + // Amount is unknown because previous block (send1) is removed + ASSERT_EQ (key.pub.to_account (), account2->text ().toStdString ()); + auto amount2 (history1.model->item (1, 2)); + ASSERT_EQ ("0 raw", amount2->text ().toStdString ()); + // New account pruned values nano_qt::history history2 (ledger, key.pub, *wallet); history2.refresh (); ASSERT_EQ (1, history2.model->rowCount ()); + auto type3 (history2.model->item (0, 0)); + ASSERT_EQ ("Receive", type3->text ().toStdString ()); + // Source block (send2) account is known + auto account3 (history2.model->item (0, 1)); + ASSERT_EQ (nano::dev::genesis_key.pub.to_account (), account3->text ().toStdString ()); + auto amount3 (history2.model->item (0, 2)); + ASSERT_EQ ("100 raw", amount3->text ().toStdString ()); // Additional legacy test { auto transaction = ledger.tx_begin_write (); ledger.confirm (transaction, next_pruning); ASSERT_EQ (1, ledger.pruning_action (transaction, next_pruning, 2)); } + // Genesis account pruned values history1.refresh (); ASSERT_EQ (1, history1.model->rowCount ()); + auto type4 (history1.model->item (0, 0)); + ASSERT_EQ ("Receive (pruned)", type4->text ().toStdString ()); + auto account4 (history1.model->item (0, 1)); + ASSERT_EQ (nano::account (0).to_account (), account4->text ().toStdString ()); + // Amount is unknown because previous block (send2) is removed + auto amount4 (history1.model->item (0, 2)); + ASSERT_EQ ("0 raw", amount4->text ().toStdString ()); + // New account pruned values history2.refresh (); ASSERT_EQ (1, history2.model->rowCount ()); + auto type5 (history2.model->item (0, 0)); + ASSERT_EQ ("Receive (pruned source)", type5->text ().toStdString ()); + // Source block (send2) account is unknown + auto account5 (history2.model->item (0, 1)); + ASSERT_EQ (nano::account (0).to_account (), account5->text ().toStdString ()); + auto amount5 (history2.model->item (0, 2)); + ASSERT_EQ ("100 raw", amount5->text ().toStdString ()); // Pruning for state blocks. Previous block is pruned, source is pruned { auto transaction = ledger.tx_begin_write (); auto latest (ledger.any.account_head (transaction, nano::dev::genesis_key.pub)); - auto send = std::make_shared (nano::dev::genesis_key.pub, latest, nano::dev::genesis_key.pub, nano::dev::constants.genesis_amount - 200, key.pub, nano::dev::genesis_key.prv, nano::dev::genesis_key.pub, *system.work.generate (latest)); - ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send)); + auto send3 = std::make_shared (nano::dev::genesis_key.pub, latest, nano::dev::genesis_key.pub, nano::dev::constants.genesis_amount - 200, key.pub, nano::dev::genesis_key.prv, nano::dev::genesis_key.pub, *system.work.generate (latest)); + ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send3)); auto latest_key (ledger.any.account_head (transaction, key.pub)); - auto receive = std::make_shared (key.pub, latest_key, key.pub, 200, send->hash (), key.prv, key.pub, *system.work.generate (latest_key)); - ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive)); + auto receive2 = std::make_shared (key.pub, latest_key, key.pub, 200, send3->hash (), key.prv, key.pub, *system.work.generate (latest_key)); + ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive2)); ledger.confirm (transaction, latest); ASSERT_EQ (1, ledger.pruning_action (transaction, latest, 2)); ledger.confirm (transaction, latest_key); ASSERT_EQ (1, ledger.pruning_action (transaction, latest_key, 2)); } + // Genesis account pruned values + history1.refresh (); + ASSERT_EQ (1, history1.model->rowCount ()); + auto type6 (history1.model->item (0, 0)); + ASSERT_EQ ("Send (pruned)", type6->text ().toStdString ()); + auto account6 (history1.model->item (0, 1)); + ASSERT_EQ (key.pub.to_account (), account6->text ().toStdString ()); + // Amount is unknown because previous block (receive1) is removed + auto amount6 (history1.model->item (0, 2)); + ASSERT_EQ ("0 raw", amount6->text ().toStdString ()); + // New account pruned values + history2.refresh (); + ASSERT_EQ (1, history2.model->rowCount ()); + auto type7 (history2.model->item (0, 0)); + ASSERT_EQ ("Receive (pruned)", type7->text ().toStdString ()); + // Source block (send3) account is known + auto account7 (history2.model->item (0, 1)); + ASSERT_EQ (nano::dev::genesis_key.pub.to_account (), account7->text ().toStdString ()); + // Amount is unknown because previous block (receive1) is removed + auto amount7 (history2.model->item (0, 2)); + ASSERT_EQ ("0 raw", amount7->text ().toStdString ()); + // Pruning for state blocks. Change state block + { + auto transaction = ledger.tx_begin_write (); + auto latest (ledger.any.account_head (transaction, nano::dev::genesis_key.pub)); + auto change = std::make_shared (nano::dev::genesis_key.pub, latest, key.pub, nano::dev::constants.genesis_amount - 200, 0, nano::dev::genesis_key.prv, nano::dev::genesis_key.pub, *system.work.generate (latest)); + ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, change)); + ledger.confirm (transaction, latest); + ASSERT_EQ (1, ledger.pruning_action (transaction, latest, 2)); + } + // Genesis account pruned values history1.refresh (); ASSERT_EQ (1, history1.model->rowCount ()); + auto type8 (history1.model->item (0, 0)); + ASSERT_EQ ("Change (pruned)", type8->text ().toStdString ()); + auto account8 (history1.model->item (0, 1)); + ASSERT_EQ (key.pub.to_account (), account8->text ().toStdString ()); + // Amount is 0 for change blocks + auto amount8 (history1.model->item (0, 2)); + ASSERT_EQ ("0 raw", amount8->text ().toStdString ()); + // New account pruned values + history2.refresh (); + ASSERT_EQ (1, history2.model->rowCount ()); + auto type9 (history2.model->item (0, 0)); + ASSERT_EQ ("Receive (pruned)", type9->text ().toStdString ()); + // Source block (send3) account is unknown + auto account9 (history2.model->item (0, 1)); + ASSERT_EQ (nano::account (0).to_account (), account9->text ().toStdString ()); + // Amount is unknown because previous block (receive1) is removed + auto amount9 (history2.model->item (0, 2)); + ASSERT_EQ ("0 raw", amount9->text ().toStdString ()); + // Pruning for state blocks. Open state block + nano::keypair key2; + system.wallet (0)->insert_adhoc (key2.prv); + { + auto transaction = ledger.tx_begin_write (); + auto latest_key (ledger.any.account_head (transaction, key.pub)); + auto send4 = std::make_shared (key.pub, latest_key, key.pub, 100, key2.pub, key.prv, key.pub, *system.work.generate (latest_key)); + ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send4)); + auto open2 = std::make_shared (key2.pub, 0, key2.pub, 100, send4->hash (), key2.prv, key2.pub, *system.work.generate (key2.pub)); + ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, open2)); + ledger.confirm (transaction, latest_key); + ASSERT_EQ (1, ledger.pruning_action (transaction, latest_key, 2)); + } + // New account (key) pruned values history2.refresh (); ASSERT_EQ (1, history2.model->rowCount ()); + auto type10 (history2.model->item (0, 0)); + ASSERT_EQ ("Send (pruned)", type10->text ().toStdString ()); + auto account10 (history2.model->item (0, 1)); + ASSERT_EQ (key2.pub.to_account (), account10->text ().toStdString ()); + // Amount is unknown because previous block (receive2) is removed + auto amount10 (history2.model->item (0, 2)); + ASSERT_EQ ("0 raw", amount10->text ().toStdString ()); + // Account (key2) pruned values + nano_qt::history history3 (ledger, key2.pub, *wallet); + history3.refresh (); + ASSERT_EQ (1, history3.model->rowCount ()); + // Type is "Recieve" for open block because source block (send4) isn't removed + auto type11 (history3.model->item (0, 0)); + ASSERT_EQ ("Receive", type11->text ().toStdString ()); + auto account11 (history3.model->item (0, 1)); + ASSERT_EQ (key.pub.to_account (), account11->text ().toStdString ()); + // Amount is known for open blocks + auto amount11 (history3.model->item (0, 2)); + ASSERT_EQ ("100 raw", amount11->text ().toStdString ()); } TEST (wallet, startup_work)