1325 lines
63 KiB
C++
1325 lines
63 KiB
C++
// Copyright (c) 2014-2018 Zano Project
|
|
// Copyright (c) 2014-2018 The Louisdor Project
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include "chaingen.h"
|
|
|
|
#include "offers_test.h"
|
|
#include "offers_tests_common.h"
|
|
#include "offers_helper.h"
|
|
#include "currency_core/offers_services_helpers.h"
|
|
#include "currency_core/bc_offers_service.h"
|
|
|
|
using namespace epee;
|
|
using namespace currency;
|
|
|
|
bool sign_an_offer(const transaction &offer_source_tx, size_t offer_tx_index, const account_keys& offer_owner, const blobdata &blob_for_sig, crypto::signature &sig)
|
|
{
|
|
crypto::public_key related_tx_pub_key = get_tx_pub_key_from_extra(offer_source_tx);
|
|
crypto::public_key related_tx_offer_pub_key = bc_services::get_offer_secure_key_by_index_from_tx(offer_source_tx, offer_tx_index);
|
|
currency::keypair ephemeral = AUTO_VAL_INIT(ephemeral);
|
|
bool r = currency::derive_ephemeral_key_helper(offer_owner, related_tx_pub_key, offer_tx_index, ephemeral);
|
|
CHECK_AND_ASSERT_MES(r, false, "derive_ephemeral_key_helper failed");
|
|
CHECK_AND_ASSERT_MES(ephemeral.pub == related_tx_offer_pub_key, false, "ephemeral.pub == related_tx_offer_pub_key");
|
|
|
|
crypto::generate_signature(crypto::cn_fast_hash(blob_for_sig.data(), blob_for_sig.size()), ephemeral.pub, ephemeral.sec, sig);
|
|
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
bool fill_default_offer(bc_services::offer_details& od)
|
|
{
|
|
od.offer_type = OFFER_TYPE_PRIMARY_TO_TARGET;
|
|
od.amount_primary = 1000000000;
|
|
od.amount_target = 22222222;
|
|
od.target = "USD";
|
|
od.location_country = "US";
|
|
od.location_city = "New York City";
|
|
od.contacts = "skype: zina; icq: 12313212; email: zina@zina.com; mobile: +621234567834";
|
|
od.comment = "The best ever rate in NYC!!!";
|
|
od.payment_types = "BTC;BANK;CASH";
|
|
od.expiration_time = 10;
|
|
return true;
|
|
}
|
|
|
|
offers_tests::offers_tests()
|
|
{
|
|
REGISTER_CALLBACK_METHOD(offers_tests, configure_core);
|
|
REGISTER_CALLBACK_METHOD(offers_tests, check_offers_1);
|
|
REGISTER_CALLBACK_METHOD(offers_tests, check_offers_updated_1);
|
|
REGISTER_CALLBACK_METHOD(offers_tests, check_offers_updated_2);
|
|
}
|
|
#define FIRST_UPDATED_LOCATION "Washington"
|
|
#define SECOND_UPDATED_LOCATION "LA"
|
|
|
|
bool offers_tests::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
uint64_t ts_start = 1338224400;
|
|
|
|
GENERATE_ACCOUNT(miner_account);
|
|
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
|
|
MAKE_ACCOUNT(events, third_acc);
|
|
DO_CALLBACK(events, "configure_core");
|
|
MAKE_NEXT_BLOCK(events, blk_1, blk_0, miner_account);
|
|
REWIND_BLOCKS_N_WITH_TIME(events, blk_11, blk_1, miner_account, 10);
|
|
|
|
|
|
//push first offers
|
|
std::vector<currency::attachment_v> attachments;
|
|
bc_services::offer_details od = AUTO_VAL_INIT(od);
|
|
fill_default_offer(od);
|
|
bc_services::put_offer_into_attachment(od, attachments);
|
|
MAKE_TX_LIST_START_WITH_ATTACHS(events, txs_blk, miner_account, miner_account, MK_TEST_COINS(1), blk_11, attachments);
|
|
MAKE_NEXT_BLOCK_TX_LIST(events, blk_12, blk_11, miner_account, txs_blk);
|
|
|
|
//push second offer
|
|
std::vector<currency::attachment_v> attachments2;
|
|
bc_services::offer_details od2 = AUTO_VAL_INIT(od2);
|
|
fill_default_offer(od2);
|
|
bc_services::put_offer_into_attachment(od2, attachments2);
|
|
MAKE_TX_LIST_START_WITH_ATTACHS(events, txs_blk2, miner_account, miner_account, MK_TEST_COINS(1), blk_12, attachments2);
|
|
//crypto::secret_key offer_secret_key = generator.last_tx_generated_secret_key;
|
|
currency::transaction tx = txs_blk2.back();
|
|
MAKE_NEXT_BLOCK_TX_LIST(events, blk_13, blk_12, miner_account, txs_blk2);
|
|
|
|
// Both offers has timestamp of correspoging blocks which is greather then blk_11.
|
|
// However, "now" is close to blk_11.timestamp because there was no commands after it in event queue to adjust the time.
|
|
// This condition helps to check rear case, when offers marked with future timestamp should not be filtered out.
|
|
DO_CALLBACK(events, "check_offers_1");
|
|
|
|
//now update offer
|
|
std::vector<currency::attachment_v> attachments3;
|
|
bc_services::update_offer uo = AUTO_VAL_INIT(uo);
|
|
fill_default_offer(uo.of);
|
|
uo.of.location_city = "Washington";
|
|
uo.offer_index = 0;
|
|
uo.tx_id = currency::get_transaction_hash(tx);
|
|
|
|
crypto::public_key related_tx_pub_key_1 = get_tx_pub_key_from_extra(tx);
|
|
crypto::public_key related_tx_offer_pub_key_1 = bc_services::get_offer_secure_key_by_index_from_tx(tx, 0);
|
|
currency::keypair ephemeral_1;
|
|
bool r = currency::derive_ephemeral_key_helper(miner_account.get_keys(), related_tx_pub_key_1, 0, ephemeral_1);
|
|
CHECK_AND_ASSERT_THROW_MES(r, "Failed to derive_ephemeral_key_helper");
|
|
CHECK_AND_ASSERT_THROW_MES(ephemeral_1.pub == related_tx_offer_pub_key_1, "Failed to derive_ephemeral_key_helper(pub keys missmatch)");
|
|
|
|
blobdata bl_for_sig = bc_services::make_offer_sig_blob(uo);
|
|
crypto::generate_signature(crypto::cn_fast_hash(bl_for_sig.data(), bl_for_sig.size()), ephemeral_1.pub, ephemeral_1.sec, uo.sig);
|
|
bc_services::put_offer_into_attachment(uo, attachments3);
|
|
|
|
|
|
|
|
//blobdata bl_for_sig = currency::make_offer_sig_blob(uo);
|
|
//crypto::generate_signature(crypto::cn_fast_hash(bl_for_sig.data(), bl_for_sig.size()), currency::get_tx_pub_key_from_extra(tx), offer_secret_key, uo.sig);
|
|
MAKE_TX_LIST_START_WITH_ATTACHS(events, txs_blk3, miner_account, miner_account, MK_TEST_COINS(1), blk_13, attachments3);
|
|
//offer_secret_key = generator.last_tx_generated_secret_key;
|
|
tx = txs_blk3.back();
|
|
tx_id_1 = currency::get_transaction_hash(tx);
|
|
MAKE_NEXT_BLOCK_TX_LIST(events, blk_14, blk_13, miner_account, txs_blk3);
|
|
DO_CALLBACK(events, "check_offers_updated_1");
|
|
|
|
//and update this offer once again
|
|
std::vector<currency::attachment_v> attachments4;
|
|
bc_services::update_offer uo2 = AUTO_VAL_INIT(uo2);
|
|
fill_default_offer(uo2.of);
|
|
uo2.of.location_city = "LA";
|
|
uo2.offer_index = 0;
|
|
uo2.tx_id = currency::get_transaction_hash(tx);
|
|
//---------------------------
|
|
crypto::public_key related_tx_pub_key = get_tx_pub_key_from_extra(tx);
|
|
crypto::public_key related_tx_offer_pub_key = bc_services::get_offer_secure_key_by_index_from_tx(tx, 0);
|
|
currency::keypair ephemeral;
|
|
r = currency::derive_ephemeral_key_helper(miner_account.get_keys(), related_tx_pub_key, 0, ephemeral);
|
|
CHECK_AND_ASSERT_THROW_MES(r, "Failed to derive_ephemeral_key_helper");
|
|
CHECK_AND_ASSERT_THROW_MES(ephemeral.pub == related_tx_offer_pub_key, "Failed to derive_ephemeral_key_helper(pub keys missmatch)");
|
|
|
|
bl_for_sig = bc_services::make_offer_sig_blob(uo2);
|
|
crypto::generate_signature(crypto::cn_fast_hash(bl_for_sig.data(), bl_for_sig.size()), ephemeral.pub, ephemeral.sec, uo2.sig);
|
|
bc_services::put_offer_into_attachment(uo2, attachments4);
|
|
MAKE_TX_LIST_START_WITH_ATTACHS(events, txs_blk4, miner_account, miner_account, MK_TEST_COINS(1), blk_14, attachments4);
|
|
//offer_secret_key = generator.last_tx_generated_secret_key;
|
|
tx_id_2 = currency::get_transaction_hash(txs_blk4.back());
|
|
MAKE_NEXT_BLOCK_TX_LIST(events, blk_15, blk_14, miner_account, txs_blk4);
|
|
DO_CALLBACK(events, "check_offers_updated_2");
|
|
|
|
|
|
MAKE_NEXT_BLOCK(events, blk_16, blk_15, miner_account);
|
|
return true;
|
|
}
|
|
|
|
bool offers_tests::configure_core(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
|
{
|
|
currency::core_runtime_config pc = c.get_blockchain_storage().get_core_runtime_config();
|
|
pc.min_coinstake_age = TESTS_POS_CONFIG_MIN_COINSTAKE_AGE; //four blocks
|
|
pc.pos_minimum_heigh = TESTS_POS_CONFIG_POS_MINIMUM_HEIGH; //four blocks
|
|
|
|
c.get_blockchain_storage().set_core_runtime_config(pc);
|
|
return true;
|
|
}
|
|
|
|
bool get_of_by_hash(crypto::hash& id, const std::list<bc_services::offer_details_ex>& of_list, bc_services::offer_details_ex& od)
|
|
{
|
|
for (auto& of : of_list)
|
|
{
|
|
if (of.tx_hash == id)
|
|
{
|
|
od = of;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
bool offers_tests::check_offers_1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
|
{
|
|
std::list<bc_services::offer_details_ex> offers;
|
|
|
|
get_all_offers(c, offers);
|
|
CHECK_EQ(2, offers.size());
|
|
return true;
|
|
}
|
|
bool offers_tests::check_offers_updated_1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
|
{
|
|
std::list<bc_services::offer_details_ex> offers;
|
|
|
|
get_all_offers(c, offers);
|
|
CHECK_EQ(2, offers.size());
|
|
|
|
bc_services::offer_details_ex target_offer;
|
|
bool r = get_of_by_hash(tx_id_1, offers, target_offer);
|
|
CHECK_TEST_CONDITION(r);
|
|
|
|
CHECK_EQ(target_offer.location_city, FIRST_UPDATED_LOCATION);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool offers_tests::check_offers_updated_2(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
|
{
|
|
std::list<bc_services::offer_details_ex> offers;
|
|
|
|
get_all_offers(c, offers);
|
|
CHECK_EQ(2, offers.size());
|
|
|
|
bc_services::offer_details_ex target_offer;
|
|
bool r = get_of_by_hash(tx_id_1, offers, target_offer);
|
|
CHECK_TEST_CONDITION(!r);
|
|
r = get_of_by_hash(tx_id_2, offers, target_offer);
|
|
CHECK_TEST_CONDITION(r);
|
|
|
|
CHECK_EQ(target_offer.location_city, SECOND_UPDATED_LOCATION);
|
|
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
bool construct_block_gentime_with_timestamp(test_generator& generator, const currency::block& prev_block, const currency::account_base& acc, uint64_t timestamp, currency::block& b)
|
|
{
|
|
bool r = false;
|
|
crypto::hash prev_id = get_block_hash(prev_block);
|
|
uint64_t already_generated_coins = generator.get_already_generated_coins(prev_block);
|
|
std::vector<size_t> block_sizes;
|
|
generator.get_last_n_block_sizes(block_sizes, prev_id, CURRENCY_REWARD_BLOCKS_WINDOW);
|
|
|
|
size_t height = get_block_height(prev_block) + 1;
|
|
|
|
r = generator.construct_block(b, height, prev_id, acc, timestamp, already_generated_coins, block_sizes, std::list<transaction>());
|
|
CHECK_AND_ASSERT_MES(r, false, "construct_block_manually failed");
|
|
|
|
return true;
|
|
}
|
|
|
|
offers_expiration_test::offers_expiration_test()
|
|
: m_offers_ts(0)
|
|
{
|
|
REGISTER_CALLBACK_METHOD(offers_expiration_test, c1);
|
|
}
|
|
|
|
bool offers_expiration_test::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
// Makes sure that offers are correctly removed from blockchain storage when they're expired by specified time or by general time limit (two weeks for now).
|
|
// Test outline:
|
|
// 1. Create four txs with expiration time set to 0 (max), 1, 2 and 3 days.
|
|
// 2. Rewind time by 2 days, make sure only 2 offers are expired.
|
|
// 3. Rewind time by OFFER_MAXIMUM_LIFE_TIME - 2 days more (to be OFFER_MAXIMUM_LIFE_TIME in total), make sure all offers are expired.
|
|
|
|
GENERATE_ACCOUNT(miner_acc);
|
|
GENERATE_ACCOUNT(alice_acc);
|
|
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 + 2);
|
|
|
|
std::vector<currency::attachment_v> attachment;
|
|
m_offer = AUTO_VAL_INIT(m_offer);
|
|
fill_test_offer(m_offer);
|
|
|
|
bc_services::offer_details od = m_offer;
|
|
|
|
od.expiration_time = 0;
|
|
attachment.clear();
|
|
bc_services::put_offer_into_attachment(od, attachment);
|
|
MAKE_TX_ATTACH(events, tx_0, miner_acc, alice_acc, TX_DEFAULT_FEE, blk_0r, attachment);
|
|
|
|
od.expiration_time = 1;
|
|
attachment.clear();
|
|
bc_services::put_offer_into_attachment(od, attachment);
|
|
MAKE_TX_ATTACH(events, tx_1, miner_acc, alice_acc, TX_DEFAULT_FEE, blk_0r, attachment);
|
|
|
|
od.expiration_time = 2;
|
|
attachment.clear();
|
|
bc_services::put_offer_into_attachment(od, attachment);
|
|
MAKE_TX_ATTACH(events, tx_2, miner_acc, alice_acc, TX_DEFAULT_FEE, blk_0r, attachment);
|
|
|
|
od.expiration_time = 3;
|
|
attachment.clear();
|
|
bc_services::put_offer_into_attachment(od, attachment);
|
|
MAKE_TX_ATTACH(events, tx_3, miner_acc, alice_acc, TX_DEFAULT_FEE, blk_0r, attachment);
|
|
|
|
MAKE_NEXT_BLOCK_TX_LIST(events, blk_1, blk_0r, miner_acc, std::list<transaction>({ tx_0, tx_1, tx_2, tx_3 }));
|
|
m_offers_ts = blk_1.timestamp;
|
|
events.push_back(event_core_time(m_offers_ts));
|
|
|
|
MAKE_NEXT_BLOCK(events, blk_2, blk_1, miner_acc);
|
|
|
|
// rewind time for 2 days
|
|
uint64_t offers_ts_plus_two_days = m_offers_ts + 60 * 60 * 24 * 2 + 1;
|
|
events.push_back(event_core_time(offers_ts_plus_two_days));
|
|
|
|
DO_CALLBACK(events, "c1");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool offers_expiration_test::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
|
{
|
|
bc_services::bc_offers_service* offers_service = dynamic_cast<bc_services::bc_offers_service*>(c.get_blockchain_storage().get_attachment_services_manager().get_service_by_id(BC_OFFERS_SERVICE_ID));
|
|
CHECK_AND_ASSERT_MES(offers_service != nullptr, false, "Offers service was not registered in attachment service manager!");
|
|
|
|
// don't need to trim_offers() here because get_all_offers() returns offers filtered by expiration_time
|
|
// offers_service->trim_offers();
|
|
|
|
std::list<bc_services::offer_details_ex> offers;
|
|
bool r = get_all_offers(c, offers);
|
|
CHECK_AND_ASSERT_MES(r, false, "get_all_offers failed");
|
|
CHECK_V_EQ_EXPECTED_AND_ASSERT(offers.size(), 2);
|
|
|
|
bc_services::offer_details_ex offer_0 = offers.front(), offer_3 = offers.back();
|
|
if (offers.front().expiration_time != 0)
|
|
{
|
|
offer_0 = offers.back();
|
|
offer_3 = offers.front();
|
|
}
|
|
CHECK_AND_ASSERT_MES(offer_0.expiration_time == 0 && offer_3.expiration_time == 3, false, "Incorrect offers expiration time left");
|
|
r = compare_offers(m_offer, offer_0, default_offer_skip_field_mask | offers_fields::offer_field_expiration_time);
|
|
CHECK_AND_ASSERT_MES(r, false, "offer_0 obtained via bc_service differs from original");
|
|
r = compare_offers(m_offer, offer_3, default_offer_skip_field_mask | offers_fields::offer_field_expiration_time);
|
|
CHECK_AND_ASSERT_MES(r, false, "offer_3 obtained via bc_service differs from original");
|
|
|
|
// rewind time very close to the offers' lifetime limit
|
|
test_core_time::adjust(m_offers_ts + OFFER_MAXIMUM_LIFE_TIME - 1);
|
|
|
|
offers_service->trim_offers();
|
|
// trim_offers should still has no effect as all offers' are within their allowed lifetime (OFFER_MAXIMUM_LIFE_TIME)
|
|
CHECK_V_EQ_EXPECTED_AND_ASSERT(offers_service->get_offers_container().size(), 4);
|
|
|
|
offers.clear();
|
|
r = get_all_offers(c, offers);
|
|
CHECK_AND_ASSERT_MES(r, false, "get_all_offers failed");
|
|
// get_all_offers should returns only 1 offer -- the one with 0 (max) expiration time
|
|
CHECK_V_EQ_EXPECTED_AND_ASSERT(offers.size(), 1);
|
|
CHECK_V_EQ_EXPECTED_AND_ASSERT(offers.front().expiration_time, 0);
|
|
|
|
|
|
// rewind time beyond the offers' lifetime limit
|
|
test_core_time::adjust(m_offers_ts + OFFER_MAXIMUM_LIFE_TIME + 1);
|
|
|
|
offers_service->trim_offers();
|
|
// no offers should left in the backend service
|
|
CHECK_V_EQ_EXPECTED_AND_ASSERT(offers_service->get_offers_container().size(), 0);
|
|
|
|
offers.clear();
|
|
r = get_all_offers(c, offers);
|
|
CHECK_AND_ASSERT_MES(r, false, "get_all_offers failed");
|
|
// no offers should be visible via get_all_offers()
|
|
CHECK_V_EQ_EXPECTED_AND_ASSERT(offers.size(), 0);
|
|
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
offers_filtering_1::offers_filtering_1()
|
|
{
|
|
REGISTER_CALLBACK_METHOD(offers_filtering_1, c1);
|
|
|
|
test_gentime_settings s = test_generator::get_test_gentime_settings();
|
|
s.tx_max_out_amount = PREMINE_AMOUNT; // use very high max allowed output amount to avoid splitting premine to countless outputs and slowing down the test to the death
|
|
s.miner_tx_max_outs = 11; // limit genesis coinbase outs count for the same reason
|
|
test_generator::set_test_gentime_settings(s);
|
|
}
|
|
|
|
bool offers_filtering_1::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
GENERATE_ACCOUNT(preminer_acc);
|
|
GENERATE_ACCOUNT(miner_acc);
|
|
MAKE_GENESIS_BLOCK(events, blk_0, preminer_acc, test_core_time::get_time());
|
|
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 4);
|
|
|
|
std::list<transaction> txs;
|
|
size_t count = 0;
|
|
for (; count < offers_count / 2; ++count)
|
|
{
|
|
bc_services::offer_details od = AUTO_VAL_INIT(od);
|
|
fill_test_offer(od);
|
|
std::vector<currency::attachment_v> attachment;
|
|
bc_services::put_offer_into_attachment(od, attachment);
|
|
MAKE_TX_LIST_ATTACH(events, txs, miner_acc, miner_acc, TX_DEFAULT_FEE, blk_0r, attachment);
|
|
}
|
|
MAKE_NEXT_BLOCK_TX_LIST(events, blk_1, blk_0r, miner_acc, txs);
|
|
|
|
txs.clear();
|
|
for (; count < offers_count; ++count)
|
|
{
|
|
bc_services::offer_details od = AUTO_VAL_INIT(od);
|
|
fill_test_offer(od);
|
|
std::vector<currency::attachment_v> attachment;
|
|
bc_services::put_offer_into_attachment(od, attachment);
|
|
MAKE_TX_LIST_ATTACH(events, txs, miner_acc, miner_acc, TX_DEFAULT_FEE, blk_1, attachment);
|
|
}
|
|
MAKE_NEXT_BLOCK_TX_LIST(events, blk_2, blk_1, miner_acc, txs);
|
|
|
|
|
|
DO_CALLBACK(events, "c1");
|
|
|
|
return true;
|
|
}
|
|
|
|
void _dbg_print_offer_list(const std::list<bc_services::offer_details_ex>& ol)
|
|
{
|
|
int i = 0;
|
|
for (auto o : ol)
|
|
{
|
|
LOG_PRINT_L0(
|
|
i++ << ENDL <<
|
|
" amount_primary: " << o.amount_primary << ENDL <<
|
|
" amount_target: " << o.amount_target << ENDL <<
|
|
" rate: " << static_cast<double>(o.amount_primary) / o.amount_target << ENDL <<
|
|
" timestamp: " << o.timestamp);
|
|
}
|
|
}
|
|
|
|
bool check_offers_sorting(bool reverse, uint64_t order_by, const char* field_name, const std::list<bc_services::offer_details_ex>& original_list, //field_t (*field_selector) (const bc_services::offer_details_ex&),
|
|
bool (*field_cmp) (const bc_services::offer_details_ex&, const bc_services::offer_details_ex&), currency::core& c)
|
|
{
|
|
std::list<bc_services::offer_details_ex> offers_sorted(original_list);
|
|
bc_services::core_offers_filter filter = AUTO_VAL_INIT(filter);
|
|
filter.limit = UINT64_MAX;
|
|
filter.order_by = order_by;
|
|
filter.reverse = reverse;
|
|
bool r = bc_services::filter_offers_list(offers_sorted, filter, c.get_blockchain_storage().get_core_runtime_config().get_core_time());
|
|
CHECK_AND_FORCE_ASSERT_MES(r, false, "filter_offers_list failed");
|
|
CHECK_AND_FORCE_ASSERT_MES(offers_sorted.size() == offers_filtering_1::offers_count, false, "filter_offers_list returned wrong offer list");
|
|
|
|
//_dbg_print_offer_list(original_list);
|
|
//_dbg_print_offer_list(offers_sorted);
|
|
|
|
const bc_services::offer_details_ex* prev_offer = nullptr;
|
|
for (const bc_services::offer_details_ex& o : offers_sorted)
|
|
{
|
|
CHECK_AND_ASSERT_MES(prev_offer == nullptr || field_cmp(*prev_offer, o), false, "Offers list was not sorted by " << field_name);
|
|
prev_offer = &o;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool check_offers_limits(const std::list<bc_services::offer_details_ex>& original_list, uint64_t offset, uint64_t limit, uint64_t expect, currency::core& c)
|
|
{
|
|
std::list<bc_services::offer_details_ex> offers_sorted(original_list);
|
|
bc_services::core_offers_filter filter = AUTO_VAL_INIT(filter);
|
|
filter.offset = offset;
|
|
filter.limit = limit;
|
|
filter.order_by = ORDER_BY_TIMESTAMP;
|
|
filter.reverse = false;
|
|
bool r = bc_services::filter_offers_list(offers_sorted, filter, c.get_blockchain_storage().get_core_runtime_config().get_core_time());
|
|
CHECK_AND_FORCE_ASSERT_MES(r, false, "filter_offers_list failed");
|
|
CHECK_AND_FORCE_ASSERT_MES(offers_sorted.size() == expect, false, "filter_offers_list returned wrong size of offer list: " << offers_sorted.size() << " for offset: " << offset << ", limit: " << limit << ", expect: " << expect);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool offers_filtering_1::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
|
{
|
|
bc_services::bc_offers_service* offers_service = dynamic_cast<bc_services::bc_offers_service*>(c.get_blockchain_storage().get_attachment_services_manager().get_service_by_id(BC_OFFERS_SERVICE_ID));
|
|
CHECK_AND_ASSERT_MES(offers_service != nullptr, false, "Offers service was not registered in attachment service manager!");
|
|
|
|
std::list<bc_services::offer_details_ex> offers;
|
|
bool r = get_all_offers(c, offers);
|
|
CHECK_AND_FORCE_ASSERT_MES(r, false, "get_all_offers failed");
|
|
CHECK_AND_FORCE_ASSERT_MES(offers.size() == offers_count, false, "Incorrect offers count");
|
|
|
|
#define CHECK_SORTING(field, enum_val) \
|
|
r &= check_offers_sorting(false, enum_val, #field, offers, [](const bc_services::offer_details_ex& a, const bc_services::offer_details_ex& b)->bool { return a.field <= b.field; }, c); \
|
|
r &= check_offers_sorting(true, enum_val, #field, offers, [](const bc_services::offer_details_ex& a, const bc_services::offer_details_ex& b)->bool { return a.field >= b.field; }, c)
|
|
|
|
CHECK_SORTING(timestamp, ORDER_BY_TIMESTAMP);
|
|
CHECK_SORTING(amount_primary, ORDER_BY_AMOUNT_PRIMARY);
|
|
CHECK_SORTING(amount_target, ORDER_BY_AMOUNT_TARGET);
|
|
|
|
// amount_rate requires custom comparator
|
|
r &= check_offers_sorting(false, ORDER_BY_AMOUNT_RATE, "amount_rate", offers,
|
|
[](const bc_services::offer_details_ex& a, const bc_services::offer_details_ex& b)->bool { return bc_services::calculate_offer_rate(a) <= bc_services::calculate_offer_rate(b); },
|
|
c);
|
|
r &= check_offers_sorting(true, ORDER_BY_AMOUNT_RATE, "amount_rate", offers,
|
|
[](const bc_services::offer_details_ex& a, const bc_services::offer_details_ex& b)->bool { return bc_services::calculate_offer_rate(a) >= bc_services::calculate_offer_rate(b); },
|
|
c);
|
|
|
|
CHECK_SORTING(payment_types, ORDER_BY_PAYMENT_TYPES);
|
|
CHECK_SORTING(contacts, ORDER_BY_CONTACTS);
|
|
CHECK_SORTING(location_city, ORDER_BY_LOCATION);
|
|
|
|
r &= check_offers_sorting(false, ORDER_BY_NAME, "target", offers,
|
|
[](const bc_services::offer_details_ex& a, const bc_services::offer_details_ex& b)->bool { return utf8_to_lower(a.target) <= utf8_to_lower(b.target); },
|
|
c);
|
|
|
|
uint64_t oles[][3] =
|
|
// offset limit expect
|
|
{{0, offers_filtering_1::offers_count, offers_filtering_1::offers_count},
|
|
{1, offers_filtering_1::offers_count, offers_filtering_1::offers_count-1},
|
|
{offers_filtering_1::offers_count-1, offers_filtering_1::offers_count, 1},
|
|
{offers_filtering_1::offers_count, offers_filtering_1::offers_count, 0},
|
|
{offers_filtering_1::offers_count+1, offers_filtering_1::offers_count, 0},
|
|
{0, offers_filtering_1::offers_count+1, offers_filtering_1::offers_count},
|
|
{0, offers_filtering_1::offers_count-1, offers_filtering_1::offers_count-1},
|
|
{1, offers_filtering_1::offers_count-1, offers_filtering_1::offers_count-1},
|
|
{1, offers_filtering_1::offers_count-2, offers_filtering_1::offers_count-2},
|
|
{0, 0, 0}
|
|
};
|
|
|
|
for (auto& el : oles)
|
|
{
|
|
r &= check_offers_limits(offers, el[0], el[1], el[2], c);
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
offers_handling_on_chain_switching::offers_handling_on_chain_switching()
|
|
{
|
|
REGISTER_CALLBACK_METHOD(offers_handling_on_chain_switching, c1);
|
|
}
|
|
|
|
bool offers_handling_on_chain_switching::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
m_accounts.resize(TOTAL_ACCS_COUNT);
|
|
auto& miner_acc = m_accounts[MINER_ACC_IDX];
|
|
miner_acc.generate();
|
|
auto& alice_acc = m_accounts[ALICE_ACC_IDX];
|
|
alice_acc.generate();
|
|
// event index
|
|
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time()); // 0
|
|
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); // 2N (N == CURRENCY_MINED_MONEY_UNLOCK_WINDOW)
|
|
|
|
MAKE_TX(events, tx_0, miner_acc, alice_acc, TX_DEFAULT_FEE * 70, blk_0r); // 2N+1
|
|
MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_acc, tx_0); // 2N+2
|
|
|
|
REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); // 4N+2
|
|
|
|
bc_services::offer_details od = AUTO_VAL_INIT(od);
|
|
fill_test_offer(od);
|
|
std::vector<currency::attachment_v> attachment;
|
|
bc_services::put_offer_into_attachment(od, attachment);
|
|
MAKE_TX_ATTACH(events, tx_1, alice_acc, alice_acc, TX_DEFAULT_FEE, blk_1r, attachment); // 4N+3
|
|
|
|
MAKE_NEXT_BLOCK(events, blk_2, blk_1r, miner_acc); // 4N+4
|
|
//MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1r, miner_acc, tx_1); // 4N+4
|
|
|
|
DO_CALLBACK(events, "c1"); // 4N+5
|
|
|
|
return true;
|
|
}
|
|
|
|
bool offers_handling_on_chain_switching::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
|
{
|
|
bool r = false, bool_stub = false;
|
|
|
|
// there's one tx with an offer in the pool
|
|
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
|
|
CHECK_AND_ASSERT_MES(c.get_alternative_blocks_count() == 0, false, "Incorrect alt blocks count: " << c.get_alternative_blocks_count());
|
|
|
|
// check Alice's wallet against offers, there should not be a one
|
|
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, m_accounts[ALICE_ACC_IDX]);
|
|
alice_wlt->refresh();
|
|
alice_wlt->scan_tx_pool(bool_stub);
|
|
std::list<bc_services::offer_details_ex> offers;
|
|
r = alice_wlt->get_actual_offers(offers);
|
|
CHECK_AND_ASSERT_MES(r, false, "get_actual_offers failed");
|
|
CHECK_AND_ASSERT_MES(offers.size() == 0, false, "incorrect offers.size() = " << offers.size());
|
|
|
|
|
|
// mine first alt block (no txs)
|
|
uint64_t blk_1r_height = c.get_current_blockchain_size() - 2;
|
|
crypto::hash blk_1r_id = c.get_block_id_by_height(blk_1r_height);
|
|
block blk_2a = AUTO_VAL_INIT(blk_2a);
|
|
r = mine_next_pow_block_in_playtime_with_given_txs(m_scratchpad_keeper, m_accounts[MINER_ACC_IDX].get_public_address(), c, std::vector<transaction>(), blk_1r_id, blk_1r_height + 1, &blk_2a);
|
|
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime_with_given_txs failed");
|
|
|
|
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
|
|
CHECK_AND_ASSERT_MES(c.get_alternative_blocks_count() == 1, false, "Incorrect alt blocks count: " << c.get_alternative_blocks_count());
|
|
|
|
const transaction& tx_1 = boost::get<transaction>(events[4 * CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 3]);
|
|
|
|
// mine second alt block (include offer tx) -- this should trigger chain switching
|
|
block blk_3a = AUTO_VAL_INIT(blk_3a);
|
|
r = mine_next_pow_block_in_playtime_with_given_txs(m_scratchpad_keeper, m_accounts[MINER_ACC_IDX].get_public_address(), c, std::vector<transaction>({tx_1}), get_block_hash(blk_2a), blk_1r_height + 2, &blk_3a);
|
|
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime_with_given_txs failed");
|
|
|
|
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
|
|
CHECK_AND_ASSERT_MES(c.get_alternative_blocks_count() == 1, false, "Incorrect alt blocks count: " << c.get_alternative_blocks_count());
|
|
|
|
// check the wallet for offers, we should have an offer now
|
|
alice_wlt->refresh();
|
|
alice_wlt->scan_tx_pool(bool_stub);
|
|
offers.clear();
|
|
r = alice_wlt->get_actual_offers(offers);
|
|
CHECK_AND_ASSERT_MES(r, false, "get_actual_offers failed");
|
|
CHECK_AND_ASSERT_MES(offers.size() == 1, false, "incorrect offers.size() = " << offers.size());
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
offer_removing_and_selected_output::offer_removing_and_selected_output()
|
|
{
|
|
REGISTER_CALLBACK_METHOD(offer_removing_and_selected_output, check_offers);
|
|
}
|
|
|
|
bool offer_removing_and_selected_output::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
// Test idea: remove two offers and make sure in both cases a transfer with minimal amount is used for that "free" operation
|
|
|
|
bool r = false;
|
|
|
|
m_accounts.resize(TOTAL_ACCS_COUNT);
|
|
auto& miner_acc = m_accounts[MINER_ACC_IDX];
|
|
miner_acc.generate();
|
|
auto& alice_acc = m_accounts[ALICE_ACC_IDX];
|
|
alice_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);
|
|
|
|
// prepare different outputs for Alice
|
|
transaction tx_0 = AUTO_VAL_INIT(tx_0);
|
|
std::vector<tx_destination_entry> destinations;
|
|
destinations.push_back(tx_destination_entry(MK_TEST_COINS(4), alice_acc.get_public_address()));
|
|
destinations.push_back(tx_destination_entry(MK_TEST_COINS(8), alice_acc.get_public_address()));
|
|
destinations.push_back(tx_destination_entry(MK_TEST_COINS(1) / 5, alice_acc.get_public_address())); // <- min #2
|
|
destinations.push_back(tx_destination_entry(MK_TEST_COINS(3), alice_acc.get_public_address()));
|
|
destinations.push_back(tx_destination_entry(MK_TEST_COINS(1) / 10, alice_acc.get_public_address())); // <- min #1
|
|
destinations.push_back(tx_destination_entry(MK_TEST_COINS(2), alice_acc.get_public_address()));
|
|
destinations.push_back(tx_destination_entry(MK_TEST_COINS(5), alice_acc.get_public_address()));
|
|
uint64_t destinations_amount = 0;
|
|
for(auto& d : destinations)
|
|
destinations_amount += d.amount;
|
|
std::vector<tx_source_entry> sources;
|
|
r = fill_tx_sources(sources, events, blk_0r, miner_acc.get_keys(), destinations_amount + TESTS_DEFAULT_FEE, 0);
|
|
CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources failed");
|
|
r = construct_tx(miner_acc.get_keys(), sources, destinations, empty_attachment, tx_0, 0);
|
|
CHECK_AND_ASSERT_MES(r, false, "construct_tx failed");
|
|
events.push_back(tx_0);
|
|
MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_acc, tx_0);
|
|
|
|
REWIND_BLOCKS_N(events, blk_1r, blk_1, miner_acc, WALLET_DEFAULT_TX_SPENDABLE_AGE);
|
|
|
|
uint64_t offer_cancel_tx_selected_amount = MK_TEST_COINS(1) / 10; // min #1
|
|
DO_CALLBACK_PARAMS(events, "check_offers", offer_cancel_tx_selected_amount);
|
|
|
|
offer_cancel_tx_selected_amount = MK_TEST_COINS(1) / 5; // min #2
|
|
DO_CALLBACK_PARAMS(events, "check_offers", offer_cancel_tx_selected_amount);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool offer_removing_and_selected_output::check_offers(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
|
{
|
|
bool r = false;
|
|
uint64_t offer_cancel_tx_selected_amount = 0;
|
|
r = epee::string_tools::hex_to_pod(boost::get<callback_entry>(events[ev_index]).callback_params, offer_cancel_tx_selected_amount);
|
|
CHECK_AND_ASSERT_MES(r && offer_cancel_tx_selected_amount > 0, false, "hex_to_pod failed");
|
|
|
|
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, m_accounts[ALICE_ACC_IDX]);
|
|
alice_wlt->refresh();
|
|
LOG_PRINT_CYAN("Alice's transfers:" << ENDL << alice_wlt->dump_trunsfers(), LOG_LEVEL_0);
|
|
uint64_t alice_start_balance = alice_wlt->balance();
|
|
|
|
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
|
|
|
|
bc_services::offer_details_ex od = AUTO_VAL_INIT(od);
|
|
fill_test_offer(od);
|
|
od.fee = TESTS_DEFAULT_FEE;
|
|
|
|
transaction create_offer_tx = AUTO_VAL_INIT(create_offer_tx);
|
|
LOG_PRINT_CYAN("%%%%% alice_wlt->push_offer()", LOG_LEVEL_0);
|
|
alice_wlt->push_offer(od, create_offer_tx);
|
|
crypto::hash create_offer_tx_id = get_transaction_hash(create_offer_tx);
|
|
|
|
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
|
|
CHECK_AND_ASSERT_MES(mine_next_pow_block_in_playtime(m_scratchpad_keeper, m_accounts[MINER_ACC_IDX].get_public_address(), c), 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());
|
|
|
|
refresh_wallet_and_check_balance("offer's put into the blockchain", "Alice", alice_wlt, alice_start_balance - od.fee, true, 1);
|
|
|
|
// check offer, retured by get_actual_offers()
|
|
std::list<bc_services::offer_details_ex> offers;
|
|
r = alice_wlt->get_actual_offers(offers);
|
|
CHECK_AND_ASSERT_MES(r && offers.size() == 1, false, "alice_wlt->get_actual_offers failed, offers.size() == " << offers.size());
|
|
CHECK_AND_ASSERT_MES(compare_offers(od, offers.front()), false, "");
|
|
|
|
// cancel offer
|
|
transaction cancel_offer_tx = AUTO_VAL_INIT(cancel_offer_tx);
|
|
LOG_PRINT_CYAN("%%%%% alice_wlt->cancel_offer_by_id()", LOG_LEVEL_0);
|
|
alice_wlt->cancel_offer_by_id(create_offer_tx_id, 0, cancel_offer_tx);
|
|
|
|
// make sure used input is the expected one
|
|
CHECK_AND_ASSERT_MES(cancel_offer_tx.vin.size() == 1, false, "cancel_offer_tx.vin.size() == " << cancel_offer_tx.vin.size());
|
|
CHECKED_GET_SPECIFIC_VARIANT(cancel_offer_tx.vin[0], txin_to_key, in, false);
|
|
CHECK_AND_ASSERT_MES(in.amount == offer_cancel_tx_selected_amount, false, "Offer's cancellation tx uses input with amount " << print_money_brief(in.amount) << ", while expected amount to be used is: " << print_money_brief(offer_cancel_tx_selected_amount));
|
|
|
|
// make sure offer's cancellation tx has zero fee
|
|
CHECK_AND_ASSERT_MES(get_tx_fee(cancel_offer_tx) == 0, false, "get_tx_fee(cancel_offer_tx) = " << get_tx_fee(cancel_offer_tx));
|
|
|
|
// add it to the blockchain
|
|
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
|
|
CHECK_AND_ASSERT_MES(mine_next_pow_block_in_playtime(m_scratchpad_keeper, m_accounts[MINER_ACC_IDX].get_public_address(), c), 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's banace should not change
|
|
refresh_wallet_and_check_balance("offer has been cancelled, ", "Alice", alice_wlt, alice_start_balance - od.fee, true, 1);
|
|
|
|
// get_actual_offers should return empty list
|
|
offers.clear();
|
|
r = alice_wlt->get_actual_offers(offers);
|
|
CHECK_AND_ASSERT_MES(r && offers.size() == 0, false, "alice_wlt->get_actual_offers failed, offers.size() = " << offers.size());
|
|
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
offers_and_stuck_txs::offers_and_stuck_txs()
|
|
{
|
|
REGISTER_CALLBACK_METHOD(offers_and_stuck_txs, c1);
|
|
REGISTER_CALLBACK_METHOD(offers_and_stuck_txs, check_offers);
|
|
}
|
|
|
|
bool offers_and_stuck_txs::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
bool r = false;
|
|
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();
|
|
|
|
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
|
|
REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
|
|
|
|
// add an offer -- tx_1
|
|
bc_services::offer_details od = AUTO_VAL_INIT(od);
|
|
fill_test_offer(od);
|
|
od.comment = "new";
|
|
std::vector<attachment_v> attachments;
|
|
bc_services::put_offer_into_attachment(od, attachments);
|
|
MAKE_TX_ATTACH(events, tx_1, miner_acc, miner_acc, TESTS_DEFAULT_FEE, blk_0r, attachments);
|
|
MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_acc, tx_1);
|
|
|
|
|
|
// make tx deleting the offer -- tx_2, don't add it to the blockchain
|
|
bc_services::cancel_offer co = AUTO_VAL_INIT(co);
|
|
co.tx_id = get_transaction_hash(tx_1);
|
|
co.offer_index = 0;
|
|
r = sign_an_offer(tx_1, co.offer_index, miner_acc.get_keys(), bc_services::make_offer_sig_blob(co), co.sig);
|
|
CHECK_AND_ASSERT_MES(r, false, "sign_an_offer failed");
|
|
attachments.clear();
|
|
bc_services::put_offer_into_attachment(co, attachments);
|
|
transaction tx_2 = AUTO_VAL_INIT(tx_2);
|
|
uint64_t fee = 7 * TESTS_DEFAULT_FEE;
|
|
r = construct_tx_to_key(events, tx_2, blk_1, miner_acc, miner_acc, TESTS_DEFAULT_FEE, fee, 0, 0, empty_extra, attachments);
|
|
CHECK_AND_ASSERT_MES(r, false, "construct_tx_to_key failed");
|
|
events.push_back(tx_2);
|
|
|
|
CHECK_AND_ASSERT_MES(get_tx_fee(tx_2) == fee, false, "Incorrect fee: " << print_money_brief(get_tx_fee(tx_2)) << ", expected: " << print_money_brief(fee));
|
|
|
|
// don't include tx_2 with offer deletion yet
|
|
MAKE_NEXT_BLOCK(events, blk_2, blk_1, miner_acc);
|
|
|
|
size_t expected_tx_count = 1;
|
|
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", expected_tx_count);
|
|
DO_CALLBACK(events, "print_tx_pool");
|
|
|
|
// make tx updating the offer
|
|
bc_services::update_offer uo = AUTO_VAL_INIT(uo);
|
|
uo.tx_id = get_transaction_hash(tx_1);
|
|
uo.offer_index = 0;
|
|
uo.of = od;
|
|
uo.of.comment = "update";
|
|
r = sign_an_offer(tx_1, co.offer_index, miner_acc.get_keys(), bc_services::make_offer_sig_blob(uo), uo.sig);
|
|
CHECK_AND_ASSERT_MES(r, false, "sign_an_offer failed");
|
|
attachments.clear();
|
|
bc_services::put_offer_into_attachment(uo, attachments);
|
|
transaction tx_3 = AUTO_VAL_INIT(tx_3);
|
|
fee = 90 * TESTS_DEFAULT_FEE;
|
|
r = construct_tx_to_key(events, tx_3, blk_2, miner_acc, miner_acc, TESTS_DEFAULT_FEE, fee, 0, 0, empty_extra, attachments);
|
|
CHECK_AND_ASSERT_MES(r, false, "construct_tx_to_key failed");
|
|
events.push_back(tx_3);
|
|
|
|
CHECK_AND_ASSERT_MES(get_tx_fee(tx_3) == fee, false, "Incorrect fee: " << print_money_brief(get_tx_fee(tx_3)) << ", expected: " << print_money_brief(fee));
|
|
|
|
expected_tx_count = 2;
|
|
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", expected_tx_count);
|
|
DO_CALLBACK(events, "print_tx_pool");
|
|
|
|
DO_CALLBACK(events, "c1");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool offers_and_stuck_txs::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool offers_and_stuck_txs::check_offers(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
offers_updating_hack::offers_updating_hack()
|
|
{
|
|
REGISTER_CALLBACK_METHOD(offers_updating_hack, update_foreign_offer);
|
|
REGISTER_CALLBACK_METHOD(offers_updating_hack, delete_foreign_offer);
|
|
}
|
|
|
|
bool offers_updating_hack::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
bool r = false;
|
|
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(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 2);
|
|
|
|
transaction tx_a = AUTO_VAL_INIT(tx_a);
|
|
r = construct_tx_with_many_outputs(events, blk_0r, miner_acc.get_keys(), alice_acc.get_public_address(), TESTS_DEFAULT_FEE * 1000, 10, TESTS_DEFAULT_FEE, tx_a);
|
|
CHECK_AND_ASSERT_MES(r, false, "construct_tx_with_many_outputs failed");
|
|
events.push_back(tx_a);
|
|
transaction tx_b = AUTO_VAL_INIT(tx_b);
|
|
r = construct_tx_with_many_outputs(events, blk_0r, miner_acc.get_keys(), bob_acc.get_public_address(), TESTS_DEFAULT_FEE * 1000, 10, TESTS_DEFAULT_FEE, tx_b);
|
|
CHECK_AND_ASSERT_MES(r, false, "construct_tx_with_many_outputs failed");
|
|
events.push_back(tx_b);
|
|
MAKE_NEXT_BLOCK_TX_LIST(events, blk_1, blk_0r, miner_acc, std::list<transaction>({ tx_a, tx_b }));
|
|
REWIND_BLOCKS_N(events, blk_1r, blk_1, miner_acc, WALLET_DEFAULT_TX_SPENDABLE_AGE);
|
|
|
|
// Alice adds and offer
|
|
bc_services::offer_details od = AUTO_VAL_INIT(od);
|
|
fill_test_offer(od);
|
|
od.comment = "new";
|
|
std::vector<attachment_v> attachments;
|
|
bc_services::put_offer_into_attachment(od, attachments);
|
|
MAKE_TX_ATTACH(events, tx_1, alice_acc, alice_acc, TESTS_DEFAULT_FEE, blk_1r, attachments);
|
|
MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1r, miner_acc, tx_1);
|
|
|
|
DO_CALLBACK(events, "check_tx_pool_empty");
|
|
|
|
// Alice updates the offer
|
|
bc_services::update_offer uo = AUTO_VAL_INIT(uo);
|
|
uo.tx_id = get_transaction_hash(tx_1);
|
|
uo.offer_index = 0;
|
|
uo.of = od;
|
|
uo.of.comment = "update by Alice";
|
|
r = sign_an_offer(tx_1, uo.offer_index, alice_acc.get_keys(), bc_services::make_offer_sig_blob(uo), uo.sig);
|
|
CHECK_AND_ASSERT_MES(r, false, "sign_an_offer failed");
|
|
attachments.clear();
|
|
bc_services::put_offer_into_attachment(uo, attachments);
|
|
transaction tx_2 = AUTO_VAL_INIT(tx_2);
|
|
uint64_t fee = 90 * TESTS_DEFAULT_FEE;
|
|
r = construct_tx_to_key(events, tx_2, blk_2, alice_acc, alice_acc, TESTS_DEFAULT_FEE, fee, 0, 0, empty_extra, attachments);
|
|
CHECK_AND_ASSERT_MES(r, false, "construct_tx_to_key failed");
|
|
events.push_back(tx_2);
|
|
CHECK_AND_ASSERT_MES(get_tx_fee(tx_2) == fee, false, "Incorrect fee: " << print_money_brief(get_tx_fee(tx_2)) << ", expected: " << print_money_brief(fee));
|
|
// don't add tx_2 to the blockchain yet
|
|
|
|
|
|
// ======= Case 1
|
|
DO_CALLBACK(events, "update_foreign_offer");
|
|
|
|
// ======= Prepare for case 2
|
|
// these block should cause chain switching
|
|
MAKE_NEXT_BLOCK(events, blk_3a, blk_2, miner_acc);
|
|
MAKE_NEXT_BLOCK(events, blk_4a, blk_3a, miner_acc);
|
|
MAKE_NEXT_BLOCK(events, blk_5a, blk_4a, miner_acc);
|
|
MAKE_NEXT_BLOCK(events, blk_6a, blk_5a, miner_acc);
|
|
DO_CALLBACK(events, "clear_tx_pool"); // remove alt txs
|
|
events.push_back(tx_2); // put tx_2 into the pool back
|
|
size_t expected_tx_count = 1;
|
|
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", expected_tx_count);
|
|
|
|
DO_CALLBACK_PARAMS(events, "check_top_block", params_top_block(get_block_height(blk_6a), get_block_hash(blk_6a)));
|
|
|
|
// ======= Case 2
|
|
DO_CALLBACK(events, "delete_foreign_offer");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool offers_updating_hack::update_foreign_offer(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
|
{
|
|
bool r = false;
|
|
bc_services::bc_offers_service* offers_service = dynamic_cast<bc_services::bc_offers_service*>(c.get_blockchain_storage().get_attachment_services_manager().get_service_by_id(BC_OFFERS_SERVICE_ID));
|
|
CHECK_AND_ASSERT_MES(offers_service != nullptr, false, "Offers service was not registered in attachment service manager!");
|
|
|
|
//
|
|
// Attacker's part -- modify Alice's offers without Alice's private keys
|
|
//
|
|
|
|
// get offer update tx from the pool (it's not added yet to the blockchain)
|
|
std::list<transaction> txs;
|
|
CHECK_AND_ASSERT_MES(c.get_pool_transactions(txs), false, "");
|
|
CHECK_AND_ASSERT_MES(txs.size() == 1, false, "Incorrect tx pool count: " << txs.size());
|
|
LOG_PRINT_L0(ENDL << c.get_tx_pool().print_pool(true));
|
|
|
|
const transaction& offer_update_tx = txs.back();
|
|
tx_service_attachment tsa = AUTO_VAL_INIT(tsa);
|
|
r = get_type_in_variant_container(offer_update_tx.attachment, tsa);
|
|
CHECK_AND_ASSERT_MES(r, false, "get_type_in_variant_container failed");
|
|
CHECK_AND_ASSERT_MES(tsa.service_id == BC_OFFERS_SERVICE_ID && tsa.instruction == BC_OFFERS_SERVICE_INSTRUCTION_UPD, false, "invalid tsa: " << tsa.service_id << ", " << tsa.instruction);
|
|
|
|
// unpack tsa with update offer
|
|
bc_services::update_offer uo = AUTO_VAL_INIT(uo);
|
|
std::string json_buff;
|
|
CHECK_AND_ASSERT_MES(epee::zlib_helper::unpack(tsa.body, json_buff), false, "");
|
|
CHECK_AND_ASSERT_MES(epee::serialization::load_t_from_json(uo, json_buff), false, "");
|
|
|
|
// Case 1: Bob modifies Alice's offer
|
|
|
|
// modify an offer and don't resign
|
|
uo.of.comment = "update by Bob";
|
|
|
|
// repack
|
|
std::vector<attachment_v> attachments;
|
|
bc_services::put_offer_into_attachment(uo, attachments);
|
|
|
|
std::shared_ptr<tools::wallet2> bob_wlt = init_playtime_test_wallet(events, c, m_accounts[BOB_ACC_IDX]);
|
|
bob_wlt->refresh();
|
|
|
|
// make a tx with this attachment (note: tsa.security is signed using Bob's keys, can be resign later using Alice's public address)
|
|
transaction tx = AUTO_VAL_INIT(tx);
|
|
bob_wlt->transfer(std::vector<tx_destination_entry>({ tx_destination_entry(TESTS_DEFAULT_FEE * 3, m_accounts[BOB_ACC_IDX].get_public_address()) }), 0, 0, TESTS_DEFAULT_FEE * 81, empty_extra, attachments,
|
|
tools::detail::ssi_digit, tools::tx_dust_policy(DEFAULT_DUST_THRESHOLD), tx, 0, true, 0, false);
|
|
|
|
// don't resign yet
|
|
|
|
// send to network
|
|
tx_verification_context tvc = AUTO_VAL_INIT(tvc);
|
|
r = c.handle_incoming_tx(tx_to_blob(tx), tvc, false);
|
|
CHECK_AND_ASSERT_MES(r && tvc.m_added_to_pool, false, "handle_incoming_tx failed: r = " << r << ", tvc.m_added_to_pool = " << tvc.m_added_to_pool);
|
|
LOG_PRINT_L0(ENDL << c.get_tx_pool().print_pool(true));
|
|
|
|
// put in a block 'tx', leave Alice's tx in the pool
|
|
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 2, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
|
|
CHECK_AND_ASSERT_MES(mine_next_pow_block_in_playtime_with_given_txs(m_scratchpad_keeper, m_accounts[MINER_ACC_IDX].get_public_address(), c, std::vector<transaction>({ tx })), false, "mine_next_pow_block_in_playtime failed");
|
|
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
|
|
LOG_PRINT_L0(ENDL << c.get_tx_pool().print_pool(true));
|
|
|
|
// check offers
|
|
|
|
std::list<bc_services::offer_details_ex> offers;
|
|
r = get_all_offers(c, offers);
|
|
CHECK_AND_ASSERT_MES(r, false, "get_all_offers failed");
|
|
|
|
CHECK_AND_ASSERT_MES(offers.size() == 1, false, "Incorrect offers count: " << offers.size());
|
|
bc_services::offer_details_ex offer_0 = offers.front();
|
|
|
|
LOG_PRINT_YELLOW("\n\n!!!!!!!!!!!! offer_0.comment: " << offer_0.comment << "\n\n", LOG_LEVEL_0);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool offers_updating_hack::delete_foreign_offer(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
|
{
|
|
bool r = false;
|
|
bc_services::bc_offers_service* offers_service = dynamic_cast<bc_services::bc_offers_service*>(c.get_blockchain_storage().get_attachment_services_manager().get_service_by_id(BC_OFFERS_SERVICE_ID));
|
|
CHECK_AND_ASSERT_MES(offers_service != nullptr, false, "Offers service was not registered in attachment service manager!");
|
|
|
|
//
|
|
// Attacker's part -- modify Alice's offers without Alice's private keys
|
|
//
|
|
|
|
// get offer update tx from the pool (it's not added yet to the blockchain)
|
|
std::list<transaction> txs;
|
|
CHECK_AND_ASSERT_MES(c.get_pool_transactions(txs), false, "");
|
|
CHECK_AND_ASSERT_MES(txs.size() == 1, false, "Incorrect tx pool count: " << txs.size());
|
|
LOG_PRINT_L0(ENDL << c.get_tx_pool().print_pool(true));
|
|
|
|
const transaction& offer_update_tx = txs.back();
|
|
tx_service_attachment tsa = AUTO_VAL_INIT(tsa);
|
|
r = get_type_in_variant_container(offer_update_tx.attachment, tsa);
|
|
CHECK_AND_ASSERT_MES(r, false, "get_type_in_variant_container failed");
|
|
CHECK_AND_ASSERT_MES(tsa.service_id == BC_OFFERS_SERVICE_ID && tsa.instruction == BC_OFFERS_SERVICE_INSTRUCTION_UPD, false, "invalid tsa: " << tsa.service_id << ", " << tsa.instruction);
|
|
|
|
// unpack tsa with update offer
|
|
bc_services::update_offer uo = AUTO_VAL_INIT(uo);
|
|
std::string json_buff;
|
|
CHECK_AND_ASSERT_MES(epee::zlib_helper::unpack(tsa.body, json_buff), false, "");
|
|
CHECK_AND_ASSERT_MES(epee::serialization::load_t_from_json(uo, json_buff), false, "");
|
|
|
|
// Case 2: Bob deletes Alice's offer
|
|
bc_services::cancel_offer co = AUTO_VAL_INIT(co);
|
|
co.tx_id = uo.tx_id;
|
|
co.offer_index = uo.offer_index;
|
|
co.sig = uo.sig;
|
|
|
|
std::vector<attachment_v> attachments;
|
|
bc_services::put_offer_into_attachment(co, attachments);
|
|
|
|
std::shared_ptr<tools::wallet2> bob_wlt = init_playtime_test_wallet(events, c, m_accounts[BOB_ACC_IDX]);
|
|
bob_wlt->refresh();
|
|
|
|
// make a tx with this attachment (note: tsa.security is signed using Bob's keys, can be resign later using Alice's public address)
|
|
transaction tx_c = AUTO_VAL_INIT(tx_c);
|
|
bob_wlt->transfer(std::vector<tx_destination_entry>({ tx_destination_entry(TESTS_DEFAULT_FEE * 3, m_accounts[BOB_ACC_IDX].get_public_address()) }), 0, 0, TESTS_DEFAULT_FEE * 19, empty_extra, attachments,
|
|
tools::detail::ssi_digit, tools::tx_dust_policy(DEFAULT_DUST_THRESHOLD), tx_c, 0, true, 0, true);
|
|
|
|
LOG_PRINT_L0(ENDL << c.get_tx_pool().print_pool(true));
|
|
|
|
// put in a block 'tx', leave Alice's tx in the pool
|
|
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 2, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
|
|
CHECK_AND_ASSERT_MES(mine_next_pow_block_in_playtime_with_given_txs(m_scratchpad_keeper, m_accounts[MINER_ACC_IDX].get_public_address(), c, std::vector<transaction>({ tx_c })), false, "mine_next_pow_block_in_playtime failed");
|
|
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
|
|
|
|
LOG_PRINT_L0(ENDL << c.get_tx_pool().print_pool(true));
|
|
|
|
std::list<bc_services::offer_details_ex> offers;
|
|
r = get_all_offers(c, offers);
|
|
CHECK_AND_ASSERT_MES(r, false, "get_all_offers failed");
|
|
|
|
LOG_PRINT_YELLOW("\n\n!!!!!!!!!!!! offers.size(): " << offers.size() << "\n\n", LOG_LEVEL_0);
|
|
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
bool offers_multiple_update::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
// Test idea: make an offer then update it twice (two very similar update requests). Make sure raw/normal offers' count is correct.
|
|
|
|
bool r = false;
|
|
m_accounts.resize(TOTAL_ACCS_COUNT);
|
|
account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate();
|
|
|
|
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
|
|
REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
|
|
|
|
MAKE_NEXT_BLOCK(events, blk_1, blk_0r, miner_acc);
|
|
|
|
// Miner adds and offer
|
|
bc_services::offer_details od = AUTO_VAL_INIT(od);
|
|
fill_test_offer(od);
|
|
od.comment = "new";
|
|
std::vector<attachment_v> attachments;
|
|
bc_services::put_offer_into_attachment(od, attachments);
|
|
MAKE_TX_ATTACH(events, tx_1, miner_acc, miner_acc, TESTS_DEFAULT_FEE, blk_1, attachments);
|
|
MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1, miner_acc, tx_1);
|
|
|
|
DO_CALLBACK_PARAMS(events, "check_offers_count", offers_count_param(1, 1));
|
|
|
|
// Miner updates the offer
|
|
bc_services::update_offer uo = AUTO_VAL_INIT(uo);
|
|
uo.tx_id = get_transaction_hash(tx_1);
|
|
uo.offer_index = 0;
|
|
uo.of = od;
|
|
uo.of.comment = "update";
|
|
r = sign_an_offer(tx_1, uo.offer_index, miner_acc.get_keys(), bc_services::make_offer_sig_blob(uo), uo.sig);
|
|
CHECK_AND_ASSERT_MES(r, false, "sign_an_offer failed");
|
|
attachments.clear();
|
|
bc_services::put_offer_into_attachment(uo, attachments);
|
|
transaction tx_2 = AUTO_VAL_INIT(tx_2);
|
|
uint64_t fee = 14 * TESTS_DEFAULT_FEE;
|
|
r = construct_tx_to_key(events, tx_2, blk_2, miner_acc, miner_acc, TESTS_DEFAULT_FEE, fee, 0, 0, empty_extra, attachments);
|
|
CHECK_AND_ASSERT_MES(r, false, "construct_tx_to_key failed");
|
|
events.push_back(tx_2);
|
|
|
|
// Miner updates the same the offer once more
|
|
uo.of.comment = "update 2";
|
|
r = sign_an_offer(tx_1, uo.offer_index, miner_acc.get_keys(), bc_services::make_offer_sig_blob(uo), uo.sig);
|
|
CHECK_AND_ASSERT_MES(r, false, "sign_an_offer failed");
|
|
attachments.clear();
|
|
bc_services::put_offer_into_attachment(uo, attachments);
|
|
transaction tx_3 = AUTO_VAL_INIT(tx_3);
|
|
fee = 19 * TESTS_DEFAULT_FEE;
|
|
r = construct_tx_to_key(events, tx_3, blk_2, miner_acc, miner_acc, TESTS_DEFAULT_FEE, fee, 0, 0, empty_extra, attachments);
|
|
CHECK_AND_ASSERT_MES(r, false, "construct_tx_to_key failed");
|
|
events.push_back(tx_3);
|
|
|
|
MAKE_NEXT_BLOCK_TX_LIST(events, blk_3, blk_2, miner_acc, std::list<transaction>({ tx_2, tx_3 }));
|
|
|
|
// raw count should increase by 1 (=2), normal cound should remain the same (=1)
|
|
DO_CALLBACK_PARAMS(events, "check_offers_count", offers_count_param(1, 2));
|
|
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
bool offer_sig_validity_in_update_and_cancel::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
// Test idea: check that changing any field of signed update_offer or cancel_offer made signature invalid
|
|
|
|
bool r = false;
|
|
m_accounts.resize(TOTAL_ACCS_COUNT);
|
|
account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate();
|
|
|
|
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
|
|
REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
|
|
|
|
MAKE_NEXT_BLOCK(events, blk_1, blk_0r, miner_acc);
|
|
|
|
// del offer: valid sig, change tx_id, change offer_index
|
|
// upd offer: valid sig, change tx_id, change offer_index, change of
|
|
|
|
// Miner adds two offers
|
|
bc_services::offer_details od_1 = AUTO_VAL_INIT(od_1);
|
|
fill_test_offer(od_1);
|
|
od_1.comment = "1";
|
|
std::vector<attachment_v> attachments;
|
|
bc_services::put_offer_into_attachment(od_1, attachments);
|
|
MAKE_TX_ATTACH(events, tx_offer_1, miner_acc, miner_acc, TESTS_DEFAULT_FEE, blk_1, attachments);
|
|
|
|
bc_services::offer_details od_2 = AUTO_VAL_INIT(od_2);
|
|
fill_test_offer(od_2);
|
|
od_2.comment = "2";
|
|
attachments.clear();
|
|
bc_services::put_offer_into_attachment(od_2, attachments);
|
|
MAKE_TX_ATTACH(events, tx_offer_2, miner_acc, miner_acc, TESTS_DEFAULT_FEE, blk_1, attachments);
|
|
|
|
MAKE_NEXT_BLOCK_TX_LIST(events, blk_2, blk_1, miner_acc, std::list<transaction>({ tx_offer_1, tx_offer_2 }));
|
|
|
|
DO_CALLBACK_PARAMS(events, "check_offers_count", offers_count_param(2, 2));
|
|
|
|
|
|
// Miner updates the offer
|
|
bc_services::update_offer uo_good = AUTO_VAL_INIT(uo_good);
|
|
uo_good.tx_id = get_transaction_hash(tx_offer_1);
|
|
uo_good.offer_index = 0;
|
|
uo_good.of = od_1;
|
|
uo_good.of.comment = "update";
|
|
r = sign_an_offer(tx_offer_1, uo_good.offer_index, miner_acc.get_keys(), bc_services::make_offer_sig_blob(uo_good), uo_good.sig);
|
|
CHECK_AND_ASSERT_MES(r, false, "sign_an_offer failed");
|
|
|
|
// 1. update offer with wrong tx_id (same sig)
|
|
bc_services::update_offer uo_bad = uo_good;
|
|
uo_bad.tx_id = get_transaction_hash(tx_offer_2); // tx_id of valid but another offer
|
|
attachments.clear();
|
|
bc_services::put_offer_into_attachment(uo_bad, attachments);
|
|
MAKE_TX_ATTACH_FEE(events, tx_wrong_uo_1, miner_acc, miner_acc, TESTS_DEFAULT_FEE, 3 * TESTS_DEFAULT_FEE, blk_2, attachments);
|
|
MAKE_NEXT_BLOCK_TX1(events, blk_3, blk_2, miner_acc, tx_wrong_uo_1);
|
|
|
|
// offers count shoudn't change
|
|
DO_CALLBACK_PARAMS(events, "check_offers_count", offers_count_param(2, 2));
|
|
|
|
// 2. update offer with wrong offer_index (same sig)
|
|
uo_bad = uo_good;
|
|
uo_bad.offer_index = 1;
|
|
attachments.clear();
|
|
bc_services::put_offer_into_attachment(uo_bad, attachments);
|
|
MAKE_TX_ATTACH_FEE(events, tx_wrong_uo_2, miner_acc, miner_acc, TESTS_DEFAULT_FEE, 4 * TESTS_DEFAULT_FEE, blk_3, attachments);
|
|
MAKE_NEXT_BLOCK_TX1(events, blk_4, blk_3, miner_acc, tx_wrong_uo_2);
|
|
|
|
// offers count shoudn't change
|
|
DO_CALLBACK_PARAMS(events, "check_offers_count", offers_count_param(2, 2));
|
|
|
|
// 3. update offer with wrong 'of' (same sig)
|
|
uo_bad = uo_good;
|
|
uo_bad.of.comment = "update bad";
|
|
attachments.clear();
|
|
bc_services::put_offer_into_attachment(uo_bad, attachments);
|
|
MAKE_TX_ATTACH_FEE(events, tx_wrong_uo_3, miner_acc, miner_acc, TESTS_DEFAULT_FEE, 5 * TESTS_DEFAULT_FEE, blk_4, attachments);
|
|
MAKE_NEXT_BLOCK_TX1(events, blk_5, blk_4, miner_acc, tx_wrong_uo_3);
|
|
//MAKE_NEXT_BLOCK(events, blk_5, blk_4, miner_acc);
|
|
|
|
// offers count shoudn't change
|
|
DO_CALLBACK_PARAMS(events, "check_offers_count", offers_count_param(2, 2));
|
|
|
|
// 4. update offer with valid sig (sanity check)
|
|
attachments.clear();
|
|
bc_services::put_offer_into_attachment(uo_good, attachments);
|
|
MAKE_TX_ATTACH_FEE(events, tx_good_uo, miner_acc, miner_acc, TESTS_DEFAULT_FEE, 6 * TESTS_DEFAULT_FEE, blk_5, attachments);
|
|
MAKE_NEXT_BLOCK_TX1(events, blk_6, blk_5, miner_acc, tx_good_uo);
|
|
|
|
// offers raw count should change, normal count - not
|
|
DO_CALLBACK_PARAMS(events, "check_offers_count", offers_count_param(2, 3));
|
|
|
|
|
|
|
|
// miner removes offer
|
|
bc_services::cancel_offer co_good = AUTO_VAL_INIT(co_good);
|
|
co_good.tx_id = get_transaction_hash(tx_good_uo);
|
|
co_good.offer_index = 0;
|
|
r = sign_an_offer(tx_good_uo, co_good.offer_index, miner_acc.get_keys(), bc_services::make_offer_sig_blob(co_good), co_good.sig);
|
|
CHECK_AND_ASSERT_MES(r, false, "sign_an_offer failed");
|
|
|
|
// 5. remove offer with invalid tx_id
|
|
bc_services::cancel_offer co_bad = co_good;
|
|
co_bad.tx_id = get_transaction_hash(tx_offer_2); // another tx
|
|
attachments.clear();
|
|
bc_services::put_offer_into_attachment(co_bad, attachments);
|
|
MAKE_TX_ATTACH_FEE(events, tx_wrong_co_1, miner_acc, miner_acc, TESTS_DEFAULT_FEE, 7 * TESTS_DEFAULT_FEE, blk_6, attachments);
|
|
MAKE_NEXT_BLOCK_TX1(events, blk_7, blk_6, miner_acc, tx_wrong_co_1);
|
|
|
|
// offers count shoudn't change
|
|
DO_CALLBACK_PARAMS(events, "check_offers_count", offers_count_param(2, 3));
|
|
|
|
// 6. remove offer with invalid
|
|
co_bad = co_good;
|
|
co_bad.offer_index = 1;
|
|
attachments.clear();
|
|
bc_services::put_offer_into_attachment(co_bad, attachments);
|
|
MAKE_TX_ATTACH_FEE(events, tx_wrong_co_2, miner_acc, miner_acc, TESTS_DEFAULT_FEE, 8 * TESTS_DEFAULT_FEE, blk_7, attachments);
|
|
MAKE_NEXT_BLOCK_TX1(events, blk_8, blk_7, miner_acc, tx_wrong_co_2);
|
|
|
|
// offers count shoudn't change
|
|
DO_CALLBACK_PARAMS(events, "check_offers_count", offers_count_param(2, 3));
|
|
|
|
// 7. remove offer normally (sanity check)
|
|
attachments.clear();
|
|
bc_services::put_offer_into_attachment(co_good, attachments);
|
|
MAKE_TX_ATTACH_FEE(events, tx_good_co, miner_acc, miner_acc, TESTS_DEFAULT_FEE, 9 * TESTS_DEFAULT_FEE, blk_8, attachments);
|
|
MAKE_NEXT_BLOCK_TX1(events, blk_9, blk_8, miner_acc, tx_good_co);
|
|
|
|
// offers raw count shoudn't change, normal count should decreased
|
|
DO_CALLBACK_PARAMS(events, "check_offers_count", offers_count_param(1, 3));
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
offer_lifecycle_via_tx_pool::offer_lifecycle_via_tx_pool()
|
|
{
|
|
REGISTER_CALLBACK_METHOD(offer_lifecycle_via_tx_pool, c1);
|
|
}
|
|
|
|
bool offer_lifecycle_via_tx_pool::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
// Test idea: go though typical offer life cycle using wallet member functions. Each operation will put a tx into the pool, then a block will be mined to commit it into the blockchain.
|
|
|
|
GENERATE_ACCOUNT(preminer_acc);
|
|
m_accounts.resize(TOTAL_ACCS_COUNT);
|
|
account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate();
|
|
|
|
MAKE_GENESIS_BLOCK(events, blk_0, preminer_acc, test_core_time::get_time());
|
|
REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 3);
|
|
|
|
DO_CALLBACK(events, "c1");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool offer_lifecycle_via_tx_pool::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
|
{
|
|
bool r = false;
|
|
std::shared_ptr<tools::wallet2> miner_wlt = init_playtime_test_wallet(events, c, m_accounts[MINER_ACC_IDX]);
|
|
miner_wlt->refresh();
|
|
|
|
account_base someone;
|
|
someone.generate();
|
|
account_public_address some_addr = someone.get_public_address(); // just random public address
|
|
|
|
uint64_t miner_start_balance = miner_wlt->balance();
|
|
|
|
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Invalid tx pool count: " << c.get_pool_transactions_count() << ENDL << "tx pool:" << ENDL << c.get_tx_pool().print_pool(true));
|
|
|
|
bc_services::offer_details_ex od = AUTO_VAL_INIT(od);
|
|
fill_test_offer(od);
|
|
od.comment = "1";
|
|
od.fee = TX_DEFAULT_FEE * 3;
|
|
|
|
// create an offer
|
|
transaction tx_1 = AUTO_VAL_INIT(tx_1);
|
|
TRY_ENTRY()
|
|
miner_wlt->push_offer(od, tx_1);
|
|
CATCH_ENTRY2(false);
|
|
|
|
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Invalid tx pool count: " << c.get_pool_transactions_count() << ENDL << "tx pool:" << ENDL << c.get_tx_pool().print_pool(true));
|
|
r = mine_next_pow_block_in_playtime(m_scratchpad_keeper, some_addr, 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, "Invalid tx pool count: " << c.get_pool_transactions_count() << ENDL << "tx pool:" << ENDL << c.get_tx_pool().print_pool(true));
|
|
|
|
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("After putting offer push tx into the blockchain", "miner", miner_wlt, miner_start_balance - od.fee), false, "");
|
|
|
|
CHECK_AND_ASSERT_MES(check_offers_count(c, 0, std::vector<test_event_entry>({ callback_entry("", epee::string_tools::pod_to_hex(offers_count_param(1, 1))) })), false, "");
|
|
|
|
// update the offer
|
|
transaction tx_2 = AUTO_VAL_INIT(tx_2);
|
|
od.comment = "UPDATE";
|
|
TRY_ENTRY()
|
|
miner_wlt->update_offer_by_id(get_transaction_hash(tx_1), 0, od, tx_2);
|
|
CATCH_ENTRY2(false);
|
|
|
|
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Invalid tx pool count: " << c.get_pool_transactions_count() << ENDL << "tx pool:" << ENDL << c.get_tx_pool().print_pool(true));
|
|
r = mine_next_pow_block_in_playtime(m_scratchpad_keeper, some_addr, 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, "Invalid tx pool count: " << c.get_pool_transactions_count() << ENDL << "tx pool:" << ENDL << c.get_tx_pool().print_pool(true));
|
|
|
|
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("After updating offer push tx into the blockchain", "miner", miner_wlt, miner_start_balance - od.fee * 2), false, "");
|
|
|
|
CHECK_AND_ASSERT_MES(check_offers_count(c, 0, std::vector<test_event_entry>({ callback_entry("", epee::string_tools::pod_to_hex(offers_count_param(1, 2))) })), false, "");
|
|
|
|
|
|
// update the offer once again
|
|
transaction tx_3 = AUTO_VAL_INIT(tx_3);
|
|
od.comment = "UPDATE2";
|
|
TRY_ENTRY()
|
|
miner_wlt->update_offer_by_id(get_transaction_hash(tx_2), 0, od, tx_3);
|
|
CATCH_ENTRY2(false);
|
|
|
|
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Invalid tx pool count: " << c.get_pool_transactions_count() << ENDL << "tx pool:" << ENDL << c.get_tx_pool().print_pool(true));
|
|
r = mine_next_pow_block_in_playtime(m_scratchpad_keeper, some_addr, 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, "Invalid tx pool count: " << c.get_pool_transactions_count() << ENDL << "tx pool:" << ENDL << c.get_tx_pool().print_pool(true));
|
|
|
|
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("After second update offer push tx into the blockchain", "miner", miner_wlt, miner_start_balance - od.fee * 3), false, "");
|
|
|
|
CHECK_AND_ASSERT_MES(check_offers_count(c, 0, std::vector<test_event_entry>({ callback_entry("", epee::string_tools::pod_to_hex(offers_count_param(1, 3))) })), false, "");
|
|
|
|
|
|
// cancel the offer
|
|
transaction tx_4 = AUTO_VAL_INIT(tx_4);
|
|
TRY_ENTRY()
|
|
miner_wlt->cancel_offer_by_id(get_transaction_hash(tx_3), 0, tx_4);
|
|
CATCH_ENTRY2(false);
|
|
|
|
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Invalid tx pool count: " << c.get_pool_transactions_count() << ENDL << "tx pool:" << ENDL << c.get_tx_pool().print_pool(true));
|
|
r = mine_next_pow_block_in_playtime(m_scratchpad_keeper, some_addr, 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, "Invalid tx pool count: " << c.get_pool_transactions_count() << ENDL << "tx pool:" << ENDL << c.get_tx_pool().print_pool(true));
|
|
|
|
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("After offer's cancellation push tx into the blockchain", "miner", miner_wlt, miner_start_balance - od.fee * 3), false, "");
|
|
|
|
CHECK_AND_ASSERT_MES(check_offers_count(c, 0, std::vector<test_event_entry>({ callback_entry("", epee::string_tools::pod_to_hex(offers_count_param(0, 3))) })), false, "");
|
|
|
|
return true;
|
|
}
|