forked from lthn/blockchain
added new test, fixed multiple bugs
This commit is contained in:
parent
821776d198
commit
e82bd6b575
18 changed files with 847 additions and 21 deletions
|
|
@ -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)
|
||||
|
|
|
|||
41
contrib/epee/include/serialization/keyvalue_hexemizer.h
Normal file
41
contrib/epee/include/serialization/keyvalue_hexemizer.h
Normal 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()
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
571
tests/core_tests/wallet_packing_tx.cpp
Normal file
571
tests/core_tests/wallet_packing_tx.cpp
Normal 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;
|
||||
}
|
||||
17
tests/core_tests/wallet_packing_tx.h
Normal file
17
tests/core_tests/wallet_packing_tx.h
Normal 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);
|
||||
};
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue