diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 27383e5d..bcee6f48 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -765,6 +765,7 @@ int main(int argc, char* argv[]) // GENERATE_AND_PLAY(escrow_proposal_acceptance_in_alt_chain); -- work in progress GENERATE_AND_PLAY(escrow_zero_amounts); GENERATE_AND_PLAY(escrow_acceptance_and_balance); + GENERATE_AND_PLAY(escrow_balance); GENERATE_AND_PLAY(escrow_altchain_meta_test<0>); GENERATE_AND_PLAY(escrow_altchain_meta_test<1>); diff --git a/tests/core_tests/escrow_wallet_tests.cpp b/tests/core_tests/escrow_wallet_tests.cpp index 9bbc919f..8fc4fd81 100644 --- a/tests/core_tests/escrow_wallet_tests.cpp +++ b/tests/core_tests/escrow_wallet_tests.cpp @@ -3292,3 +3292,490 @@ bool escrow_acceptance_and_balance::check_balance_after_acceptance_confirmed(cur return true; } + +//------------------------------------------------------------------------------ + +escrow_balance::escrow_balance() + : m_alice_bob_start_amount(0) + , m_alice_bob_start_chunk_amount(0) +{ + REGISTER_CALLBACK_METHOD(escrow_balance, c1); +} + +bool escrow_balance::generate(std::vector& events) const +{ + // Test idea: carefull check balances on each stage of escrow contract (including cancellation req and acc): + // 1) within wallet callback in the middle of contract function call + // 2) after tx was sent to network but not yet confirmed + // 3) after tx was confirmed + + m_accounts.resize(TOTAL_ACCS_COUNT); + account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); + account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); + account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate(); + + MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time()); + REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + + m_alice_bob_start_amount = MK_TEST_COINS(200); + uint64_t amount_chunks = 10; + m_alice_bob_start_chunk_amount = m_alice_bob_start_amount / 10; + + transaction tx_0 = AUTO_VAL_INIT(tx_0); + bool r = construct_tx_with_many_outputs(events, blk_0r, miner_acc.get_keys(), alice_acc.get_public_address(), m_alice_bob_start_amount, 10, TESTS_DEFAULT_FEE, tx_0); + CHECK_AND_ASSERT_MES(r, false, "construct_tx_with_many_outputs failed"); + events.push_back(tx_0); + + transaction tx_1 = AUTO_VAL_INIT(tx_1); + r = construct_tx_with_many_outputs(events, blk_0r, miner_acc.get_keys(), bob_acc.get_public_address(), m_alice_bob_start_amount, 10, TESTS_DEFAULT_FEE, tx_1); + CHECK_AND_ASSERT_MES(r, false, "construct_tx_with_many_outputs failed"); + events.push_back(tx_1); + + MAKE_NEXT_BLOCK_TX_LIST(events, blk_1, blk_0r, miner_acc, std::list({tx_0, tx_1})); + + REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, WALLET_DEFAULT_TX_SPENDABLE_AGE); + + DO_CALLBACK(events, "c1"); + + return true; +} + +bool escrow_balance::c1(currency::core& c, size_t ev_index, const std::vector& events) +{ + bool r = false, stub_bool = false; + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count()); + + std::shared_ptr alice_wlt = init_playtime_test_wallet(events, c, m_accounts[ALICE_ACC_IDX]); + auto alice_bc = std::make_shared("Alice"); + alice_wlt->callback(alice_bc); + + std::shared_ptr bob_wlt = init_playtime_test_wallet(events, c, m_accounts[BOB_ACC_IDX]); + auto bob_bc = std::make_shared("Bob"); + bob_wlt->callback(bob_bc); + + alice_bc->expect_balance(m_alice_bob_start_amount, 0); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, + m_alice_bob_start_amount, // total + true, UINT64_MAX, + m_alice_bob_start_amount, // unlocked + 0, // mined + 0, // awaited in + 0 // awainted out + ), false, ""); + CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed, see above"); + + bob_bc->expect_balance(m_alice_bob_start_amount, 0); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt, + m_alice_bob_start_amount, // total + true, UINT64_MAX, + m_alice_bob_start_amount, // unlocked + 0, // mined + 0, // awaited in + 0 // awaited out + ), false, ""); + CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above"); + + // + // escrow proposal + // + bc_services::contract_private_details cpd = AUTO_VAL_INIT(cpd); + cpd.amount_a_pledge = MK_TEST_COINS(7); + cpd.amount_b_pledge = MK_TEST_COINS(5); + cpd.amount_to_pay = MK_TEST_COINS(3); + cpd.a_addr = m_accounts[ALICE_ACC_IDX].get_public_address(); + cpd.b_addr = m_accounts[BOB_ACC_IDX].get_public_address(); + uint64_t alice_proposal_fee = MK_TEST_COINS(4); + uint64_t bob_acceptace_fee = MK_TEST_COINS(2); + uint64_t bob_release_fee = MK_TEST_COINS(9); // Alice states that Bob should pay this much money for upcoming contract release (which will be sent by Alice) + uint64_t alice_cancellation_request_fee = MK_TEST_COINS(1); + + alice_bc->expect_balance(m_alice_bob_start_amount - alice_proposal_fee, m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount); // balace after sending the proposal + + transaction proposal_tx = AUTO_VAL_INIT(proposal_tx); + transaction escrow_template_tx = AUTO_VAL_INIT(escrow_template_tx); + uint64_t expiration_time = test_core_time::get_time() + 60; + LOG_PRINT_GREEN("\n" "alice_wlt->send_escrow_proposal()", LOG_LEVEL_0); + alice_wlt->send_escrow_proposal(cpd, 0, 0, expiration_time, alice_proposal_fee, bob_release_fee, "", proposal_tx, escrow_template_tx); + + CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed, see above"); + + tools::wallet2::escrow_contracts_container contracts; + r = alice_wlt->get_contracts(contracts); + CHECK_AND_ASSERT_MES(r && contracts.size() == 1, false, "get_contracts() for Alice failed"); + crypto::hash contract_id = contracts.begin()->first; + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count()); + + // proposal tx is not confirmed yet + alice_bc->expect_balance(); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, + m_alice_bob_start_amount - alice_proposal_fee, // total + true, UINT64_MAX, + m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount, // unlocked + 0, // mined + 0, // awaited in + 0 // awainted out + ), false, ""); + CHECK_AND_ASSERT_MES(!alice_bc->called(), false, "balance callback check failed"); + + // Bob's balance should not change + bob_bc->expect_balance(m_alice_bob_start_amount, m_alice_bob_start_amount); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt, + m_alice_bob_start_amount, // total + true, UINT64_MAX, + m_alice_bob_start_amount, // unlocked + 0, // mined + 0, // awaited in + 0 // awaited out + ), false, ""); + CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above"); + + // mine a block to confirm escrow proposal tx + r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed"); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count()); + + // proposal tx is confirmed (balances should stay the same) + alice_bc->expect_balance(m_alice_bob_start_amount - alice_proposal_fee, m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, + m_alice_bob_start_amount - alice_proposal_fee, // total + true, UINT64_MAX, + m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount, // unlocked + 0, // mined + 0, // awaited in + 0 // awainted out + ), false, ""); + CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed, see above"); + + bob_bc->expect_balance(m_alice_bob_start_amount, m_alice_bob_start_amount); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt, + m_alice_bob_start_amount, // total + true, UINT64_MAX, + m_alice_bob_start_amount, // unlocked + 0, // mined + 0, // awaited in + 0 // awaited out + ), false, ""); + CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above"); + + // + // proposal acceptance + // + bob_bc->expect_balance(m_alice_bob_start_amount - cpd.amount_b_pledge - bob_release_fee - bob_acceptace_fee, m_alice_bob_start_amount - m_alice_bob_start_chunk_amount); + + LOG_PRINT_GREEN("\n" "bob_wlt->accept_proposal()", LOG_LEVEL_0); + bob_wlt->accept_proposal(contract_id, bob_acceptace_fee); + + CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above"); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count()); + + // acceptance tx is not confirmed yet + alice_bc->expect_balance(m_alice_bob_start_amount - alice_proposal_fee - cpd.amount_a_pledge - cpd.amount_to_pay, m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, + m_alice_bob_start_amount - alice_proposal_fee - cpd.amount_a_pledge - cpd.amount_to_pay, // total + true, UINT64_MAX, + m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount, // unlocked + 0, // mined + 0, // awaited in + cpd.amount_a_pledge + cpd.amount_to_pay // awaited out + ), false, ""); + CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed, see above"); + + bob_bc->expect_balance(); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt, + m_alice_bob_start_amount - cpd.amount_b_pledge - bob_release_fee - bob_acceptace_fee, // total + true, UINT64_MAX, + m_alice_bob_start_amount - 1 * m_alice_bob_start_chunk_amount, // unlocked + 0, // mined + 0, // awaited in + cpd.amount_b_pledge + bob_release_fee // awaited out + ), false, ""); + CHECK_AND_ASSERT_MES(!bob_bc->called(), false, "balance callback check failed"); + + // mine a block containing contract acceptance + r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed"); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count()); + + // acceptance tx is confirmed + alice_bc->expect_balance(m_alice_bob_start_amount - alice_proposal_fee - cpd.amount_a_pledge - cpd.amount_to_pay, m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, + m_alice_bob_start_amount - alice_proposal_fee - cpd.amount_a_pledge - cpd.amount_to_pay, // total + true, UINT64_MAX, + m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount, // unlocked + 0, // mined + 0, // awaited in + 0 // awaited out + ), false, ""); + CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed, see above"); + + bob_bc->expect_balance(m_alice_bob_start_amount - cpd.amount_b_pledge - bob_release_fee - bob_acceptace_fee, m_alice_bob_start_amount - 1 * m_alice_bob_start_chunk_amount); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt, + m_alice_bob_start_amount - cpd.amount_b_pledge - bob_release_fee - bob_acceptace_fee, // total + true, UINT64_MAX, + m_alice_bob_start_amount - 1 * m_alice_bob_start_chunk_amount, // unlocked + 0, // mined + 0, // awaited in + 0 // awaited out + ), false, ""); + CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above"); + + // + // cancellation request + // + alice_bc->expect_balance(m_alice_bob_start_amount - alice_proposal_fee - cpd.amount_a_pledge - cpd.amount_to_pay - alice_cancellation_request_fee, m_alice_bob_start_amount - 3 * m_alice_bob_start_chunk_amount); + + LOG_PRINT_GREEN("\n" "alice_wlt->request_cancel_contract()", LOG_LEVEL_0); + alice_wlt->request_cancel_contract(contract_id, alice_cancellation_request_fee, 60 * 60); + + CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed, see above"); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count()); + + // cancellation request is not confirmed yet + alice_bc->expect_balance(); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, + m_alice_bob_start_amount - alice_proposal_fee - cpd.amount_a_pledge - cpd.amount_to_pay - alice_cancellation_request_fee, // total + true, UINT64_MAX, + m_alice_bob_start_amount - 3 * m_alice_bob_start_chunk_amount, // unlocked + 0, // mined + 0, // awaited in + 0 // awaited out + ), false, ""); + CHECK_AND_ASSERT_MES(!alice_bc->called(), false, "balance callback check failed"); + + bob_bc->expect_balance(m_alice_bob_start_amount - cpd.amount_b_pledge - bob_release_fee - bob_acceptace_fee, m_alice_bob_start_amount - 1 * m_alice_bob_start_chunk_amount); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt, + m_alice_bob_start_amount - cpd.amount_b_pledge - bob_release_fee - bob_acceptace_fee, // total + true, UINT64_MAX, + m_alice_bob_start_amount - 1 * m_alice_bob_start_chunk_amount, // unlocked + 0, // mined + 0, // awaited in + 0 // awaited out + ), false, ""); + CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above"); + + // mine a block containing cancellation request + r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed"); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count()); + + // cancellation request is confirmed + alice_bc->expect_balance(m_alice_bob_start_amount - alice_proposal_fee - cpd.amount_a_pledge - cpd.amount_to_pay - alice_cancellation_request_fee, m_alice_bob_start_amount - 3 * m_alice_bob_start_chunk_amount); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, + m_alice_bob_start_amount - alice_proposal_fee - cpd.amount_a_pledge - cpd.amount_to_pay - alice_cancellation_request_fee, // total + true, UINT64_MAX, + m_alice_bob_start_amount - 3 * m_alice_bob_start_chunk_amount, // unlocked + 0, // mined + 0, // awaited in + 0 // awaited out + ), false, ""); + CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed, see above"); + + bob_bc->expect_balance(m_alice_bob_start_amount - cpd.amount_b_pledge - bob_release_fee - bob_acceptace_fee, m_alice_bob_start_amount - 1 * m_alice_bob_start_chunk_amount); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt, + m_alice_bob_start_amount - cpd.amount_b_pledge - bob_release_fee - bob_acceptace_fee, // total + true, UINT64_MAX, + m_alice_bob_start_amount - 1 * m_alice_bob_start_chunk_amount, // unlocked + 0, // mined + 0, // awaited in + 0 // awaited out + ), false, ""); + CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above"); + + // + // cancellation acceptance + // + bob_bc->expect_balance(); + + LOG_PRINT_GREEN("\n" "bob_wlt->accept_cancel_contract()", LOG_LEVEL_0); + bob_wlt->accept_cancel_contract(contract_id); + + CHECK_AND_ASSERT_MES(!bob_bc->called(), false, "balance callback check failed"); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count()); + + // cancellation acceptance is not confirmed yet + alice_bc->expect_balance(m_alice_bob_start_amount - alice_proposal_fee - alice_cancellation_request_fee, m_alice_bob_start_amount - 3 * m_alice_bob_start_chunk_amount); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, + m_alice_bob_start_amount - alice_proposal_fee - alice_cancellation_request_fee, // total + true, UINT64_MAX, + m_alice_bob_start_amount - 3 * m_alice_bob_start_chunk_amount, // unlocked + 0, // mined + cpd.amount_a_pledge + cpd.amount_to_pay, // awaited in + 0 // awaited out + ), false, ""); + CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed, see above"); + + bob_bc->expect_balance(m_alice_bob_start_amount - bob_release_fee - bob_acceptace_fee, m_alice_bob_start_amount - 1 * m_alice_bob_start_chunk_amount); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt, + m_alice_bob_start_amount - bob_release_fee - bob_acceptace_fee, // total + true, UINT64_MAX, + m_alice_bob_start_amount - 1 * m_alice_bob_start_chunk_amount, // unlocked + 0, // mined + cpd.amount_b_pledge, // awaited in + 0 // awaited out + ), false, ""); + CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above"); + + // mine a block containing cancellation acceptance + r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed"); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count()); + + // cancellation acceptance is confirmed + alice_bc->expect_balance(m_alice_bob_start_amount - alice_proposal_fee - alice_cancellation_request_fee, m_alice_bob_start_amount - 3 * m_alice_bob_start_chunk_amount); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, + m_alice_bob_start_amount - alice_proposal_fee - alice_cancellation_request_fee, // total + true, UINT64_MAX, + m_alice_bob_start_amount - 3 * m_alice_bob_start_chunk_amount, // unlocked + 0, // mined + 0, // awaited in + 0 // awaited out + ), false, ""); + CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed, see above"); + + bob_bc->expect_balance(m_alice_bob_start_amount - bob_release_fee - bob_acceptace_fee, m_alice_bob_start_amount - 1 * m_alice_bob_start_chunk_amount); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt, + m_alice_bob_start_amount - bob_release_fee - bob_acceptace_fee, // total + true, UINT64_MAX, + m_alice_bob_start_amount - 1 * m_alice_bob_start_chunk_amount, // unlocked + 0, // mined + 0, // awaited in + 0 // awaited out + ), false, ""); + CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above"); + + + // + // Stage 2 : check normal contract workflow + // don't check balances on request and accept as it was checked above + // + + uint64_t alice_balance_before_stage_2 = m_alice_bob_start_amount - alice_proposal_fee - alice_cancellation_request_fee; + uint64_t bob_balance_before_stage_2 = m_alice_bob_start_amount - bob_release_fee - bob_acceptace_fee; + + alice_bc->expect_balance(alice_balance_before_stage_2 - alice_proposal_fee, m_alice_bob_start_amount - 5 * m_alice_bob_start_chunk_amount); + + LOG_PRINT_GREEN("\n" "stage2: alice_wlt->send_escrow_proposal()", LOG_LEVEL_0); + proposal_tx = AUTO_VAL_INIT(proposal_tx); + escrow_template_tx = AUTO_VAL_INIT(escrow_template_tx); + alice_wlt->send_escrow_proposal(cpd, 0, 0, expiration_time, alice_proposal_fee, bob_release_fee, "", proposal_tx, escrow_template_tx); + + CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed, see above"); + + contracts.clear(); + r = alice_wlt->get_contracts(contracts); + CHECK_AND_ASSERT_MES(r && contracts.size() == 2, false, "get_contracts() for Alice failed"); + // get new contract id + if (contract_id != contracts.begin()->first) + contract_id = contracts.begin()->first; + else + contract_id = (++contracts.begin())->first; + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count()); + r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed"); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count()); + + bob_wlt->refresh(); + + bob_bc->expect_balance(bob_balance_before_stage_2 - cpd.amount_b_pledge - bob_release_fee - bob_acceptace_fee, m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount); + + LOG_PRINT_GREEN("\n" "stage2: bob_wlt->accept_proposal()", LOG_LEVEL_0); + bob_wlt->accept_proposal(contract_id, bob_acceptace_fee); + + CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above"); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count()); + r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed"); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count()); + + alice_bc->expect_balance(alice_balance_before_stage_2 - alice_proposal_fee - cpd.amount_a_pledge - cpd.amount_to_pay, m_alice_bob_start_amount - 5 * m_alice_bob_start_chunk_amount); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, + alice_balance_before_stage_2 - alice_proposal_fee - cpd.amount_a_pledge - cpd.amount_to_pay, // total + true, UINT64_MAX, + m_alice_bob_start_amount - 5 * m_alice_bob_start_chunk_amount, // unlocked + 0, // mined + 0, // awaited in + 0 // awaited out + ), false, ""); + CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed"); + + bob_bc->expect_balance(bob_balance_before_stage_2 - cpd.amount_b_pledge - bob_release_fee - bob_acceptace_fee, m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt, + bob_balance_before_stage_2 - cpd.amount_b_pledge - bob_release_fee - bob_acceptace_fee, // total + true, UINT64_MAX, + m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount, // unlocked + 0, // mined + 0, // awaited in + 0 // awaited out + ), false, ""); + CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above"); + + // + // contract release + // + + alice_bc->expect_balance(); + + alice_wlt->finish_contract(contract_id, BC_ESCROW_SERVICE_INSTRUCTION_RELEASE_NORMAL); + + CHECK_AND_ASSERT_MES(!alice_bc->called(), false, "balance callback check failed"); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count()); + + // contract release tx is unconfirmed + alice_bc->expect_balance(alice_balance_before_stage_2 - alice_proposal_fee - cpd.amount_to_pay, m_alice_bob_start_amount - 5 * m_alice_bob_start_chunk_amount); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, + alice_balance_before_stage_2 - alice_proposal_fee - cpd.amount_to_pay, // total + true, UINT64_MAX, + m_alice_bob_start_amount - 5 * m_alice_bob_start_chunk_amount, // unlocked + 0, // mined + cpd.amount_a_pledge, // awaited in + 0 // awaited out + ), false, ""); + CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed, see above"); + + bob_bc->expect_balance(bob_balance_before_stage_2 - bob_release_fee - bob_acceptace_fee + cpd.amount_to_pay, m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt, + bob_balance_before_stage_2 - bob_release_fee - bob_acceptace_fee + cpd.amount_to_pay, // total + true, UINT64_MAX, + m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount, // unlocked + 0, // mined + cpd.amount_b_pledge + cpd.amount_to_pay, // awaited in + 0 // awaited out + ), false, ""); + CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above"); + + r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed"); + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count()); + + // contract release tx is confirmed + alice_bc->expect_balance(alice_balance_before_stage_2 - alice_proposal_fee - cpd.amount_to_pay, m_alice_bob_start_amount - 5 * m_alice_bob_start_chunk_amount); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, + alice_balance_before_stage_2 - alice_proposal_fee - cpd.amount_to_pay, // total + true, UINT64_MAX, + m_alice_bob_start_amount - 5 * m_alice_bob_start_chunk_amount, // unlocked + 0, // mined + 0, // awaited in + 0 // awaited out + ), false, ""); + CHECK_AND_ASSERT_MES(alice_bc->check(), false, "balance callback check failed, see above"); + + bob_bc->expect_balance(bob_balance_before_stage_2 - bob_release_fee - bob_acceptace_fee + cpd.amount_to_pay, m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt, + bob_balance_before_stage_2 - bob_release_fee - bob_acceptace_fee + cpd.amount_to_pay, // total + true, UINT64_MAX, + m_alice_bob_start_amount - 2 * m_alice_bob_start_chunk_amount, // unlocked + 0, // mined + 0, // awaited in + 0 // awaited out + ), false, ""); + CHECK_AND_ASSERT_MES(bob_bc->check(), false, "balance callback check failed, see above"); + + return true; +} diff --git a/tests/core_tests/escrow_wallet_tests.h b/tests/core_tests/escrow_wallet_tests.h index 332ac5a2..da1a56f0 100644 --- a/tests/core_tests/escrow_wallet_tests.h +++ b/tests/core_tests/escrow_wallet_tests.h @@ -158,3 +158,13 @@ struct escrow_acceptance_and_balance : public wallet_test mutable uint64_t m_bob_fee_accept; mutable bc_services::contract_private_details m_cpd; }; + +struct escrow_balance : public wallet_test +{ + escrow_balance(); + bool generate(std::vector& events) const; + bool c1(currency::core& c, size_t ev_index, const std::vector& events); + + mutable uint64_t m_alice_bob_start_amount; + mutable uint64_t m_alice_bob_start_chunk_amount; +}; diff --git a/tests/core_tests/wallet_tests_basic.h b/tests/core_tests/wallet_tests_basic.h index 6ef041dc..4627284c 100644 --- a/tests/core_tests/wallet_tests_basic.h +++ b/tests/core_tests/wallet_tests_basic.h @@ -43,3 +43,47 @@ protected: mutable test_generator generator; std::shared_ptr m_core_proxy; }; + +// wallet callback helper to check balance in wallet callbacks +// see escrow_balance test for usage example +struct wallet_callback_balance_checker : public tools::i_wallet2_callback +{ + wallet_callback_balance_checker(const std::string& label) : m_label(label), m_result(true), m_called(false), m_balance(UINT64_MAX), m_unlocked_balance(UINT64_MAX), m_total_mined(UINT64_MAX) {} + + void expect_balance(uint64_t balance = UINT64_MAX, uint64_t unlocked_balance = UINT64_MAX, uint64_t total_mined = UINT64_MAX) + { + m_balance = balance; + m_unlocked_balance = unlocked_balance; + m_total_mined = total_mined; + m_called = false; + } + + virtual void on_transfer2(const tools::wallet_public::wallet_transfer_info& wti, uint64_t balance, uint64_t unlocked_balance, uint64_t total_mined) override + { + m_called = true; + m_result = false; + CHECK_AND_ASSERT_MES(m_balance == UINT64_MAX || balance == m_balance, (void)(0), m_label << " balance is incorrect: " << currency::print_money_brief(balance) << ", expected: " << currency::print_money_brief(m_balance)); + CHECK_AND_ASSERT_MES(m_unlocked_balance == UINT64_MAX || unlocked_balance == m_unlocked_balance, (void)(0), m_label << " unlocked balance is incorrect: " << currency::print_money_brief(unlocked_balance) << ", expected: " << currency::print_money_brief(m_unlocked_balance)); + CHECK_AND_ASSERT_MES(m_total_mined == UINT64_MAX || total_mined == m_total_mined, (void)(0), m_label << " total mined is incorrect: " << currency::print_money_brief(total_mined) << ", expected: " << currency::print_money_brief(m_total_mined)); + m_result = true; + } + + bool check() + { + bool result = m_result; + m_result = false; // clear result to avoid errorneous successive calls to check() without calling except_balance() + return result; + } + + bool called() + { + return m_called; + } + + bool m_result; + bool m_called; + std::string m_label; + uint64_t m_balance; + uint64_t m_unlocked_balance; + uint64_t m_total_mined; +};