diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index 73340589..5ab4bf0e 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -2319,15 +2319,25 @@ bool check_ring_signature_at_gen_time(const std::vector& event bool check_mixin_value_for_each_input(size_t mixin, const crypto::hash& tx_id, currency::core& c) { - std::shared_ptr ptce = c.get_blockchain_storage().get_tx_chain_entry(tx_id); - if (!ptce) - return false; + transaction tx_local; + const transaction* ptx = &tx_local; - for (size_t i = 0; i < ptce->tx.vin.size(); ++i) + std::shared_ptr ptce = c.get_blockchain_storage().get_tx_chain_entry(tx_id); + if (ptce) { - auto& input = ptce->tx.vin[i]; + ptx = &ptce->tx; + } + else + { + if (!c.get_tx_pool().get_transaction(tx_id, tx_local)) + return false; + } + + for (size_t i = 0; i < ptx->vin.size(); ++i) + { + auto& input = ptx->vin[i]; const std::vector& key_offsets = get_key_offsets_from_txin_v(input); - CHECK_AND_ASSERT_MES(key_offsets.size() == mixin + 1, false, "for input #" << i << " mixin count is " << key_offsets.size() - 1 << ", expected is " << mixin); + CHECK_AND_ASSERT_MES(key_offsets.size() == mixin + 1, false, "for input #" << i << " ring size is " << key_offsets.size() << ", mixin count is " << key_offsets.size() - 1 << ", expected mixin count is " << mixin); } return true; diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 486237ed..01f5c9cc 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -1248,6 +1248,7 @@ int main(int argc, char* argv[]) // HF4 GENERATE_AND_PLAY_HF(hard_fork_4_consolidated_txs, "3-*"); GENERATE_AND_PLAY_HF(hardfork_4_wallet_transfer_with_mandatory_mixins, "3-*"); + GENERATE_AND_PLAY(hardfork_4_wallet_sweep_bare_outs); // atomics GENERATE_AND_PLAY(atomic_simple_test); diff --git a/tests/core_tests/hard_fork_4.cpp b/tests/core_tests/hard_fork_4.cpp index b8b9a3e5..2349167d 100644 --- a/tests/core_tests/hard_fork_4.cpp +++ b/tests/core_tests/hard_fork_4.cpp @@ -321,3 +321,121 @@ bool hardfork_4_wallet_transfer_with_mandatory_mixins::c1(currency::core& c, siz return true; } + +//------------------------------------------------------------------------------ + +hardfork_4_wallet_sweep_bare_outs::hardfork_4_wallet_sweep_bare_outs() +{ + REGISTER_CALLBACK_METHOD(hardfork_4_wallet_sweep_bare_outs, c1); + + m_hardforks.set_hardfork_height(ZANO_HARDFORK_04_ZARCANUM, 10); +} + +bool hardfork_4_wallet_sweep_bare_outs::generate(std::vector& events) const +{ + // Test idea: make sure wallet2::sweep_bare_outs works well even if there's not enough outputs to mix + + uint64_t ts = test_core_time::get_time(); + m_accounts.resize(TOTAL_ACCS_COUNT); + account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); miner_acc.set_createtime(ts); + account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); alice_acc.set_createtime(ts); + account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate(); bob_acc.set_createtime(ts); + + MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, ts); + + // rebuild genesis miner tx + std::vector destinations; + destinations.emplace_back(MK_TEST_COINS(23), alice_acc.get_public_address()); + destinations.emplace_back(MK_TEST_COINS(23), bob_acc.get_public_address()); + destinations.emplace_back(MK_TEST_COINS(55), bob_acc.get_public_address()); // this output is unique and doesn't have decoys + for (size_t i = 0; i < 10; ++i) + destinations.emplace_back(MK_TEST_COINS(23), miner_acc.get_public_address()); // decoys (later Alice will spend her output using mixins) + destinations.emplace_back(COIN, miner_acc.get_public_address()); // leftover amount will be also send to the last destination + CHECK_AND_ASSERT_MES(replace_coinbase_in_genesis_block(destinations, generator, events, blk_0), false, ""); + + DO_CALLBACK(events, "configure_core"); // default configure_core callback will initialize core runtime config with m_hardforks + REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1); + + DO_CALLBACK_PARAMS(events, "check_hardfork_active", static_cast(ZANO_HARDFORK_04_ZARCANUM)); + + DO_CALLBACK(events, "c1"); + + return true; +} + +bool hardfork_4_wallet_sweep_bare_outs::c1(currency::core& c, size_t ev_index, const std::vector& events) +{ + bool r = false; + std::shared_ptr alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX); + alice_wlt->refresh(); + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt.get(), "Alice", MK_TEST_COINS(23), UINT64_MAX, MK_TEST_COINS(23), 0, 0), false, ""); + + std::shared_ptr bob_wlt = init_playtime_test_wallet(events, c, BOB_ACC_IDX); + bob_wlt->refresh(); + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*bob_wlt.get(), "Bob", MK_TEST_COINS(23 + 55), UINT64_MAX, MK_TEST_COINS(23 + 55), 0, 0), false, ""); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + + // 1. Try to sweep bare out for Alice (enough decoys to mix with) + std::vector tids_grouped_by_txs; + CHECK_AND_ASSERT_MES(alice_wlt->get_bare_unspent_outputs_stats(tids_grouped_by_txs), false, ""); + size_t total_txs_sent = 0; + uint64_t total_amount_sent = 0; + uint64_t total_fee_spent = 0; + uint64_t total_bare_outs_sent = 0; + CHECK_AND_ASSERT_MES(alice_wlt->sweep_bare_unspent_outputs(m_accounts[ALICE_ACC_IDX].get_public_address(), tids_grouped_by_txs, total_txs_sent, total_amount_sent, total_fee_spent, total_bare_outs_sent), false, ""); + + CHECK_V_EQ_EXPECTED_AND_ASSERT(total_txs_sent, 1); + CHECK_V_EQ_EXPECTED_AND_ASSERT(total_amount_sent, MK_TEST_COINS(23)); + CHECK_V_EQ_EXPECTED_AND_ASSERT(total_fee_spent, TESTS_DEFAULT_FEE); + CHECK_V_EQ_EXPECTED_AND_ASSERT(total_bare_outs_sent, 1); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + + std::list txs; + c.get_pool_transactions(txs); + auto& tx = txs.back(); + CHECK_AND_ASSERT_MES(check_mixin_value_for_each_input(CURRENCY_DEFAULT_DECOY_SET_SIZE, get_transaction_hash(tx), c), false, ""); + + r = mine_next_pow_blocks_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed"); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + + alice_wlt->refresh(); + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt.get(), "Alice", MK_TEST_COINS(23) - TESTS_DEFAULT_FEE, UINT64_MAX, MK_TEST_COINS(23) - TESTS_DEFAULT_FEE, 0, 0), false, ""); + + + // 2. Try to sweep bare out for Bob (not enough decoys to mix with) + tids_grouped_by_txs.clear(); + CHECK_AND_ASSERT_MES(bob_wlt->get_bare_unspent_outputs_stats(tids_grouped_by_txs), false, ""); + CHECK_AND_ASSERT_MES(bob_wlt->sweep_bare_unspent_outputs(m_accounts[BOB_ACC_IDX].get_public_address(), tids_grouped_by_txs, total_txs_sent, total_amount_sent, total_fee_spent, total_bare_outs_sent), false, ""); + + CHECK_V_EQ_EXPECTED_AND_ASSERT(total_txs_sent, 1); + CHECK_V_EQ_EXPECTED_AND_ASSERT(total_amount_sent, MK_TEST_COINS(23 + 55)); + CHECK_V_EQ_EXPECTED_AND_ASSERT(total_fee_spent, TESTS_DEFAULT_FEE); + CHECK_V_EQ_EXPECTED_AND_ASSERT(total_bare_outs_sent, 2); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + + txs.clear(); + c.get_pool_transactions(txs); + auto& tx2 = txs.back(); + CHECK_V_EQ_EXPECTED_AND_ASSERT(tx2.vin.size(), 2); + + const txin_to_key &input_with_enough_decoys = boost::get(tx2.vin[0]).amount == MK_TEST_COINS(23) ? boost::get(tx2.vin[0]) : boost::get(tx2.vin[1]); + const txin_to_key &input_with_not_enough_decoys = boost::get(tx2.vin[0]).amount == MK_TEST_COINS(23) ? boost::get(tx2.vin[1]) : boost::get(tx2.vin[0]); + + CHECK_V_EQ_EXPECTED_AND_ASSERT(input_with_enough_decoys.key_offsets.size(), CURRENCY_DEFAULT_DECOY_SET_SIZE + 1); + CHECK_V_EQ_EXPECTED_AND_ASSERT(input_with_not_enough_decoys.key_offsets.size(), 1); + + r = mine_next_pow_blocks_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed"); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Unexpected number of txs in the pool: " << c.get_pool_transactions_count()); + + bob_wlt->refresh(); + CHECK_AND_ASSERT_MES(check_balance_via_wallet(*bob_wlt.get(), "Bob", MK_TEST_COINS(23 + 55) - TESTS_DEFAULT_FEE, UINT64_MAX, MK_TEST_COINS(23 + 55) - TESTS_DEFAULT_FEE, 0, 0), false, ""); + + return true; +} diff --git a/tests/core_tests/hard_fork_4.h b/tests/core_tests/hard_fork_4.h index 600a17c8..b3684f80 100644 --- a/tests/core_tests/hard_fork_4.h +++ b/tests/core_tests/hard_fork_4.h @@ -34,3 +34,11 @@ struct hardfork_4_wallet_transfer_with_mandatory_mixins : public wallet_test bool generate(std::vector& events) const; bool c1(currency::core& c, size_t ev_index, const std::vector& events); }; + + +struct hardfork_4_wallet_sweep_bare_outs : public wallet_test +{ + hardfork_4_wallet_sweep_bare_outs(); + bool generate(std::vector& events) const; + bool c1(currency::core& c, size_t ev_index, const std::vector& events); +};