forked from lthn/blockchain
atomics: tests for altchain switching
This commit is contained in:
parent
8fe41c8df2
commit
f05bd7a3fb
6 changed files with 294 additions and 9 deletions
|
|
@ -1302,6 +1302,68 @@ void wallet2::process_unconfirmed(const currency::transaction& tx, std::vector<s
|
|||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
||||
void wallet2::unprocess_htlc_triggers_on_block_removed(uint64_t height)
|
||||
{
|
||||
if (!m_htlcs.size())
|
||||
return;
|
||||
|
||||
if (height > m_htlcs.rbegin()->first)
|
||||
{
|
||||
//there is no active htlc that at this height
|
||||
CHECK_AND_ASSERT_MES(m_active_htlcs.size() == 0, void(), "Self check failed: m_active_htlcs.size() = " << m_active_htlcs.size());
|
||||
return;
|
||||
}
|
||||
//we have to check if there is a htlc that has to become deactivated
|
||||
auto pair_of_it = m_htlcs.equal_range(height);
|
||||
for (auto it = pair_of_it.first; it != pair_of_it.second; it++)
|
||||
{
|
||||
auto& tr = m_transfers[it->second.transfer_index];
|
||||
//found contract that supposed to be re-activated and set to active
|
||||
if (it->second.is_wallet_owns_redeem)
|
||||
{
|
||||
// this means that wallet received atomic as proposal but never activated it, and now we back to phase where out can be activated
|
||||
//but we keep spend flag anyway
|
||||
tr.m_flags |= WALLET_TRANSFER_DETAIL_FLAG_SPENT; //re assure that it has spent flag
|
||||
tr.m_spent_height = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// this means that wallet created atomic by itself, and second part didn't redeem it,
|
||||
// so refund money became available, and now we back again to unavailable state
|
||||
tr.m_flags |= WALLET_TRANSFER_DETAIL_FLAG_SPENT; //reset spent flag
|
||||
m_found_free_amounts.clear(); //reset free amounts cache
|
||||
tr.m_spent_height = 0;
|
||||
}
|
||||
//re-add to active contracts
|
||||
auto pair_key = std::make_pair(tr.m_ptx_wallet_info->m_tx.vout[tr.m_internal_output_index].amount, tr.m_global_output_index);
|
||||
auto it_active_htlc = m_active_htlcs.find(pair_key);
|
||||
if (it_active_htlc != m_active_htlcs.end())
|
||||
{
|
||||
LOG_ERROR("Error at putting back htlc: already exist?");
|
||||
it_active_htlc->second = it->second.transfer_index;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
m_active_htlcs[pair_key] = it->second.transfer_index;
|
||||
m_active_htlcs_txid[tr.tx_hash()] = it->second.transfer_index;
|
||||
}
|
||||
|
||||
const crypto::hash tx_id = tr.tx_hash();
|
||||
auto tx_id_it = m_active_htlcs_txid.find(tx_id);
|
||||
if (tx_id_it != m_active_htlcs_txid.end())
|
||||
{
|
||||
LOG_ERROR("Error at putting back htlc_txid: already exist?");
|
||||
tx_id_it->second = it->second.transfer_index;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
m_active_htlcs_txid[tx_id] = it->second.transfer_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
void wallet2::process_htlc_triggers_on_block_added(uint64_t height)
|
||||
{
|
||||
if (!m_htlcs.size())
|
||||
|
|
@ -1361,7 +1423,7 @@ void wallet2::process_new_blockchain_entry(const currency::block& b, const curre
|
|||
!(height == m_minimum_height || get_blockchain_current_size() <= 1), error::wallet_internal_error,
|
||||
"current_index=" + std::to_string(height) + ", get_blockchain_current_height()=" + std::to_string(get_blockchain_current_size()));
|
||||
|
||||
process_htlc_triggers_on_block_added(height);
|
||||
|
||||
|
||||
|
||||
//optimization: seeking only for blocks that are not older then the wallet creation time plus 1 day. 1 day is for possible user incorrect time setup
|
||||
|
|
@ -1393,6 +1455,8 @@ void wallet2::process_new_blockchain_entry(const currency::block& b, const curre
|
|||
if (!is_pos_block(b))
|
||||
m_last_pow_block_h = height;
|
||||
|
||||
|
||||
process_htlc_triggers_on_block_added(height);
|
||||
m_wcallback->on_new_block(height, b);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
|
@ -2213,6 +2277,10 @@ void wallet2::detach_blockchain(uint64_t including_height)
|
|||
}
|
||||
}
|
||||
|
||||
for (uint64_t i = get_top_block_height(); i != including_height - 1 && i != 0; i--)
|
||||
{
|
||||
unprocess_htlc_triggers_on_block_removed(i);
|
||||
}
|
||||
size_t blocks_detached = detach_from_block_ids(including_height);
|
||||
|
||||
//rollback spends
|
||||
|
|
@ -2226,14 +2294,6 @@ void wallet2::detach_blockchain(uint64_t including_height)
|
|||
WLT_LOG_BLUE("Transfer [" << i << "] spent height: " << tr.m_spent_height << " -> 0, reason: detaching blockchain", LOG_LEVEL_1);
|
||||
tr.m_spent_height = 0;
|
||||
//check if it's hltc contract
|
||||
if (tr.m_ptx_wallet_info->m_tx.vout[tr.m_internal_output_index].target.type() == typeid(txout_htlc) && tr.m_flags & WALLET_TRANSFER_DETAIL_FLAG_HTLC_REDEEM)
|
||||
{
|
||||
//only if htlc was spent as a redeem, then we put htlc back as active
|
||||
const txout_htlc& htlc = boost::get<txout_htlc>(tr.m_ptx_wallet_info->m_tx.vout[tr.m_internal_output_index].target);
|
||||
auto amount_gindex_pair = std::make_pair(tr.m_ptx_wallet_info->m_tx.vout[tr.m_internal_output_index].amount, tr.m_global_output_index);
|
||||
m_active_htlcs[amount_gindex_pair] = i;
|
||||
m_active_htlcs_txid[tr.tx_hash()] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4035,6 +4095,7 @@ void wallet2::create_htlc_proposal(uint64_t amount, const currency::account_publ
|
|||
finalized_tx ft = AUTO_VAL_INIT(ft);
|
||||
this->transfer(ctp, ft, true, nullptr);
|
||||
origin = ft.htlc_origin;
|
||||
tx = ft.tx;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::get_list_of_active_htlc(std::list<wallet_public::htlc_entry_info>& htlcs, bool only_redeem_txs)
|
||||
|
|
|
|||
|
|
@ -802,6 +802,7 @@ namespace tools
|
|||
const crypto::hash& bl_id,
|
||||
uint64_t height);
|
||||
void process_htlc_triggers_on_block_added(uint64_t height);
|
||||
void unprocess_htlc_triggers_on_block_removed(uint64_t height);
|
||||
bool get_pos_entries(currency::COMMAND_RPC_SCAN_POS::request& req);
|
||||
bool build_minted_block(const currency::COMMAND_RPC_SCAN_POS::request& req, const currency::COMMAND_RPC_SCAN_POS::response& rsp, uint64_t new_block_expected_height = UINT64_MAX);
|
||||
bool build_minted_block(const currency::COMMAND_RPC_SCAN_POS::request& req, const currency::COMMAND_RPC_SCAN_POS::response& rsp, const currency::account_public_address& miner_address, uint64_t new_block_expected_height = UINT64_MAX);
|
||||
|
|
|
|||
|
|
@ -454,3 +454,195 @@ bool atomic_test_wrong_redeem_wrong_refund::c1(currency::core& c, size_t ev_inde
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
|
||||
atomic_test_altchain_simple::atomic_test_altchain_simple()
|
||||
{
|
||||
REGISTER_CALLBACK_METHOD(atomic_test_altchain_simple, c1);
|
||||
}
|
||||
|
||||
bool atomic_test_altchain_simple::generate(std::vector<test_event_entry>& 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_altchain_simple::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
||||
{
|
||||
/*
|
||||
1. Create HTLC proposal
|
||||
2. Do switch to altchain to the point before HTLC proposal creation (core and wallet)
|
||||
3. Do redeem in altchain
|
||||
4. Do switch to altchain to the point before HTLC redeem (core and wallet)
|
||||
5. Do redeem in altchain
|
||||
6. Validate correct state of core and wallet
|
||||
7. Do redeem in altchain
|
||||
8. Validate state
|
||||
|
||||
TODO: play with expirations
|
||||
TODO: do refund, and then redeem in altchain
|
||||
*/
|
||||
|
||||
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);
|
||||
|
||||
|
||||
#define AMOUNT_TO_TRANSFER_HTLC (TESTS_DEFAULT_FEE*10)
|
||||
|
||||
std::shared_ptr<tools::wallet2> miner_wlt = init_playtime_test_wallet(events, c, m_mining_accunt);
|
||||
|
||||
size_t blocks_fetched = 0;
|
||||
bool received_money = false;
|
||||
std::atomic<bool> 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);
|
||||
|
||||
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();
|
||||
|
||||
std::string alice_origin; //will be deterministically generated by Alice's A wallet
|
||||
currency::transaction htlc_proposal_tx = AUTO_VAL_INIT(htlc_proposal_tx);
|
||||
alice_a_wlt_instance->create_htlc_proposal(transfer_amount - TESTS_DEFAULT_FEE, alice_b_wlt_instance->get_account().get_public_address(), 12, htlc_proposal_tx, currency::null_hash, alice_origin);
|
||||
|
||||
crypto::hash split_id = c.get_blockchain_storage().get_top_block_id();
|
||||
uint64_t split_height = c.get_blockchain_storage().get_top_block_height();
|
||||
|
||||
//forward blockchain to create redeem transaction
|
||||
r = mine_next_pow_blocks_in_playtime(m_mining_accunt.get_public_address(), c, 3);
|
||||
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed");
|
||||
|
||||
alice_a_wlt_instance->refresh();
|
||||
alice_b_wlt_instance->refresh();
|
||||
|
||||
//memorize block id at split height before split to make sure split happened
|
||||
crypto::hash id_first_splited_block = c.get_blockchain_storage().get_block_id_by_height(split_height + 1);
|
||||
|
||||
|
||||
//validate state of a
|
||||
std::list<tools::wallet_public::htlc_entry_info> htlcs_a;
|
||||
alice_a_wlt_instance->get_list_of_active_htlc(htlcs_a, false);
|
||||
CHECK_AND_FORCE_ASSERT_MES(htlcs_a.size() == 1, false, "Epected htlc not found");
|
||||
CHECK_AND_FORCE_ASSERT_MES(htlcs_a.back().is_redeem == false, false, "type of htlc mismatched");
|
||||
|
||||
//validate state of b
|
||||
std::list<tools::wallet_public::htlc_entry_info> htlcs_b;
|
||||
alice_b_wlt_instance->get_list_of_active_htlc(htlcs_b, true);
|
||||
CHECK_AND_FORCE_ASSERT_MES(htlcs_b.size() == 1, false, "Epected htlc not found");
|
||||
CHECK_AND_FORCE_ASSERT_MES(htlcs_b.back().is_redeem == true, false, "type of htlc mismatched");
|
||||
|
||||
//create altchain
|
||||
std::vector<currency::transaction> txs;
|
||||
txs.push_back(htlc_proposal_tx);
|
||||
r = mine_next_pow_blocks_in_playtime_with_given_txs(m_mining_accunt.get_public_address(), txs, c, 10, split_id);
|
||||
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed");
|
||||
|
||||
//make sure reorganize happened
|
||||
crypto::hash id_new_chain = c.get_blockchain_storage().get_block_id_by_height(split_height + 1);
|
||||
CHECK_AND_ASSERT_MES(id_new_chain != id_first_splited_block, false, "Reorganize didn't happen");
|
||||
|
||||
alice_a_wlt_instance->refresh();
|
||||
alice_b_wlt_instance->refresh();
|
||||
|
||||
htlcs_a.clear();
|
||||
alice_a_wlt_instance->get_list_of_active_htlc(htlcs_a, false);
|
||||
CHECK_AND_FORCE_ASSERT_MES(htlcs_a.size() == 1, false, "Epected htlc not found");
|
||||
CHECK_AND_FORCE_ASSERT_MES(htlcs_a.back().is_redeem == false, false, "type of htlc mismatched");
|
||||
|
||||
//validate state of b
|
||||
htlcs_b.clear();
|
||||
alice_b_wlt_instance->get_list_of_active_htlc(htlcs_b, true);
|
||||
CHECK_AND_FORCE_ASSERT_MES(htlcs_b.size() == 1, false, "Epected htlc not found");
|
||||
CHECK_AND_FORCE_ASSERT_MES(htlcs_b.back().is_redeem == true, false, "type of htlc mismatched");
|
||||
|
||||
currency::transaction result_redeem_tx = AUTO_VAL_INIT(result_redeem_tx);
|
||||
alice_b_wlt_instance->redeem_htlc(htlcs_b.front().tx_id, alice_origin, result_redeem_tx);
|
||||
|
||||
crypto::hash split_id_2 = c.get_blockchain_storage().get_top_block_id();
|
||||
uint64_t split_height_2 = c.get_blockchain_storage().get_top_block_height();
|
||||
|
||||
//forward blockchain
|
||||
r = mine_next_pow_blocks_in_playtime(m_mining_accunt.get_public_address(), c, 3);
|
||||
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed");
|
||||
|
||||
alice_a_wlt_instance->refresh();
|
||||
alice_b_wlt_instance->refresh();
|
||||
|
||||
|
||||
//memorize block id at split height before split to make sure split happened
|
||||
crypto::hash id_first_splited_block_2 = c.get_blockchain_storage().get_block_id_by_height(split_height_2 + 1);
|
||||
|
||||
txs.clear();
|
||||
txs.push_back(result_redeem_tx);
|
||||
r = mine_next_pow_blocks_in_playtime_with_given_txs(m_mining_accunt.get_public_address(), txs, c, 10, split_id_2);
|
||||
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed");
|
||||
|
||||
//make sure reorganize happened
|
||||
crypto::hash id_new_chain_2 = c.get_blockchain_storage().get_block_id_by_height(split_height_2 + 1);
|
||||
CHECK_AND_ASSERT_MES(id_new_chain_2 != id_first_splited_block_2, false, "Reorganize didn't happen");
|
||||
|
||||
alice_a_wlt_instance->refresh();
|
||||
alice_b_wlt_instance->refresh();
|
||||
|
||||
htlcs_a.clear();
|
||||
alice_a_wlt_instance->get_list_of_active_htlc(htlcs_a, false);
|
||||
CHECK_AND_FORCE_ASSERT_MES(htlcs_a.size() == 1, false, "Epected htlc not found");
|
||||
CHECK_AND_FORCE_ASSERT_MES(htlcs_a.back().is_redeem == false, false, "type of htlc mismatched");
|
||||
|
||||
//validate state of b
|
||||
htlcs_b.clear();
|
||||
alice_b_wlt_instance->get_list_of_active_htlc(htlcs_b, true);
|
||||
CHECK_AND_FORCE_ASSERT_MES(htlcs_b.size() == 1, false, "Epected htlc not found");
|
||||
CHECK_AND_FORCE_ASSERT_MES(htlcs_b.back().is_redeem == true, false, "type of htlc mismatched");
|
||||
|
||||
//forward blockchain
|
||||
r = mine_next_pow_blocks_in_playtime(m_mining_accunt.get_public_address(), c, 3);
|
||||
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed");
|
||||
|
||||
alice_a_wlt_instance->refresh();
|
||||
alice_b_wlt_instance->refresh();
|
||||
|
||||
htlcs_a.clear();
|
||||
alice_a_wlt_instance->get_list_of_active_htlc(htlcs_a, false);
|
||||
CHECK_AND_FORCE_ASSERT_MES(htlcs_a.size() == 0, false, "htlc contracts count is wrong");
|
||||
|
||||
//validate state of b
|
||||
htlcs_b.clear();
|
||||
alice_b_wlt_instance->get_list_of_active_htlc(htlcs_b, true);
|
||||
CHECK_AND_FORCE_ASSERT_MES(htlcs_b.size() == 0, false, "htlc contracts count is wrong");
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,3 +26,12 @@ private:
|
|||
mutable currency::account_base m_mining_accunt;
|
||||
};
|
||||
|
||||
|
||||
struct atomic_test_altchain_simple : public wallet_test
|
||||
{
|
||||
atomic_test_altchain_simple();
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
private:
|
||||
mutable currency::account_base m_mining_accunt;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -137,6 +137,27 @@ inline bool mine_next_pow_blocks_in_playtime(const currency::account_public_addr
|
|||
return true;
|
||||
}
|
||||
|
||||
inline bool mine_next_pow_blocks_in_playtime_with_given_txs(const currency::account_public_address& miner_addr, const std::vector<currency::transaction>& txs, currency::core& c, size_t blocks_count, const crypto::hash& prev_id)
|
||||
{
|
||||
std::vector<currency::transaction> txs_local = txs;
|
||||
|
||||
crypto::hash prev_id_internal = prev_id;
|
||||
currency::block prv_block = AUTO_VAL_INIT(prv_block);
|
||||
bool r = c.get_blockchain_storage().get_block_by_hash(prev_id, prv_block);
|
||||
CHECK_AND_ASSERT_MES(r, false, "block with id " << prev_id << " not found");
|
||||
|
||||
for (size_t i = 0; i != blocks_count; i++)
|
||||
{
|
||||
if (!mine_next_pow_block_in_playtime_with_given_txs(miner_addr, c, txs_local, prev_id_internal, currency::get_block_height(prv_block)+1, &prv_block))
|
||||
return false;
|
||||
prev_id_internal = get_block_hash(prv_block);
|
||||
txs_local.clear();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// NOTE: stake coins return back to the wallet, newly generated coins go to miner_address (by default they are the same destinations)
|
||||
inline bool mine_next_pos_block_in_playtime_with_wallet(tools::wallet2& w, const currency::account_public_address& miner_address, size_t& pos_entries_count)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1023,6 +1023,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(atomic_test_altchain_simple);
|
||||
|
||||
// GENERATE_AND_PLAY(gen_block_reward);
|
||||
// END OF TESTS */
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue