1
0
Fork 0
forked from lthn/blockchain

added new test, fixed multiple bugs

This commit is contained in:
cryptozoidberg 2019-11-29 21:43:17 +01:00
parent 821776d198
commit e82bd6b575
No known key found for this signature in database
GPG key ID: 22DEB97A54C6FDEC
18 changed files with 847 additions and 21 deletions

View file

@ -51,6 +51,7 @@ namespace epee
}
};
//basic helpers for pod-to-hex serialization
template<class t_pod_type>
std::string transform_t_pod_to_str(const t_pod_type & a)

View file

@ -0,0 +1,41 @@
// Copyright (c) 2006-2019, Andrey N. Sabelnikov, www.sabelnikov.net
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the Andrey N. Sabelnikov nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#pragma once
#include "keyvalue_serialization.h"
namespace epee
{
struct hexemizer
{
std::string blob;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_BLOB_AS_HEX_STRING(blob)
END_KV_SERIALIZE_MAP()
};
}

View file

@ -1358,8 +1358,8 @@ bool blockchain_storage::create_block_template(const create_block_template_param
CRITICAL_REGION_END();
size_t txs_size;
uint64_t fee;
size_t txs_size = 0;
uint64_t fee = 0;
bool block_filled = false;
if (pcustom_fill_block_template_func == nullptr)
block_filled = m_tx_pool.fill_block_template(b, pos, median_size, already_generated_coins, txs_size, fee, height, params.explicit_txs);

View file

@ -1138,6 +1138,7 @@ namespace currency
// add explicit transactions
for (const auto& tx : explicit_txs)
{
fee += get_tx_fee(tx);
bl.tx_hashes.push_back(get_transaction_hash(tx));
}
return true;

View file

@ -109,6 +109,11 @@ namespace tools
return m_rpc.on_submitblock(req, rsp, m_err_stub, m_cntxt_stub);
}
//------------------------------------------------------------------------------------------------------------------------------
bool call_COMMAND_RPC_SUBMITBLOCK2(const currency::COMMAND_RPC_SUBMITBLOCK2::request& req, currency::COMMAND_RPC_SUBMITBLOCK2::response& rsp) override
{
return m_rpc.on_submitblock2(req, rsp, m_err_stub, m_cntxt_stub);
}
//------------------------------------------------------------------------------------------------------------------------------
bool call_COMMAND_RPC_GET_POS_MINING_DETAILS(const currency::COMMAND_RPC_GET_POS_MINING_DETAILS::request& req, currency::COMMAND_RPC_GET_POS_MINING_DETAILS::response& rsp) override
{
return m_rpc.on_get_pos_mining_details(req, rsp, m_cntxt_stub);

View file

@ -890,6 +890,63 @@ namespace currency
//
res.status = "OK";
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_submitblock2(const COMMAND_RPC_SUBMITBLOCK2::request& req, COMMAND_RPC_SUBMITBLOCK2::response& res, epee::json_rpc::error& error_resp, connection_context& cntx)
{
CHECK_CORE_READY();
block b = AUTO_VAL_INIT(b);
if (!parse_and_validate_block_from_blob(req.b, b))
{
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB;
error_resp.message = "Wrong block blob";
return false;
}
block_verification_context bvc = AUTO_VAL_INIT(bvc);
for (const auto& txblob : req.explicit_txs)
{
crypto::hash tx_hash = AUTO_VAL_INIT(tx_hash);
transaction tx = AUTO_VAL_INIT(tx);
if (!parse_and_validate_tx_from_blob(txblob.blob, tx, tx_hash))
{
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB;
error_resp.message = "Wrong explicit tx blob";
return false;
}
bvc.m_onboard_transactions[tx_hash] = tx;
}
if (!m_core.handle_block_found(b, &bvc))
{
if (bvc.m_added_to_altchain)
{
error_resp.code = CORE_RPC_ERROR_CODE_BLOCK_ADDED_AS_ALTERNATIVE;
error_resp.message = "Block added as alternative";
return false;
}
error_resp.code = CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED;
error_resp.message = "Block not accepted";
return false;
}
//@#@
//temporary double check timestamp
if (time(NULL) - get_actual_timestamp(b) > 5)
{
LOG_PRINT_RED_L0("Found block (" << get_block_hash(b) << ") timestamp (" << get_actual_timestamp(b)
<< ") is suspiciously less (" << time(NULL) - get_actual_timestamp(b) << ") then curren time( " << time(NULL) << ")");
//mark node to make it easier to find it via scanner
m_core.get_blockchain_storage().get_performnce_data().epic_failure_happend = true;
}
//
res.status = "OK";
return true;
}

View file

@ -63,6 +63,7 @@ namespace currency
bool on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp, connection_context& cntx);
bool on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res, epee::json_rpc::error& error_resp, connection_context& cntx);
bool on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp, connection_context& cntx);
bool on_submitblock2(const COMMAND_RPC_SUBMITBLOCK2::request& req, COMMAND_RPC_SUBMITBLOCK2::response& res, epee::json_rpc::error& error_resp, connection_context& cntx);
bool on_get_last_block_header(const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request& req, COMMAND_RPC_GET_LAST_BLOCK_HEADER::response& res, epee::json_rpc::error& error_resp, connection_context& cntx);
bool on_get_block_header_by_hash(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response& res, epee::json_rpc::error& error_resp, connection_context& cntx);
bool on_get_block_header_by_height(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response& res, epee::json_rpc::error& error_resp, connection_context& cntx);
@ -125,6 +126,7 @@ namespace currency
MAP_JON_RPC_WE("on_getblockhash", on_getblockhash, COMMAND_RPC_GETBLOCKHASH)
MAP_JON_RPC_WE("getblocktemplate", on_getblocktemplate, COMMAND_RPC_GETBLOCKTEMPLATE)
MAP_JON_RPC_WE("submitblock", on_submitblock, COMMAND_RPC_SUBMITBLOCK)
MAP_JON_RPC_WE("submitblock2", on_submitblock2, COMMAND_RPC_SUBMITBLOCK2)
MAP_JON_RPC_WE("getlastblockheader", on_get_last_block_header, COMMAND_RPC_GET_LAST_BLOCK_HEADER)
MAP_JON_RPC_WE("getblockheaderbyhash", on_get_block_header_by_hash, COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH)
MAP_JON_RPC_WE("getblockheaderbyheight", on_get_block_header_by_height, COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT)

View file

@ -5,6 +5,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#include "serialization/keyvalue_hexemizer.h"
#include "currency_protocol/currency_protocol_defs.h"
#include "currency_core/currency_basic.h"
#include "currency_core/difficulty.h"
@ -825,6 +826,29 @@ namespace currency
};
};
struct COMMAND_RPC_SUBMITBLOCK2
{
struct request
{
std::string b; //hex encoded block blob
std::list<epee::hexemizer> explicit_txs; //hex encoded tx blobs
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_BLOB_AS_HEX_STRING(b)
KV_SERIALIZE(explicit_txs)
END_KV_SERIALIZE_MAP()
};
struct response
{
std::string status;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
END_KV_SERIALIZE_MAP()
};
};
struct block_header_response
{
uint8_t major_version;

View file

@ -116,6 +116,11 @@ namespace tools
return invoke_http_json_rpc_update_is_disconnect("submitblock", req, rsp);
}
//------------------------------------------------------------------------------------------------------------------------------
bool default_http_core_proxy::call_COMMAND_RPC_SUBMITBLOCK2(const currency::COMMAND_RPC_SUBMITBLOCK2::request& req, currency::COMMAND_RPC_SUBMITBLOCK2::response& rsp)
{
return invoke_http_json_rpc_update_is_disconnect("submitblock2", req, rsp);
}
//------------------------------------------------------------------------------------------------------------------------------
bool default_http_core_proxy::check_connection()
{
CRITICAL_REGION_LOCAL(m_lock);

View file

@ -39,6 +39,7 @@ namespace tools
bool call_COMMAND_RPC_SCAN_POS(const currency::COMMAND_RPC_SCAN_POS::request& req, currency::COMMAND_RPC_SCAN_POS::response& rsp) override;
bool call_COMMAND_RPC_GETBLOCKTEMPLATE(const currency::COMMAND_RPC_GETBLOCKTEMPLATE::request& req, currency::COMMAND_RPC_GETBLOCKTEMPLATE::response& rsp) override;
bool call_COMMAND_RPC_SUBMITBLOCK(const currency::COMMAND_RPC_SUBMITBLOCK::request& req, currency::COMMAND_RPC_SUBMITBLOCK::response& rsp) override;
bool call_COMMAND_RPC_SUBMITBLOCK2(const currency::COMMAND_RPC_SUBMITBLOCK2::request& req, currency::COMMAND_RPC_SUBMITBLOCK2::response& rsp) override;
bool call_COMMAND_RPC_GET_POS_MINING_DETAILS(const currency::COMMAND_RPC_GET_POS_MINING_DETAILS::request& req, currency::COMMAND_RPC_GET_POS_MINING_DETAILS::response& rsp) override;
bool call_COMMAND_RPC_GET_BLOCKS_DETAILS(const currency::COMMAND_RPC_GET_BLOCKS_DETAILS::request& req, currency::COMMAND_RPC_GET_BLOCKS_DETAILS::response& res) override;
bool call_COMMAND_RPC_GET_CURRENT_CORE_TX_EXPIRATION_MEDIAN(const currency::COMMAND_RPC_GET_CURRENT_CORE_TX_EXPIRATION_MEDIAN::request& req, currency::COMMAND_RPC_GET_CURRENT_CORE_TX_EXPIRATION_MEDIAN::response& res) override;

View file

@ -36,6 +36,7 @@ namespace tools
virtual bool call_COMMAND_RPC_SCAN_POS(const currency::COMMAND_RPC_SCAN_POS::request& req, currency::COMMAND_RPC_SCAN_POS::response& rsp){ return false; }
virtual bool call_COMMAND_RPC_GETBLOCKTEMPLATE(const currency::COMMAND_RPC_GETBLOCKTEMPLATE::request& req, currency::COMMAND_RPC_GETBLOCKTEMPLATE::response& rsp){ return false; }
virtual bool call_COMMAND_RPC_SUBMITBLOCK(const currency::COMMAND_RPC_SUBMITBLOCK::request& req, currency::COMMAND_RPC_SUBMITBLOCK::response& rsp){ return false; }
virtual bool call_COMMAND_RPC_SUBMITBLOCK2(const currency::COMMAND_RPC_SUBMITBLOCK2::request& req, currency::COMMAND_RPC_SUBMITBLOCK2::response& rsp) { return false; }
virtual bool call_COMMAND_RPC_GET_POS_MINING_DETAILS(const currency::COMMAND_RPC_GET_POS_MINING_DETAILS::request& req, currency::COMMAND_RPC_GET_POS_MINING_DETAILS::response& rsp){ return false; }
virtual bool call_COMMAND_RPC_GET_BLOCKS_DETAILS(const currency::COMMAND_RPC_GET_BLOCKS_DETAILS::request& req, currency::COMMAND_RPC_GET_BLOCKS_DETAILS::response& res){ return false; }
virtual bool call_COMMAND_RPC_GET_OFFERS_EX(const currency::COMMAND_RPC_GET_OFFERS_EX::request& req, currency::COMMAND_RPC_GET_OFFERS_EX::response& res){ return false; }

View file

@ -147,6 +147,11 @@ bool wallet2::set_core_proxy(const std::shared_ptr<i_core_proxy>& proxy)
return true;
}
//----------------------------------------------------------------------------------------------------
void wallet2::set_pos_mint_packing_size(uint64_t new_size)
{
m_pos_mint_packing_size = new_size;
}
//----------------------------------------------------------------------------------------------------
std::shared_ptr<i_core_proxy> wallet2::get_core_proxy()
{
return m_core_proxy;
@ -2295,22 +2300,22 @@ bool wallet2::generate_packing_transaction_if_needed(currency::transaction& tx,
{
prepare_free_transfers_cache(0);
auto it = m_found_free_amounts.find(CURRENCY_BLOCK_REWARD);
if (it == m_found_free_amounts.end() || it->second.size() < WALLET_POS_MINT_PACKING_SIZE)
if (it == m_found_free_amounts.end() || it->second.size() < m_pos_mint_packing_size)
return false;
//let's check if we have at least WALLET_POS_MINT_PACKING_SIZE transactions which is ready to go
size_t count = 0;
for (auto it_ind = it->second.begin(); it_ind != it->second.end() && count < WALLET_POS_MINT_PACKING_SIZE; it_ind++)
for (auto it_ind = it->second.begin(); it_ind != it->second.end() && count < m_pos_mint_packing_size; it_ind++)
{
if (is_transfer_ready_to_go(m_transfers[*it_ind], fake_outputs_number))
++count;
}
if (count < WALLET_POS_MINT_PACKING_SIZE)
if (count < m_pos_mint_packing_size)
return false;
construct_tx_param ctp = get_default_construct_tx_param();
currency::tx_destination_entry de = AUTO_VAL_INIT(de);
de.addr.push_back(m_account.get_public_address());
de.amount = WALLET_POS_MINT_PACKING_SIZE;
de.amount = m_pos_mint_packing_size*CURRENCY_BLOCK_REWARD;
ctp.dsts.push_back(de);
ctp.perform_packing = true;
@ -2835,10 +2840,13 @@ bool wallet2::build_minted_block(const currency::COMMAND_RPC_SCAN_POS::request&
WLT_LOG_GREEN("Block constructed <" << get_block_hash(b) << ">, sending to core...", LOG_LEVEL_0);
currency::COMMAND_RPC_SUBMITBLOCK::request subm_req = AUTO_VAL_INIT(subm_req);
currency::COMMAND_RPC_SUBMITBLOCK::response subm_rsp = AUTO_VAL_INIT(subm_rsp);
subm_req.push_back(epee::string_tools::buff_to_hex_nodelimer(t_serializable_object_to_blob(b)));
m_core_proxy->call_COMMAND_RPC_SUBMITBLOCK(subm_req, subm_rsp);
currency::COMMAND_RPC_SUBMITBLOCK2::request subm_req = AUTO_VAL_INIT(subm_req);
currency::COMMAND_RPC_SUBMITBLOCK2::response subm_rsp = AUTO_VAL_INIT(subm_rsp);
subm_req.b = t_serializable_object_to_blob(b);
if (tmpl_req.explicit_transaction.size())
subm_req.explicit_txs.push_back(hexemizer{ tmpl_req.explicit_transaction });
m_core_proxy->call_COMMAND_RPC_SUBMITBLOCK2(subm_req, subm_rsp);
if (subm_rsp.status != CORE_RPC_STATUS_OK)
{
WLT_LOG_ERROR("Constructed block is not accepted by core, status: " << subm_rsp.status);
@ -3462,21 +3470,24 @@ bool wallet2::prepare_tx_sources_for_packing(uint64_t items_to_pack, size_t fake
{
prepare_free_transfers_cache(fake_outputs_count);
auto it = m_found_free_amounts.find(CURRENCY_BLOCK_REWARD);
if (it == m_found_free_amounts.end() || it->second.size() < WALLET_POS_MINT_PACKING_SIZE)
if (it == m_found_free_amounts.end() || it->second.size() < m_pos_mint_packing_size)
return false;
for (auto set_it = it->second.begin(); set_it != it->second.end(); it++)
for (auto set_it = it->second.begin(); set_it != it->second.end() && selected_indicies.size() <= m_pos_mint_packing_size; )
{
if (is_transfer_ready_to_go(m_transfers[*set_it], fake_outputs_count))
{
found_money += it->first;
selected_indicies.push_back(*set_it);
WLT_LOG_L2("Selected index: " << *set_it << ", transfer_details: " << ENDL << epee::serialization::store_t_to_json(m_transfers[*set_it]));
it->second.erase(set_it++);
}
it->second.erase(it->second.begin());
if (!it->second.size())
m_found_free_amounts.erase(it);
else
set_it++;
}
if (!it->second.size())
m_found_free_amounts.erase(it);
return prepare_tx_sources(fake_outputs_count, sources, selected_indicies, found_money);
}
@ -4128,10 +4139,10 @@ void wallet2::prepare_transaction(const construct_tx_param& ctp, finalize_tx_par
uint64_t found_money = 0;
TIME_MEASURE_START_MS(prepare_tx_sources_time);
if (ctp.multisig_id == currency::null_hash)
if (ctp.perform_packing)
prepare_tx_sources_for_packing(WALLET_DEFAULT_POS_MINT_PACKING_SIZE, 0, ftp.sources, ftp.selected_transfers, found_money);
else if (ctp.multisig_id == currency::null_hash)
prepare_tx_sources(needed_money, ctp.fake_outputs_count, ctp.dust_policy.dust_threshold, ftp.sources, ftp.selected_transfers, found_money);
else if (ctp.perform_packing)
prepare_tx_sources_for_packing(WALLET_POS_MINT_PACKING_SIZE, 0, ftp.sources, ftp.selected_transfers, found_money);
else
prepare_tx_sources(ctp.multisig_id, ftp.sources, found_money);
TIME_MEASURE_FINISH_MS(prepare_tx_sources_time);

View file

@ -42,7 +42,7 @@
#define WALLET_DEFAULT_TX_SPENDABLE_AGE 10
#define WALLET_POS_MINT_CHECK_HEIGHT_INTERVAL 1
#define WALLET_POS_MINT_PACKING_SIZE 100
#define WALLET_DEFAULT_POS_MINT_PACKING_SIZE 100
#undef LOG_DEFAULT_CHANNEL
#define LOG_DEFAULT_CHANNEL "wallet"
@ -316,7 +316,8 @@ namespace tools
m_last_sync_percent(0),
m_do_rise_transfer(false),
m_watch_only(false),
m_last_pow_block_h(0)
m_last_pow_block_h(0),
m_pos_mint_packing_size(WALLET_DEFAULT_POS_MINT_PACKING_SIZE)
{};
public:
wallet2() : m_stop(false),
@ -329,7 +330,8 @@ namespace tools
m_do_rise_transfer(false),
m_log_prefix("???"),
m_watch_only(false),
m_last_pow_block_h(0)
m_last_pow_block_h(0),
m_pos_mint_packing_size(WALLET_DEFAULT_POS_MINT_PACKING_SIZE)
{
m_core_runtime_config = currency::get_default_core_runtime_config();
};
@ -494,6 +496,7 @@ namespace tools
bool set_core_proxy(const std::shared_ptr<i_core_proxy>& proxy);
void set_pos_mint_packing_size(uint64_t new_size);
std::shared_ptr<i_core_proxy> get_core_proxy();
uint64_t balance() const;
uint64_t balance(uint64_t& unloked, uint64_t& awaiting_in, uint64_t& awaiting_out, uint64_t& mined) const;
@ -867,6 +870,7 @@ private:
std::atomic<uint64_t> m_local_bc_height; //temporary workaround
std::atomic<uint64_t> m_last_bc_timestamp;
bool m_do_rise_transfer;
uint64_t m_pos_mint_packing_size;
transfer_container m_transfers;
multisig_transfer_container m_multisig_transfers;

View file

@ -856,6 +856,7 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(wallet_outputs_with_same_key_image);
GENERATE_AND_PLAY(wallet_unconfirmed_tx_expiration);
GENERATE_AND_PLAY(wallet_unconfimed_tx_balance);
GENERATE_AND_PLAY(packing_outputs_on_pos_minting_wallet);
GENERATE_AND_PLAY(wallet_rpc_integrated_address);
GENERATE_AND_PLAY(wallet_rpc_integrated_address_transfer);

View file

@ -0,0 +1,571 @@
// 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 "wallet_tests.h"
#include "wallet_test_core_proxy.h"
#include "../../src/wallet/wallet_public_structs_defs.h"
#include "offers_helper.h"
#include "string_coding.h"
#include "random_helper.h"
#include "tx_builder.h"
using namespace epee;
using namespace crypto;
using namespace currency;
const uint64_t uint64_max = std::numeric_limits<uint64_t>::max();
const std::wstring g_wallet_filename = L"~coretests.wallet.file.tmp";
const std::string g_wallet_password = "dofatibmzibeziyekigo";
const currency::account_base null_account = AUTO_VAL_INIT(null_account);
POD_MAKE_COMPARABLE(currency, tx_out);
// Determines which output is real and actually spent in tx inputs, when there are fake outputs.
bool determine_tx_real_inputs(currency::core& c, const currency::transaction& tx, const currency::account_keys& keys, std::vector<size_t>& real_inputs)
{
struct local_visitor
{
local_visitor(const currency::account_keys& keys, const crypto::key_image key_image)
: m_keys(keys)
, m_txin_key_image(key_image)
, m_output_in_input_index(0)
, m_found(false)
{}
bool handle_output(const transaction& source_tx, const transaction& validated_tx, const tx_out& out, uint64_t out_i)
{
CHECK_AND_ASSERT_MES(!m_found, false, "Internal error: m_found is true but the visitor is still being applied");
auto it = std::find(validated_tx.vout.begin(), validated_tx.vout.end(), out);
if (it == validated_tx.vout.end())
return false;
size_t output_tx_index = it - validated_tx.vout.begin();
crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(validated_tx);
crypto::key_derivation derivation;
bool r = generate_key_derivation(tx_pub_key, m_keys.m_view_secret_key, derivation);
CHECK_AND_ASSERT_MES(r, false, "generate_key_derivation failed");
crypto::secret_key ephemeral_secret_key;
derive_secret_key(derivation, output_tx_index, m_keys.m_spend_secret_key, ephemeral_secret_key);
crypto::public_key output_public_key = boost::get<txout_to_key>(out.target).key;
/*crypto::public_key ephemeral_public_key;
derive_public_key(derivation, output_tx_index, m_keys.m_account_address.m_spend_public_key, ephemeral_public_key);*/
crypto::key_image ki;
generate_key_image(output_public_key, ephemeral_secret_key, ki);
if (ki == m_txin_key_image)
{
m_found = true;
return false; // to break the loop in scan_outputkeys_for_indexes
}
++m_output_in_input_index;
return true;
}
currency::account_keys m_keys;
crypto::key_image m_txin_key_image;
size_t m_output_in_input_index;
bool m_found;
};
for (auto& txin : tx.vin)
{
const txin_to_key& in = boost::get<txin_to_key>(txin);
if (in.key_offsets.size() == 1)
{
real_inputs.push_back(0); // trivial case when no mixin is used
continue;
}
local_visitor vis(keys, in.k_image);
bool r = c.get_blockchain_storage().scan_outputkeys_for_indexes(tx, in, vis);
CHECK_AND_ASSERT_MES(r || vis.m_found, false, "scan_outputkeys_for_indexes failed");
if (!vis.m_found)
return false;
real_inputs.push_back(vis.m_output_in_input_index);
}
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
mined_balance_wallet_test::mined_balance_wallet_test()
{
REGISTER_CALLBACK_METHOD(mined_balance_wallet_test, c1);
REGISTER_CALLBACK_METHOD(mined_balance_wallet_test, set_core_config);
}
bool mined_balance_wallet_test::generate(std::vector<test_event_entry>& events) const
{
GENERATE_ACCOUNT(preminer_acc);
GENERATE_ACCOUNT(miner_acc);
m_accounts.push_back(miner_acc);
GENERATE_ACCOUNT(alice_acc);
m_accounts.push_back(alice_acc);
block blk_0 = AUTO_VAL_INIT(blk_0);
generator.construct_genesis_block(blk_0, preminer_acc, test_core_time::get_time());
events.push_back(blk_0);
DO_CALLBACK(events, "set_core_config");
DO_CALLBACK(events, "c1");
return true;
}
bool mined_balance_wallet_test::set_core_config(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
core_runtime_config crc = c.get_blockchain_storage().get_core_runtime_config();
crc.pos_minimum_heigh = TESTS_POS_CONFIG_POS_MINIMUM_HEIGH;
crc.min_coinstake_age = TESTS_POS_CONFIG_MIN_COINSTAKE_AGE;
c.get_blockchain_storage().set_core_runtime_config(crc);
return true;
}
bool mined_balance_wallet_test::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
blockchain_storage& bcs = c.get_blockchain_storage();
core_runtime_config crc = bcs.get_core_runtime_config();
crc.pos_minimum_heigh = TESTS_POS_CONFIG_POS_MINIMUM_HEIGH;
bcs.set_core_runtime_config(crc);
std::shared_ptr<tools::wallet2> miner_wlt = init_playtime_test_wallet(events, c, MINER_ACC_IDX);
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX);
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*miner_wlt.get(), "miner", 0), false, "wrong balance");
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt.get(), "alice", 0), false, "wrong balance");
uint64_t miner_mined_money = 0;
bool r = false;
std::list<currency::block> blocks;
size_t n = CURRENCY_MINED_MONEY_UNLOCK_WINDOW;
r = mine_next_pow_blocks_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c, n);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed");
r = bcs.get_blocks(bcs.get_current_blockchain_size() - n, n, blocks);
CHECK_AND_ASSERT_MES(r, false, "get_blocks failed");
for (auto& b : blocks)
miner_mined_money += get_outs_money_amount(b.miner_tx);
miner_wlt->refresh();
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*miner_wlt.get(), "miner", miner_mined_money, miner_mined_money), false, "wrong balance");
n = bcs.get_current_blockchain_size();
r = miner_wlt->try_mint_pos();
CHECK_AND_ASSERT_MES(r && bcs.get_current_blockchain_size() > n, false, "can't mint a PoS block");
block b = AUTO_VAL_INIT(b);
r = bcs.get_top_block(b);
CHECK_AND_ASSERT_MES(r, false, "get_top_block failed");
CHECK_AND_ASSERT_MES(b.miner_tx.vin.size() == 2, false, "Invalid PoS coinbase tx");
uint64_t coinbase_outs_amount = get_outs_money_amount(b.miner_tx);
uint64_t stake_amount = boost::get<txin_to_key>(b.miner_tx.vin[1]).amount;
CHECK_AND_ASSERT_MES(coinbase_outs_amount > stake_amount, false, "coinbase_outs_amount = " << coinbase_outs_amount << ", stake_amount = " << stake_amount << " : invalid condition");
miner_mined_money += coinbase_outs_amount - stake_amount;
miner_wlt->refresh();
std::stringstream ss;
miner_wlt->dump_trunsfers(ss, false);
LOG_PRINT_CYAN("miner transfers: " << ENDL << ss.str(), LOG_LEVEL_0);
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*miner_wlt.get(), "miner", miner_mined_money, miner_mined_money), false, "wrong balance");
return true;
}
//------------------------------------------------------------------------------
wallet_outputs_with_same_key_image::wallet_outputs_with_same_key_image()
{
REGISTER_CALLBACK_METHOD(wallet_outputs_with_same_key_image, c1);
}
bool wallet_outputs_with_same_key_image::generate(std::vector<test_event_entry>& events) const
{
// Test idea: make sure wallet does not take into account valid outputs having the same key image
// Only one such output is spendable thus only one output should be taken into account.
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 + 2);
uint64_t tx_amount = MK_TEST_COINS(3);
// tx_1
std::vector<tx_source_entry> sources_1;
r = fill_tx_sources(sources_1, events, blk_0r, miner_acc.get_keys(), tx_amount + TESTS_DEFAULT_FEE, 0);
CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources failed");
std::vector<tx_destination_entry> destinations{ tx_destination_entry(tx_amount, alice_acc.get_public_address()) };
tx_builder builder;
builder.step1_init();
builder.step2_fill_inputs(miner_acc.get_keys(), sources_1);
builder.step3_fill_outputs(destinations);
builder.step4_calc_hash();
builder.step5_sign(sources_1);
transaction tx_1 = builder.m_tx;
events.push_back(tx_1);
// tx_2 with the same secret key
currency::keypair tmp_sec_key = builder.m_tx_key;
builder.step1_init();
builder.m_tx_key = tmp_sec_key;
builder.m_tx.extra.clear();
add_tx_pub_key_to_extra(builder.m_tx, builder.m_tx_key.pub);
std::vector<tx_source_entry> sources_2;
r = fill_tx_sources(sources_2, events, blk_0r, miner_acc.get_keys(), tx_amount + TESTS_DEFAULT_FEE, 0, sources_1);
CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources failed");
// keep destinations the same
builder.step2_fill_inputs(miner_acc.get_keys(), sources_2);
builder.step3_fill_outputs(destinations);
builder.step4_calc_hash();
builder.step5_sign(sources_2);
transaction tx_2 = builder.m_tx;
events.push_back(tx_2);
// make sure tx_1 and tx_2 have been created with the same tx key
CHECK_AND_ASSERT_MES(get_tx_pub_key_from_extra(tx_1) == get_tx_pub_key_from_extra(tx_2), false, "internal error: tx_1 and tx_2 have different pub keys");
// now both txs are in the pool, make sure they are
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast<size_t>(2));
DO_CALLBACK(events, "c1");
return true;
}
bool wallet_outputs_with_same_key_image::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, m_accounts[ALICE_ACC_IDX]);
// check Alice has no unlocked coins
bool r = refresh_wallet_and_check_balance("before tx_1 and tx_2 added", "Alice", alice_wlt, MK_TEST_COINS(3) * 2, true, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 2, 0);
CHECK_AND_ASSERT_MES(r, false, "");
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, "there are txs in the pool!");
r = mine_next_pow_blocks_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed");
// only one tx_1 output is counted as the tx_2 output has the very same key image
r = refresh_wallet_and_check_balance("after tx_1 and tx_2 added", "Alice", alice_wlt, MK_TEST_COINS(3), true, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1, MK_TEST_COINS(3));
CHECK_AND_ASSERT_MES(r, false, "");
// make sure Alice is able to transfer her coins to smbd
std::vector<tx_destination_entry> destinations{ tx_destination_entry(MK_TEST_COINS(3) - TESTS_DEFAULT_FEE, m_accounts[MINER_ACC_IDX].get_public_address()) };
try
{
alice_wlt->transfer(destinations, 0, 0, TESTS_DEFAULT_FEE, empty_extra, empty_attachment);
}
catch (...)
{
CHECK_AND_ASSERT_MES(false, false, "Alice failed to transfer all her funds");
}
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "wrong tx 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, "there are txs in the pool!");
return true;
}
//------------------------------------------------------------------------------
wallet_unconfirmed_tx_expiration::wallet_unconfirmed_tx_expiration()
{
REGISTER_CALLBACK_METHOD(wallet_unconfirmed_tx_expiration, c1);
}
bool wallet_unconfirmed_tx_expiration::generate(std::vector<test_event_entry>& events) const
{
// Test outline:
// 1. Alice sends tx with expiration.
// 2. Miner ignores Alice's tx, so tx expires in the pool.
// 3. Tx is being removed from the pool due to expiration.
// Make sure Alice eventually spent no coins and all her money is unlocked in the wallet.
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 + 1);
bool r = false;
transaction tx_0 = AUTO_VAL_INIT(tx_0);
r = construct_tx_with_many_outputs(events, blk_0r, miner_acc.get_keys(), alice_acc.get_public_address(), TESTS_DEFAULT_FEE * 20, 10, TESTS_DEFAULT_FEE, tx_0);
CHECK_AND_ASSERT_MES(r, false, "construct_tx_with_many_outputs");
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(), TESTS_DEFAULT_FEE * 20, 10, TESTS_DEFAULT_FEE, tx_1);
CHECK_AND_ASSERT_MES(r, false, "construct_tx_with_many_outputs");
events.push_back(tx_1);
MAKE_NEXT_BLOCK_TX_LIST(events, blk_1, blk_0r, miner_acc, std::list<transaction>({ tx_0, tx_1 }));
REWIND_BLOCKS_N(events, blk_1r, blk_1, miner_acc, WALLET_DEFAULT_TX_SPENDABLE_AGE);
DO_CALLBACK(events, "c1");
return true;
}
bool wallet_unconfirmed_tx_expiration::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
bool r = false;
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX);
uint64_t alice_start_balance = TESTS_DEFAULT_FEE * 20;
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, alice_start_balance), false, "");
// Alice constructs and sends tx with expiration time
uint64_t expiration_time = c.get_blockchain_storage().get_tx_expiration_median() + TX_EXPIRATION_MEDIAN_SHIFT + 15;
etc_tx_details_expiration_time extra_entry = AUTO_VAL_INIT(extra_entry);
extra_entry.v = expiration_time;
std::vector<extra_v> extra({ extra_entry }); // extra with expiration time
std::vector<tx_destination_entry> destinations({ tx_destination_entry(TESTS_DEFAULT_FEE * 2, m_accounts[MINER_ACC_IDX].get_public_address()) });
transaction tx = AUTO_VAL_INIT(tx);
try
{
alice_wlt->transfer(destinations, 0, 0, TESTS_DEFAULT_FEE, extra, empty_attachment, tools::detail::ssi_digit, tools::tx_dust_policy(DEFAULT_DUST_THRESHOLD), tx);
}
catch (std::exception &e)
{
CHECK_AND_ASSERT_MES(false, false, "alice_wlt->transfer() caused an exception: " << e.what());
}
CHECK_AND_ASSERT_MES(get_tx_expiration_time(tx) == expiration_time, false, "tx expiration time wasn't set");
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Invalid txs count in the pool: " << c.get_pool_transactions_count());
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("tx sent,", "Alice", alice_wlt, alice_start_balance - TESTS_DEFAULT_FEE * 2 - TESTS_DEFAULT_FEE, true, 0, UINT64_MAX, 0, 0, TESTS_DEFAULT_FEE * 2), false, "");
// mine a few block with no tx, so Alice's tx is expired in the pool
for (size_t i = 0; i < 5; ++i)
{
r = mine_next_pow_block_in_playtime_with_given_txs(m_accounts[MINER_ACC_IDX].get_public_address(), c, std::vector<transaction>());
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime_with_given_txs failed");
}
// tx is still there
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Invalid txs count in the pool: " << c.get_pool_transactions_count());
// make sure expiration median was shifted enough
CHECK_AND_ASSERT_MES(c.get_blockchain_storage().is_tx_expired(tx), false, "wrong expiration time condition");
LOG_PRINT_CYAN("%%%%% tx_pool::on_idle()", LOG_LEVEL_0);
c.get_tx_pool().on_idle();
// make sure tx was removed by the pool
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Invalid txs count in the pool: " << c.get_pool_transactions_count());
// mine one more block to trigger wallet's on_idle() and outdated tx clearing
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");
// make sure all Alice's money are unlocked and no coins were actually spent
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("tx expired and removed from the pool,", "Alice", alice_wlt, alice_start_balance, true, 6, alice_start_balance, 0, 0, 0), false, "");
return true;
}
//------------------------------------------------------------------------------
wallet_chain_switch_with_spending_the_same_ki::wallet_chain_switch_with_spending_the_same_ki()
{
REGISTER_CALLBACK_METHOD(wallet_chain_switch_with_spending_the_same_ki, c1);
}
bool wallet_chain_switch_with_spending_the_same_ki::generate(std::vector<test_event_entry>& events) const
{
// Test outline
// 1. A wallet has one unspent output
// 2. wallet2::transfer() creates tx_0 that spends wallet's output
// 3. tx_0 is successfully put into the blockchain
// 4. Due to chain switch tx_0 is removed from the blockchain and get into the transaction pool
// 5. Make sure the wallet can't spend that output
// 6. After tx is expired make sure the wallet can spend that output
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);
MAKE_TX(events, tx_0, miner_acc, alice_acc, MK_TEST_COINS(30), blk_0r);
MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_acc, tx_0);
// rewind blocks to allow wallet be able to spend the coins
REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, WALLET_DEFAULT_TX_SPENDABLE_AGE);
DO_CALLBACK(events, "c1");
return true;
}
bool wallet_chain_switch_with_spending_the_same_ki::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
bool r = false;
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, MK_TEST_COINS(30), true, UINT64_MAX, MK_TEST_COINS(30)), false, "");
std::vector<tx_destination_entry> destinations { tx_destination_entry(MK_TEST_COINS(30) - TESTS_DEFAULT_FEE, m_accounts[BOB_ACC_IDX].get_public_address()) };
try
{
// create tx_1
alice_wlt->transfer(destinations, 0, 0, TESTS_DEFAULT_FEE, empty_extra, empty_attachment);
}
catch (std::exception &e)
{
CHECK_AND_ASSERT_MES(false, false, "alice_wlt->transfer() caused an exception: " << e.what());
}
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Tx pool has incorrect number of txs: " << c.get_pool_transactions_count());
// mine blk_2 on height 22
CHECK_AND_ASSERT_MES(mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c), false, "");
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Tx pool has incorrect number of txs: " << c.get_pool_transactions_count());
// refresh wallet
alice_wlt->refresh();
// DO NOT scan_tx_pool here intentionally
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt, "Alice", MK_TEST_COINS(0)), false, "");
uint64_t blk_1r_height = c.get_top_block_height() - 1;
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_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");
// one more to trigger chain switch
block blk_3a = AUTO_VAL_INIT(blk_3a);
r = mine_next_pow_block_in_playtime_with_given_txs(m_accounts[MINER_ACC_IDX].get_public_address(), c, std::vector<transaction>(), get_block_hash(blk_2a), get_block_height(blk_2a) + 1, &blk_3a);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime_with_given_txs failed");
// make sure tx_1 has been moved back to 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() == 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]);
// refresh wallet
alice_wlt->refresh();
// DO NOT scan_tx_pool here intentionally
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt, "Alice", MK_TEST_COINS(0)), false, "");
return true;
}
//------------------------------------------------------------------------------
wallet_unconfimed_tx_balance::wallet_unconfimed_tx_balance()
{
REGISTER_CALLBACK_METHOD(wallet_unconfimed_tx_balance, c1);
}
bool wallet_unconfimed_tx_balance::generate(std::vector<test_event_entry>& events) const
{
// Test outline:
// 1. Miner sends 100 coins to Alice (50 + 50)
// 2. Alice sends 30 back to Miner (tx is unconfirmed)
// 3. Make sure Alice's wallet has correct balance, when it is checked from wallet's callback
// 4. Few blocks are mined so the tx is get confirmed
// 5. Make sure Alice's balance has changed correctly
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);
MAKE_TX(events, tx_0, miner_acc, alice_acc, MK_TEST_COINS(50), blk_0r);
MAKE_TX(events, tx_1, miner_acc, alice_acc, MK_TEST_COINS(50), blk_0r);
MAKE_NEXT_BLOCK_TX_LIST(events, blk_1, blk_0r, miner_acc, std::list<transaction>({ tx_0, tx_1 }));
REWIND_BLOCKS_N(events, blk_1r, blk_1, miner_acc, WALLET_DEFAULT_TX_SPENDABLE_AGE);
DO_CALLBACK(events, "c1");
return true;
}
bool wallet_unconfimed_tx_balance::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
bool r = false;
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX);
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, MK_TEST_COINS(100), false, UINT64_MAX, MK_TEST_COINS(100)), false, "");
bool callback_is_ok = false;
// this callback will ba called from within wallet2::transfer() below
std::shared_ptr<wlt_lambda_on_transfer2_wrapper> l(new wlt_lambda_on_transfer2_wrapper(
[&callback_is_ok](const tools::wallet_public::wallet_transfer_info& wti, uint64_t balance, uint64_t unlocked_balance, uint64_t total_mined) -> bool
{
CHECK_AND_ASSERT_MES(balance == MK_TEST_COINS(70), false, "invalid balance: " << print_money_brief(balance));
CHECK_AND_ASSERT_MES(unlocked_balance == MK_TEST_COINS(50), false, "invalid unlocked_balance: " << print_money_brief(unlocked_balance));
CHECK_AND_ASSERT_MES(total_mined == 0, false, "invalid total_mined: " << print_money_brief(total_mined));
callback_is_ok = true;
return true;
}
));
alice_wlt->callback(l);
uint64_t fee = TESTS_DEFAULT_FEE * 3;
std::vector<tx_destination_entry> destinations{ tx_destination_entry(MK_TEST_COINS(30) - fee, m_accounts[MINER_ACC_IDX].get_public_address()) };
try
{
alice_wlt->transfer(destinations, 0, 0, fee, empty_extra, empty_attachment);
}
catch (std::exception &e)
{
CHECK_AND_ASSERT_MES(false, false, "alice_wlt->transfer() caused an exception: " << e.what());
}
CHECK_AND_NO_ASSERT_MES(callback_is_ok, false, "callback failed");
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Tx pool has incorrect number of txs: " << c.get_pool_transactions_count());
// 50 coins should be locked and 50 - unlocked
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, MK_TEST_COINS(70), false, UINT64_MAX, MK_TEST_COINS(50), 0, 0, MK_TEST_COINS(30) - fee), false, "");
// mine WALLET_DEFAULT_TX_SPENDABLE_AGE blocks so the tx get confirmed and coins get unlocked
CHECK_AND_ASSERT_MES(mine_next_pow_blocks_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c, WALLET_DEFAULT_TX_SPENDABLE_AGE), false, "");
CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Tx pool has incorrect number of txs: " << c.get_pool_transactions_count());
CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, MK_TEST_COINS(70), false, UINT64_MAX, MK_TEST_COINS(70), 0, 0, 0), false, "");
return true;
}

View file

@ -0,0 +1,17 @@
// 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.
#pragma once
#include "chaingen.h"
#include "wallet_tests_basic.h"
struct packing_for_pos_minting_wallet_test : public wallet_test
{
packing_for_pos_minting_wallet_test();
mined_balance_wallet_test();
bool generate(std::vector<test_event_entry>& events) const;
bool set_core_config(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
};

View file

@ -3297,3 +3297,79 @@ bool wallet_unconfimed_tx_balance::c1(currency::core& c, size_t ev_index, const
return true;
}
//------------------------------------------------------------------------------
packing_outputs_on_pos_minting_wallet::packing_outputs_on_pos_minting_wallet()
{
REGISTER_CALLBACK_METHOD(packing_outputs_on_pos_minting_wallet, c1);
REGISTER_CALLBACK_METHOD(packing_outputs_on_pos_minting_wallet, set_core_config);
}
bool packing_outputs_on_pos_minting_wallet::generate(std::vector<test_event_entry>& events) const
{
// 0 10 11 21 22 <- blockchain height (assuming CURRENCY_MINED_MONEY_UNLOCK_WINDOW == 10)
// (0 )... (0r)- (1 )... (1r)- <- main chain
// tx_0 <- txs
GENERATE_ACCOUNT(miner_acc);
m_accounts.push_back(miner_acc);
//GENERATE_ACCOUNT(alice_acc);
//m_accounts.push_back(alice_acc);
// don't use MAKE_GENESIS_BLOCK here because it will mask 'generator'
currency::block blk_0 = AUTO_VAL_INIT(blk_0);
generator.construct_genesis_block(blk_0, miner_acc, test_core_time::get_time());
events.push_back(blk_0);
DO_CALLBACK(events, "set_core_config");
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW+5);
//MAKE_TX_FEE(events, tx_0, miner_acc, alice_acc, MK_TEST_COINS(2000), TESTS_DEFAULT_FEE, blk_0r);
//MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_acc, tx_0);
//REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, WALLET_DEFAULT_TX_SPENDABLE_AGE);
DO_CALLBACK(events, "c1");
return true;
}
bool packing_outputs_on_pos_minting_wallet::set_core_config(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
core_runtime_config crc = c.get_blockchain_storage().get_core_runtime_config();
crc.pos_minimum_heigh = TESTS_POS_CONFIG_POS_MINIMUM_HEIGH;
crc.min_coinstake_age = TESTS_POS_CONFIG_MIN_COINSTAKE_AGE;
c.get_blockchain_storage().set_core_runtime_config(crc);
return true;
}
bool packing_outputs_on_pos_minting_wallet::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
std::shared_ptr<tools::wallet2> miner_wlt = init_playtime_test_wallet(events, c, MINER_ACC_IDX);
size_t blocks_fetched = 0;
bool received_money;
std::atomic<bool> atomic_false = ATOMIC_VAR_INIT(false);
miner_wlt->refresh(blocks_fetched, received_money, atomic_false);
CHECK_AND_ASSERT_MES(blocks_fetched == CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 5, false, "Incorrect numbers of blocks fetched");
miner_wlt->set_pos_mint_packing_size(4);
check_balance_via_wallet(*miner_wlt.get(), "miner_wlt", MK_TEST_COINS(2000), 0, MK_TEST_COINS(2000), 0, 0);
miner_wlt->try_mint_pos();
CHECK_AND_ASSERT_MES(c.get_current_blockchain_size() == CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 7, false, "Incorrect blockchain height:" << c.get_current_blockchain_size());
miner_wlt->refresh(blocks_fetched, received_money, atomic_false);
CHECK_AND_ASSERT_MES(blocks_fetched == 1, false, "Incorrect numbers of blocks fetched");
block top_block = AUTO_VAL_INIT(top_block);
bool r = c.get_blockchain_storage().get_top_block(top_block);
CHECK_AND_ASSERT_MES(r && is_pos_block(top_block), false, "get_top_block failed or smth goes wrong");
uint64_t top_block_reward = get_outs_money_amount(top_block.miner_tx);
check_balance_via_wallet(*miner_wlt.get(), "miner_wlt", uint64_max, MK_TEST_COINS(2000) + top_block_reward, 0, 0, 0);
miner_wlt->reset_password(g_wallet_password);
miner_wlt->store(g_wallet_filename);
return true;
}

View file

@ -251,3 +251,11 @@ struct wallet_unconfimed_tx_balance : public wallet_test
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);
};
struct packing_outputs_on_pos_minting_wallet : public wallet_test
{
packing_outputs_on_pos_minting_wallet();
bool generate(std::vector<test_event_entry>& events) const;
bool set_core_config(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
};