From 71708ca673c7a4094c7a7f94b70edc4b8f775fa1 Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Sat, 20 Feb 2021 15:54:58 +0100 Subject: [PATCH] htlc: added test for validating before and after expiration conditions --- src/wallet/wallet2.cpp | 20 ++-- src/wallet/wallet2.h | 4 +- tests/core_tests/atomic_tests.cpp | 136 +++++++++++++++++++++++++++- tests/core_tests/atomic_tests.h | 11 ++- tests/core_tests/chaingen_helpers.h | 5 + tests/core_tests/chaingen_main.cpp | 1 + 6 files changed, 164 insertions(+), 13 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index e6ad07b2..bebc1c62 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1633,7 +1633,7 @@ void wallet2::refresh(std::atomic& stop) refresh(n, f, stop); } //---------------------------------------------------------------------------------------------------- -void wallet2::transfer(uint64_t amount, const currency::account_public_address& acc) +void wallet2::transfer(uint64_t amount, const currency::account_public_address& acc, currency::transaction& result_tx) { std::vector extra; std::vector attachments; @@ -1642,9 +1642,13 @@ void wallet2::transfer(uint64_t amount, const currency::account_public_address& dst.resize(1); dst.back().addr.push_back(acc); dst.back().amount = amount; - transaction result_tx = AUTO_VAL_INIT(result_tx); this->transfer(dst, 0, 0, TX_DEFAULT_FEE, extra, attachments, tools::detail::ssi_digit, tools::tx_dust_policy(DEFAULT_DUST_THRESHOLD), result_tx); - +} +//---------------------------------------------------------------------------------------------------- +void wallet2::transfer(uint64_t amount, const currency::account_public_address& acc) +{ + transaction result_tx = AUTO_VAL_INIT(result_tx); + this->transfer(amount, acc, result_tx); } //---------------------------------------------------------------------------------------------------- void wallet2::reset_creation_time(uint64_t timestamp) @@ -4054,7 +4058,13 @@ void wallet2::get_list_of_active_htlc(std::list& } } //---------------------------------------------------------------------------------------------------- -void wallet2::redeem_htlc(const crypto::hash& htlc_tx_id, std::string origin) +void wallet2::redeem_htlc(const crypto::hash& htlc_tx_id, const std::string& origin) +{ + currency::transaction result_tx = AUTO_VAL_INIT(result_tx); + return redeem_htlc(htlc_tx_id, origin, result_tx); +} +//---------------------------------------------------------------------------------------------------- +void wallet2::redeem_htlc(const crypto::hash& htlc_tx_id, const std::string& origin, currency::transaction& result_tx) { construct_tx_param ctp = get_default_construct_tx_param(); @@ -4069,8 +4079,6 @@ void wallet2::redeem_htlc(const crypto::hash& htlc_tx_id, std::string origin) "htlc not found with tx_id = " << htlc_tx_id, API_RETURN_CODE_NOT_FOUND); ctp.dsts.back().amount = m_transfers[it->second].amount() - ctp.fee; - - currency::transaction result_tx = AUTO_VAL_INIT(result_tx); this->transfer(ctp, result_tx, true, nullptr); } //---------------------------------------------------------------------------------------------------- diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 3d815d69..733bf554 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -549,6 +549,7 @@ namespace tools uint64_t unlocked_balance() const; void transfer(uint64_t amount, const currency::account_public_address& acc); + void transfer(uint64_t amount, const currency::account_public_address& acc, currency::transaction& result_tx); void transfer(const std::vector& dsts, size_t fake_outputs_count, @@ -858,7 +859,8 @@ namespace tools */ void create_htlc_proposal(uint64_t amount, const currency::account_public_address& addr, uint64_t lock_blocks_count, currency::transaction &tx, const crypto::hash& htlc_hash, std::string &origin); void get_list_of_active_htlc(std::list& htlcs, bool only_redeem_txs); - void redeem_htlc(const crypto::hash& htlc_tx_id, std::string origin); + void redeem_htlc(const crypto::hash& htlc_tx_id, const std::string& origin, currency::transaction& result_tx); + void redeem_htlc(const crypto::hash& htlc_tx_id, const std::string& origin); bool check_htlc_redeemed(const crypto::hash& htlc_tx_id, std::string& origin); private: diff --git a/tests/core_tests/atomic_tests.cpp b/tests/core_tests/atomic_tests.cpp index 3c29da03..3ffdc4fc 100644 --- a/tests/core_tests/atomic_tests.cpp +++ b/tests/core_tests/atomic_tests.cpp @@ -12,6 +12,13 @@ using namespace epee; using namespace crypto; using namespace currency; +#define INIT_RUNTIME_WALLET(instance_name) \ + currency::account_base instance_name##acc_base; \ + instance_name##acc_base.generate(); \ + LOG_PRINT_MAGENTA(": " << currency::get_account_address_as_str(instance_name##acc_base.get_public_address()), LOG_LEVEL_0); \ + std::shared_ptr instance_name = init_playtime_test_wallet(events, c, instance_name##acc_base); + + //============================================================================================================================== struct wallet_tests_callback_handler : public tools::i_wallet2_callback @@ -72,11 +79,6 @@ bool atomic_simple_test::c1(currency::core& c, size_t ev_index, const std::vecto LOG_PRINT_MAGENTA("Mining Address: " << currency::get_account_address_as_str(m_mining_accunt.get_public_address()), LOG_LEVEL_0); -#define INIT_RUNTIME_WALLET(instance_name) \ - currency::account_base instance_name##acc_base; \ - instance_name##acc_base.generate(); \ - LOG_PRINT_MAGENTA(": " << currency::get_account_address_as_str(instance_name##acc_base.get_public_address()), LOG_LEVEL_0); \ - std::shared_ptr instance_name = init_playtime_test_wallet(events, c, instance_name##acc_base); //create wallet instances and calculate balances INIT_RUNTIME_WALLET(alice_a_wlt_instance); @@ -278,4 +280,128 @@ bool atomic_simple_test::c1(currency::core& c, size_t ev_index, const std::vecto //------------------------------------------------------------------------------ +//============================================================================== +//============================================================================== +//============================================================================== +atomic_test_wrong_redeem_wrong_refund::atomic_test_wrong_redeem_wrong_refund() +{ + REGISTER_CALLBACK_METHOD(atomic_test_wrong_redeem_wrong_refund, c1); +} + +bool atomic_test_wrong_redeem_wrong_refund::generate(std::vector& events) const +{ + epee::debug::get_set_enable_assert(true, true); + + currency::account_base genesis_acc; + genesis_acc.generate(); + m_mining_accunt.generate(); + + + block blk_0 = AUTO_VAL_INIT(blk_0); + generator.construct_genesis_block(blk_0, genesis_acc, test_core_time::get_time()); + events.push_back(blk_0); + + REWIND_BLOCKS_N(events, blk_0r, blk_0, m_mining_accunt, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 5); + + DO_CALLBACK(events, "c1"); + epee::debug::get_set_enable_assert(true, false); + return true; +} + + + +bool atomic_test_wrong_redeem_wrong_refund::c1(currency::core& c, size_t ev_index, const std::vector& events) +{ + /* + we create two HTLC and then we make attempt to spend it: + 1. spend it as refund when it supposed to be redeem + 2. spend it as redeem when it supposed to be refund + + */ + + LOG_PRINT_MAGENTA("Mining Address: " << currency::get_account_address_as_str(m_mining_accunt.get_public_address()), LOG_LEVEL_0); + + //create wallet instances and calculate balances + INIT_RUNTIME_WALLET(alice_a_wlt_instance); + INIT_RUNTIME_WALLET(alice_b_wlt_instance); + INIT_RUNTIME_WALLET(alice_c_wlt_instance); + +#define AMOUNT_TO_TRANSFER_HTLC (TESTS_DEFAULT_FEE*10) + + std::shared_ptr miner_wlt = init_playtime_test_wallet(events, c, m_mining_accunt); + + size_t blocks_fetched = 0; + bool received_money = false; + std::atomic atomic_false = ATOMIC_VAR_INIT(false); + miner_wlt->refresh(blocks_fetched, received_money, atomic_false); + CHECK_AND_FORCE_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool"); + + + uint64_t transfer_amount = AMOUNT_TO_TRANSFER_HTLC + TESTS_DEFAULT_FEE; + miner_wlt->transfer(transfer_amount, alice_a_wlt_instance->get_account().get_public_address()); + LOG_PRINT_MAGENTA("Transaction sent to Alice A: " << transfer_amount, LOG_LEVEL_0); + + miner_wlt->transfer(transfer_amount, alice_b_wlt_instance->get_account().get_public_address()); + LOG_PRINT_MAGENTA("Transaction sent to Alice A: " << transfer_amount, LOG_LEVEL_0); + + + bool r = mine_next_pow_blocks_in_playtime(m_mining_accunt.get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed"); + + alice_a_wlt_instance->refresh(); + alice_b_wlt_instance->refresh(); + alice_c_wlt_instance->refresh(); + + std::string alice_origin; //will be deterministically generated by Alice's A wallet + currency::transaction res_tx = AUTO_VAL_INIT(res_tx); + alice_a_wlt_instance->create_htlc_proposal(transfer_amount - TESTS_DEFAULT_FEE, alice_b_wlt_instance->get_account().get_public_address(), 9, res_tx, currency::null_hash, alice_origin); + + std::string alice_origin_b; //will be deterministically generated by Alice's A wallet + currency::transaction res_tx_b = AUTO_VAL_INIT(res_tx_b); + alice_b_wlt_instance->create_htlc_proposal(transfer_amount - TESTS_DEFAULT_FEE, alice_c_wlt_instance->get_account().get_public_address(), 12, res_tx, currency::null_hash, alice_origin_b); + + + //forward blockchain to create redeem transaction + r = mine_next_pow_blocks_in_playtime(m_mining_accunt.get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed"); + + alice_a_wlt_instance->refresh(); + alice_b_wlt_instance->refresh(); + alice_c_wlt_instance->refresh(); + + CHECK_AND_FORCE_ASSERT_MES(alice_a_wlt_instance->balance() == transfer_amount - TESTS_DEFAULT_FEE, false, "Incorrect balance"); + + //create refund tx + currency::transaction refund_tx = AUTO_VAL_INIT(refund_tx); + alice_a_wlt_instance->transfer(transfer_amount - TESTS_DEFAULT_FEE*2, alice_b_wlt_instance->get_account().get_public_address(), refund_tx); + + //create redeem tx + std::list htlcs; + alice_c_wlt_instance->get_list_of_active_htlc(htlcs, true); + CHECK_AND_FORCE_ASSERT_MES(htlcs.size() == 1, false, "Epected htlc not found"); + currency::transaction result_redeem_tx = AUTO_VAL_INIT(result_redeem_tx); + alice_c_wlt_instance->redeem_htlc(htlcs.front().tx_id, alice_origin_b, result_redeem_tx); + + c.get_blockchain_storage().truncate_blockchain(c.get_blockchain_storage().get_current_blockchain_size() - 8); + + //try to submit wrong transaction that do refund before expiration + std::vector txs; + txs.push_back(refund_tx); + r = mine_next_pow_block_in_playtime_with_given_txs(m_mining_accunt.get_public_address(), c, txs); + CHECK_AND_FORCE_ASSERT_MES(!r, false, "Blo k with wrong refund tx accepted"); + + //forward blockchain and try to submit wrong tx that do redeem after expiration + r = mine_next_pow_blocks_in_playtime(m_mining_accunt.get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed"); + + std::vector txs2; + txs2.push_back(result_redeem_tx); + r = mine_next_pow_block_in_playtime_with_given_txs(m_mining_accunt.get_public_address(), c, txs2); + CHECK_AND_FORCE_ASSERT_MES(!r, false, "Blo k with wrong refund tx accepted"); + + + return true; +} + +//try to do double spend!!!! (redeem and refund), check if wallet see redeemed tx and don't let it be spent \ No newline at end of file diff --git a/tests/core_tests/atomic_tests.h b/tests/core_tests/atomic_tests.h index 4c8c93fe..74a283a5 100644 --- a/tests/core_tests/atomic_tests.h +++ b/tests/core_tests/atomic_tests.h @@ -14,6 +14,15 @@ struct atomic_simple_test : public wallet_test bool c1(currency::core& c, size_t ev_index, const std::vector& events); private: mutable currency::account_base m_mining_accunt; - +}; + + +struct atomic_test_wrong_redeem_wrong_refund : public wallet_test +{ + atomic_test_wrong_redeem_wrong_refund(); + bool generate(std::vector& events) const; + bool c1(currency::core& c, size_t ev_index, const std::vector& events); +private: + mutable currency::account_base m_mining_accunt; }; diff --git a/tests/core_tests/chaingen_helpers.h b/tests/core_tests/chaingen_helpers.h index 56cf4696..dacef62c 100644 --- a/tests/core_tests/chaingen_helpers.h +++ b/tests/core_tests/chaingen_helpers.h @@ -109,6 +109,11 @@ inline bool mine_next_pow_block_in_playtime_with_given_txs(const currency::accou CHECK_AND_ASSERT_MES(r, false, "find_nonce_for_given_block failed"); currency::block_verification_context bvc = AUTO_VAL_INIT(bvc); + for (auto& tx : txs) + { + crypto::hash tx_id = currency::get_transaction_hash(tx); + bvc.m_onboard_transactions[tx_id] = tx; + } c.handle_incoming_block(t_serializable_object_to_blob(b), bvc); CHECK_AND_NO_ASSERT_MES(!bvc.m_verification_failed && !bvc.m_marked_as_orphaned && !bvc.m_already_exists, false, "block verification context check failed"); diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 0c49e02a..54ad220c 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -1022,6 +1022,7 @@ int main(int argc, char* argv[]) // atomics GENERATE_AND_PLAY(atomic_simple_test); + GENERATE_AND_PLAY(atomic_test_wrong_redeem_wrong_refund); // GENERATE_AND_PLAY(gen_block_reward); // END OF TESTS */