forked from lthn/blockchain
Merge branch 'posmixins' into develop
This commit is contained in:
commit
e9ed916550
11 changed files with 266 additions and 69 deletions
|
|
@ -13,6 +13,9 @@ set(VERSION "1.0")
|
|||
# cmake_policy(SET CMP0020 OLD)
|
||||
# endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "iOS" OR CMAKE_SYSTEM_NAME STREQUAL "Android")
|
||||
add_definitions(-DMOBILE_WALLET_BUILD)
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "iOS" )
|
||||
|
|
|
|||
|
|
@ -2569,14 +2569,14 @@ bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDO
|
|||
}
|
||||
if (result_outs.outs.size() < req.outs_count)
|
||||
{
|
||||
LOG_PRINT_RED_L0("Not enough inputs for amount " << print_money_brief(amount) << ", needed " << req.outs_count << ", added " << result_outs.outs.size() << " good outs from " << up_index_limit << " unlocked of " << outs_container_size << " total");
|
||||
LOG_PRINT_YELLOW("Not enough inputs for amount " << print_money_brief(amount) << ", needed " << req.outs_count << ", added " << result_outs.outs.size() << " good outs from " << up_index_limit << " unlocked of " << outs_container_size << " total", LOG_LEVEL_0);
|
||||
}
|
||||
}else
|
||||
{
|
||||
size_t added = 0;
|
||||
for (size_t i = 0; i != up_index_limit; i++)
|
||||
added += add_out_to_get_random_outs(result_outs, amount, i, req.outs_count, req.use_forced_mix_outs) ? 1 : 0;
|
||||
LOG_PRINT_RED_L0("Not enough inputs for amount " << print_money_brief(amount) << ", needed " << req.outs_count << ", added " << added << " good outs from " << up_index_limit << " unlocked of " << outs_container_size << " total - respond with all good outs");
|
||||
LOG_PRINT_YELLOW("Not enough inputs for amount " << print_money_brief(amount) << ", needed " << req.outs_count << ", added " << added << " good outs from " << up_index_limit << " unlocked of " << outs_container_size << " total - respond with all good outs", LOG_LEVEL_0);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@
|
|||
#define HF1_BLOCK_MAJOR_VERSION 1
|
||||
#define CURRENT_BLOCK_MAJOR_VERSION 2
|
||||
|
||||
#define CURRENCY_DEFAULT_DECOY_SET_SIZE 10
|
||||
|
||||
#define CURRENT_BLOCK_MINOR_VERSION 0
|
||||
#define CURRENCY_BLOCK_FUTURE_TIME_LIMIT 60*60*2
|
||||
#define CURRENCY_POS_BLOCK_FUTURE_TIME_LIMIT 60*20
|
||||
|
|
|
|||
|
|
@ -188,14 +188,9 @@ namespace currency
|
|||
|
||||
if (pos)
|
||||
{
|
||||
txin_to_key posin;
|
||||
posin.amount = pe.amount;
|
||||
posin.key_offsets.push_back(pe.index);
|
||||
posin.k_image = pe.keyimage;
|
||||
tx.vin.push_back(posin);
|
||||
//reserve place for ring signature
|
||||
tx.signatures.resize(1);
|
||||
tx.signatures[0].resize(posin.key_offsets.size());
|
||||
// just placeholders, they will be filled in wallet2::prepare_and_sign_pos_block()
|
||||
tx.vin.emplace_back(std::move(txin_to_key()));
|
||||
tx.signatures.emplace_back();
|
||||
}
|
||||
|
||||
uint64_t no = 0;
|
||||
|
|
|
|||
|
|
@ -359,6 +359,10 @@ namespace currency
|
|||
#pragma pack (push, 1)
|
||||
struct out_entry
|
||||
{
|
||||
out_entry() = default;
|
||||
out_entry(uint64_t global_amount_index, const crypto::public_key& stealth_address)
|
||||
: global_amount_index(global_amount_index), out_key(stealth_address)
|
||||
{}
|
||||
uint64_t global_amount_index;
|
||||
crypto::public_key out_key;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ namespace tools
|
|||
m_last_pow_block_h(0),
|
||||
m_minimum_height(WALLET_MINIMUM_HEIGHT_UNSET_CONST),
|
||||
m_pos_mint_packing_size(WALLET_DEFAULT_POS_MINT_PACKING_SIZE),
|
||||
m_required_decoys_count(CURRENCY_DEFAULT_DECOY_SET_SIZE),
|
||||
m_current_wallet_file_size(0),
|
||||
m_use_deffered_global_outputs(false),
|
||||
m_disable_tor_relay(false),
|
||||
|
|
@ -3476,52 +3477,125 @@ bool wallet2::get_pos_entries(currency::COMMAND_RPC_SCAN_POS::request& req)
|
|||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::prepare_and_sign_pos_block(currency::block& b,
|
||||
const currency::pos_entry& pos_info,
|
||||
const crypto::public_key& source_tx_pub_key,
|
||||
uint64_t in_tx_output_index,
|
||||
const std::vector<const crypto::public_key*>& keys_ptrs)
|
||||
bool wallet2::prepare_and_sign_pos_block(const currency::pos_entry& pe, currency::block& b)
|
||||
{
|
||||
//generate coinbase transaction
|
||||
WLT_CHECK_AND_ASSERT_MES(b.miner_tx.vin[0].type() == typeid(currency::txin_gen), false, "Wrong output input in transaction");
|
||||
WLT_CHECK_AND_ASSERT_MES(b.miner_tx.vin[1].type() == typeid(currency::txin_to_key), false, "Wrong output input in transaction");
|
||||
auto& txin = boost::get<currency::txin_to_key>(b.miner_tx.vin[1]);
|
||||
txin.k_image = pos_info.keyimage;
|
||||
WLT_CHECK_AND_ASSERT_MES(b.miner_tx.signatures.size() == 1 && b.miner_tx.signatures[0].size() == txin.key_offsets.size(),
|
||||
false, "Wrong signatures amount in coinbase transacton");
|
||||
bool r = false;
|
||||
WLT_CHECK_AND_ASSERT_MES(pe.wallet_index < m_transfers.size(), false, "invalid pe.wallet_index: " << pe.wallet_index);
|
||||
const transfer_details& td = m_transfers[pe.wallet_index];
|
||||
const transaction& source_tx = td.m_ptx_wallet_info->m_tx;
|
||||
const crypto::public_key source_tx_pub_key = get_tx_pub_key_from_extra(source_tx);
|
||||
WLT_CHECK_AND_ASSERT_MES(td.m_internal_output_index < source_tx.vout.size(), false, "invalid td.m_internal_output_index: " << td.m_internal_output_index);
|
||||
const currency::tx_out& stake_out = source_tx.vout[td.m_internal_output_index];
|
||||
|
||||
// calculate stake_out_derivation and secret_x (derived ephemeral secret key)
|
||||
crypto::key_derivation stake_out_derivation = AUTO_VAL_INIT(stake_out_derivation);
|
||||
r = crypto::generate_key_derivation(source_tx_pub_key, m_account.get_keys().view_secret_key, stake_out_derivation); // d = 8 * v * R
|
||||
WLT_CHECK_AND_ASSERT_MES(r, false, "generate_key_derivation failed, tid: " << pe.wallet_index << ", source_tx: " << get_transaction_hash(source_tx));
|
||||
crypto::secret_key secret_x = AUTO_VAL_INIT(secret_x);
|
||||
crypto::derive_secret_key(stake_out_derivation, td.m_internal_output_index, m_account.get_keys().spend_secret_key, secret_x); // x = Hs(8 * v * R, i) + s
|
||||
|
||||
WLT_CHECK_AND_ASSERT_MES(b.miner_tx.vin[0].type() == typeid(currency::txin_gen), false, "Wrong input 0 type in transaction: " << b.miner_tx.vin[0].type().name());
|
||||
WLT_CHECK_AND_ASSERT_MES(b.miner_tx.vin[1].type() == typeid(currency::txin_to_key), false, "Wrong input 1 type in transaction: " << b.miner_tx.vin[1].type().name());
|
||||
WLT_CHECK_AND_ASSERT_MES(b.miner_tx.signatures.size() == 1, false, "Wrong sig prepared in PoS block");
|
||||
WLT_CHECK_AND_ASSERT_MES(stake_out.target.type() == typeid(currency::txout_to_key), false, "wrong type_id in source transaction in coinbase tx");
|
||||
|
||||
std::vector<crypto::signature>& sig = b.miner_tx.signatures[0];
|
||||
txin_to_key& stake_input = boost::get<currency::txin_to_key>(b.miner_tx.vin[1]);
|
||||
const txout_to_key& stake_out_target = boost::get<txout_to_key>(stake_out.target);
|
||||
|
||||
// partially fill stake input
|
||||
stake_input.k_image = pe.keyimage;
|
||||
stake_input.amount = stake_out.amount;
|
||||
|
||||
|
||||
// get decoys outputs and construct miner tx
|
||||
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response decoys_resp = AUTO_VAL_INIT(decoys_resp);
|
||||
std::vector<const crypto::public_key*> ring;
|
||||
uint64_t secret_index = 0; // index of the real stake output
|
||||
if (m_required_decoys_count > 0)
|
||||
{
|
||||
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request decoys_req = AUTO_VAL_INIT(decoys_req);
|
||||
decoys_req.use_forced_mix_outs = false;
|
||||
decoys_req.outs_count = m_required_decoys_count + 1; // one more to be able to skip a decoy in case it hits the real output
|
||||
decoys_req.amounts.push_back(pe.amount); // request one batch of decoys
|
||||
|
||||
//derive secret key
|
||||
crypto::key_derivation pos_coin_derivation = AUTO_VAL_INIT(pos_coin_derivation);
|
||||
bool r = crypto::generate_key_derivation(source_tx_pub_key,
|
||||
m_account.get_keys().view_secret_key,
|
||||
pos_coin_derivation);
|
||||
r = m_core_proxy->call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS(decoys_req, decoys_resp);
|
||||
// TODO @#@# do we need these exceptions?
|
||||
THROW_IF_FALSE_WALLET_EX(r, error::no_connection_to_daemon, "getrandom_outs.bin");
|
||||
THROW_IF_FALSE_WALLET_EX(decoys_resp.status != API_RETURN_CODE_BUSY, error::daemon_busy, "getrandom_outs.bin");
|
||||
THROW_IF_FALSE_WALLET_EX(decoys_resp.status == API_RETURN_CODE_OK, error::get_random_outs_error, decoys_resp.status);
|
||||
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(decoys_resp.outs.size() == 1, "got wrong number of decoys batches: " << decoys_resp.outs.size());
|
||||
|
||||
// we expect that less decoys can be returned than requested, we will use them all anyway
|
||||
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(decoys_resp.outs[0].outs.size() <= m_required_decoys_count + 1, "for PoS stake tx got greater decoys to mix than requested: " << decoys_resp.outs[0].outs.size() << " < " << m_required_decoys_count + 1);
|
||||
|
||||
WLT_CHECK_AND_ASSERT_MES(r, false, "internal error: pos coin base generator: failed to generate_key_derivation("
|
||||
<< source_tx_pub_key
|
||||
<< ", view secret key: " << m_account.get_keys().view_secret_key << ")");
|
||||
auto& ring_candidates = decoys_resp.outs[0].outs;
|
||||
ring_candidates.emplace_front(td.m_global_output_index, stake_out_target.key);
|
||||
|
||||
crypto::secret_key derived_secret_ephemeral_key = AUTO_VAL_INIT(derived_secret_ephemeral_key);
|
||||
crypto::derive_secret_key(pos_coin_derivation,
|
||||
in_tx_output_index,
|
||||
m_account.get_keys().spend_secret_key,
|
||||
derived_secret_ephemeral_key);
|
||||
std::unordered_set<uint64_t> used_gindices;
|
||||
size_t good_outs_count = 0;
|
||||
for(auto it = ring_candidates.begin(); it != ring_candidates.end(); )
|
||||
{
|
||||
if (used_gindices.count(it->global_amount_index) != 0)
|
||||
{
|
||||
it = ring_candidates.erase(it);
|
||||
continue;
|
||||
}
|
||||
used_gindices.insert(it->global_amount_index);
|
||||
if (++good_outs_count == m_required_decoys_count + 1)
|
||||
{
|
||||
ring_candidates.erase(++it, ring_candidates.end());
|
||||
break;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
// won't assert that ring_candidates.size() == m_required_decoys_count + 1 here as we will use all the decoys anyway
|
||||
if (ring_candidates.size() < m_required_decoys_count + 1)
|
||||
LOG_PRINT_YELLOW("PoS: using " << ring_candidates.size() - 1 << " decoys for mining tx, while " << m_required_decoys_count << " are required", LOG_LEVEL_1);
|
||||
|
||||
ring_candidates.sort([](auto& l, auto& r){ return l.global_amount_index < r.global_amount_index; }); // sort them now (note absolute_output_offsets_to_relative() below)
|
||||
|
||||
uint64_t i = 0;
|
||||
for(auto& el : ring_candidates)
|
||||
{
|
||||
uint64_t gindex = el.global_amount_index;
|
||||
if (gindex == td.m_global_output_index)
|
||||
secret_index = i;
|
||||
++i;
|
||||
ring.emplace_back(&el.out_key);
|
||||
stake_input.key_offsets.push_back(el.global_amount_index);
|
||||
}
|
||||
stake_input.key_offsets = absolute_output_offsets_to_relative(stake_input.key_offsets);
|
||||
}
|
||||
else
|
||||
{
|
||||
// no decoys, the ring consist of one element -- the real stake output
|
||||
ring.emplace_back(&stake_out_target.key);
|
||||
stake_input.key_offsets.push_back(td.m_global_output_index);
|
||||
}
|
||||
|
||||
// sign block actually in coinbase transaction
|
||||
crypto::hash block_hash = currency::get_block_hash(b);
|
||||
|
||||
crypto::generate_ring_signature(block_hash,
|
||||
txin.k_image,
|
||||
keys_ptrs,
|
||||
derived_secret_ephemeral_key,
|
||||
0,
|
||||
&b.miner_tx.signatures[0][0]);
|
||||
// generate sring signature
|
||||
sig.resize(ring.size());
|
||||
crypto::generate_ring_signature(block_hash, stake_input.k_image, ring, secret_x, secret_index, sig.data());
|
||||
|
||||
WLT_LOG_L4("GENERATED RING SIGNATURE: block_id " << block_hash
|
||||
<< "txin.k_image" << txin.k_image
|
||||
<< "key_ptr:" << *keys_ptrs[0]
|
||||
<< "signature:" << b.miner_tx.signatures[0][0]);
|
||||
if (epee::log_space::get_set_log_detalisation_level() >= LOG_LEVEL_4)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "GENERATED RING SIGNATURE for PoS block coinbase:" << ENDL <<
|
||||
" block hash: " << block_hash << ENDL <<
|
||||
" key image: " << stake_input.k_image << ENDL <<
|
||||
" ring:" << ENDL;
|
||||
for(auto el: ring)
|
||||
ss << " " << *el << ENDL;
|
||||
ss << " signature:" << ENDL;
|
||||
for(auto el: sig)
|
||||
ss << " " << el << ENDL;
|
||||
WLT_LOG_L4(ss.str());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -3666,19 +3740,20 @@ bool wallet2::build_minted_block(const currency::COMMAND_RPC_SCAN_POS::request&
|
|||
WLT_LOG_GREEN("Found kernel, constructing block", LOG_LEVEL_0);
|
||||
|
||||
CHECK_AND_NO_ASSERT_MES(rsp.index < req.pos_entries.size(), false, "call_COMMAND_RPC_SCAN_POS returned wrong index: " << rsp.index << ", expected less then " << req.pos_entries.size());
|
||||
const pos_entry& pe = req.pos_entries[rsp.index];
|
||||
|
||||
currency::COMMAND_RPC_GETBLOCKTEMPLATE::request tmpl_req = AUTO_VAL_INIT(tmpl_req);
|
||||
currency::COMMAND_RPC_GETBLOCKTEMPLATE::response tmpl_rsp = AUTO_VAL_INIT(tmpl_rsp);
|
||||
tmpl_req.wallet_address = get_account_address_as_str(miner_address);
|
||||
tmpl_req.stakeholder_address = get_account_address_as_str(m_account.get_public_address());
|
||||
tmpl_req.pos_block = true;
|
||||
tmpl_req.pos_amount = req.pos_entries[rsp.index].amount;
|
||||
tmpl_req.pos_index = req.pos_entries[rsp.index].index;
|
||||
tmpl_req.pos_amount = pe.amount;
|
||||
tmpl_req.pos_index = pe.index;
|
||||
tmpl_req.extra_text = get_extra_text_for_block(m_chain.get_top_block_height()); // m_miner_text_info;
|
||||
tmpl_req.stake_unlock_time = req.pos_entries[rsp.index].stake_unlock_time;
|
||||
tmpl_req.stake_unlock_time = pe.stake_unlock_time;
|
||||
|
||||
// mark stake source as spent and make sure it will be restored in case of error
|
||||
const std::vector<uint64_t> stake_transfer_idx_vec{ req.pos_entries[rsp.index].wallet_index };
|
||||
const std::vector<uint64_t> stake_transfer_idx_vec{ pe.wallet_index };
|
||||
mark_transfers_as_spent(stake_transfer_idx_vec, "stake source");
|
||||
bool gracefull_leaving = false;
|
||||
auto stake_transfer_spent_flag_restorer = epee::misc_utils::create_scope_leave_handler([&](){
|
||||
|
|
@ -3710,27 +3785,17 @@ bool wallet2::build_minted_block(const currency::COMMAND_RPC_SCAN_POS::request&
|
|||
}
|
||||
|
||||
std::vector<const crypto::public_key*> keys_ptrs;
|
||||
WLT_CHECK_AND_ASSERT_MES(req.pos_entries[rsp.index].wallet_index < m_transfers.size(),
|
||||
WLT_CHECK_AND_ASSERT_MES(pe.wallet_index < m_transfers.size(),
|
||||
false, "Wrong wallet_index at generating coinbase transacton");
|
||||
|
||||
const auto& target = m_transfers[req.pos_entries[rsp.index].wallet_index].m_ptx_wallet_info->m_tx.vout[m_transfers[req.pos_entries[rsp.index].wallet_index].m_internal_output_index].target;
|
||||
WLT_CHECK_AND_ASSERT_MES(target.type() == typeid(currency::txout_to_key), false, "wrong type_id in source transaction in coinbase tx");
|
||||
|
||||
const currency::txout_to_key& txtokey = boost::get<currency::txout_to_key>(target);
|
||||
keys_ptrs.push_back(&txtokey.key);
|
||||
|
||||
// set a real timestamp
|
||||
b.timestamp = rsp.block_timestamp;
|
||||
uint64_t current_timestamp = m_core_runtime_config.get_core_time();
|
||||
set_block_datetime(current_timestamp, b);
|
||||
WLT_LOG_MAGENTA("Applying actual timestamp: " << current_timestamp, LOG_LEVEL_0);
|
||||
WLT_LOG_MAGENTA("Applying actual timestamp: " << current_timestamp, LOG_LEVEL_2);
|
||||
|
||||
//sign block
|
||||
res = prepare_and_sign_pos_block(b,
|
||||
req.pos_entries[rsp.index],
|
||||
get_tx_pub_key_from_extra(m_transfers[req.pos_entries[rsp.index].wallet_index].m_ptx_wallet_info->m_tx),
|
||||
m_transfers[req.pos_entries[rsp.index].wallet_index].m_internal_output_index,
|
||||
keys_ptrs);
|
||||
res = prepare_and_sign_pos_block(pe, b);
|
||||
WLT_CHECK_AND_ASSERT_MES(res, false, "Failed to prepare_and_sign_pos_block");
|
||||
|
||||
WLT_LOG_GREEN("Block " << get_block_hash(b) << " @ " << get_block_height(b) << " has been constructed, sending to core...", LOG_LEVEL_0);
|
||||
|
|
|
|||
|
|
@ -545,6 +545,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);
|
||||
void set_pos_required_decoys_count(size_t v) { m_required_decoys_count = v; }
|
||||
void set_minimum_height(uint64_t h);
|
||||
std::shared_ptr<i_core_proxy> get_core_proxy();
|
||||
uint64_t balance() const;
|
||||
|
|
@ -802,11 +803,7 @@ namespace tools
|
|||
//next functions in public area only becausce of test_generator
|
||||
//TODO: Need refactoring - remove it back to private zone
|
||||
void set_genesis(const crypto::hash& genesis_hash);
|
||||
bool prepare_and_sign_pos_block(currency::block& b,
|
||||
const currency::pos_entry& pos_info,
|
||||
const crypto::public_key& source_tx_pub_key,
|
||||
uint64_t in_tx_output_index,
|
||||
const std::vector<const crypto::public_key*>& keys_ptrs);
|
||||
bool prepare_and_sign_pos_block(const currency::pos_entry& pe, currency::block& b);
|
||||
void process_new_blockchain_entry(const currency::block& b,
|
||||
const currency::block_direct_data_entry& bche,
|
||||
const crypto::hash& bl_id,
|
||||
|
|
@ -1036,6 +1033,7 @@ private:
|
|||
std::atomic<uint64_t> m_last_bc_timestamp;
|
||||
bool m_do_rise_transfer;
|
||||
uint64_t m_pos_mint_packing_size;
|
||||
size_t m_required_decoys_count;
|
||||
|
||||
transfer_container m_transfers;
|
||||
multisig_transfer_container m_multisig_transfers;
|
||||
|
|
|
|||
|
|
@ -382,7 +382,7 @@ bool test_generator::sign_block(currency::block& b,
|
|||
|
||||
std::vector<const crypto::public_key*> keys_ptrs;
|
||||
keys_ptrs.push_back(&out_key);
|
||||
r = w.prepare_and_sign_pos_block(b, pe, source_tx_pub_key, out_i, keys_ptrs);
|
||||
r = w.prepare_and_sign_pos_block(pe, b);
|
||||
CHECK_AND_ASSERT_THROW_MES(r,"Failed to prepare_and_sign_pos_block()");
|
||||
|
||||
return true;
|
||||
|
|
@ -426,6 +426,7 @@ bool test_generator::build_wallets(const blockchain_vector& blocks,
|
|||
wallets.back()->get_account().set_createtime(0);
|
||||
wallets.back()->set_core_proxy(tmp_proxy);
|
||||
wallets.back()->set_minimum_height(0);
|
||||
wallets.back()->set_pos_required_decoys_count(0);
|
||||
|
||||
currency::core_runtime_config pc = cc;
|
||||
pc.min_coinstake_age = TESTS_POS_CONFIG_MIN_COINSTAKE_AGE;
|
||||
|
|
|
|||
|
|
@ -891,8 +891,9 @@ int main(int argc, char* argv[])
|
|||
// GENERATE_AND_PLAY(pos_wallet_minting_same_amount_diff_outs); // Long test! Takes ~10 hours to simulate 6000 blocks on 2015 middle-end computer
|
||||
//GENERATE_AND_PLAY(pos_emission_test); // Long test! by demand only
|
||||
GENERATE_AND_PLAY(pos_wallet_big_block_test);
|
||||
GENERATE_AND_PLAY(block_template_against_txs_size);
|
||||
//GENERATE_AND_PLAY(block_template_against_txs_size); // Long test! by demand only
|
||||
GENERATE_AND_PLAY(pos_altblocks_validation);
|
||||
GENERATE_AND_PLAY(pos_mining_with_decoys);
|
||||
|
||||
// alternative blocks and generic chain-switching tests
|
||||
GENERATE_AND_PLAY(gen_chain_switch_pow_pos);
|
||||
|
|
|
|||
|
|
@ -139,3 +139,121 @@ bool gen_pos_basic_tests::check_exchange_1(currency::core& c, size_t ev_index, c
|
|||
CHECK_EQ(offers.size(), 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
pos_mining_with_decoys::pos_mining_with_decoys()
|
||||
{
|
||||
REGISTER_CALLBACK_METHOD(pos_mining_with_decoys, c1);
|
||||
}
|
||||
|
||||
bool pos_mining_with_decoys::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
uint64_t ts = test_core_time::get_time();
|
||||
m_accounts.resize(TOTAL_ACCS_COUNT);
|
||||
currency::account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); miner_acc.set_createtime(ts);
|
||||
currency::account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); alice_acc.set_createtime(ts);
|
||||
currency::account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate(); bob_acc.set_createtime(ts);
|
||||
currency::account_base& carol_acc = m_accounts[CAROL_ACC_IDX]; carol_acc.generate(); carol_acc.set_createtime(ts);
|
||||
|
||||
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, ts);
|
||||
DO_CALLBACK(events, "configure_core"); // default configure_core callback will initialize core runtime config with m_hardforks
|
||||
REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1);
|
||||
|
||||
bool r = false;
|
||||
std::vector<tx_source_entry> sources;
|
||||
r = fill_tx_sources(sources, events, blk_0r, miner_acc.get_keys(), 2 * COIN, 0);
|
||||
CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources failed");
|
||||
std::vector<tx_destination_entry> destinations;
|
||||
destinations.emplace_back(47 * TESTS_DEFAULT_FEE, alice_acc.get_public_address());
|
||||
destinations.emplace_back(47 * TESTS_DEFAULT_FEE, miner_acc.get_public_address()); // as a decoy for Alice
|
||||
destinations.emplace_back(5 * TESTS_DEFAULT_FEE, bob_acc.get_public_address());
|
||||
destinations.emplace_back(COIN, carol_acc.get_public_address());
|
||||
|
||||
transaction tx_0{};
|
||||
r = construct_tx(miner_acc.get_keys(), sources, destinations, empty_attachment, tx_0, 0);
|
||||
CHECK_AND_ASSERT_MES(r, false, "construct_tx failed");
|
||||
events.push_back(tx_0);
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_acc, tx_0);
|
||||
|
||||
REWIND_BLOCKS_N(events, blk_1r, blk_1, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
|
||||
|
||||
DO_CALLBACK(events, "c1");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pos_mining_with_decoys::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
||||
{
|
||||
bool r = false;
|
||||
CHECK_AND_ASSERT_MES(!c.get_blockchain_storage().get_core_runtime_config().is_hardfork_active_for_height(4, c.get_top_block_height()), false, "HF4 should not be active");
|
||||
|
||||
std::shared_ptr<tools::wallet2> miner_wlt = init_playtime_test_wallet(events, c, m_accounts[MINER_ACC_IDX]);
|
||||
miner_wlt->refresh();
|
||||
|
||||
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, m_accounts[ALICE_ACC_IDX]);
|
||||
alice_wlt->refresh();
|
||||
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt, "Alice", 47 * TESTS_DEFAULT_FEE, INVALID_BALANCE_VAL, 47 * TESTS_DEFAULT_FEE), false, "");
|
||||
|
||||
std::shared_ptr<tools::wallet2> bob_wlt = init_playtime_test_wallet(events, c, m_accounts[BOB_ACC_IDX]);
|
||||
bob_wlt->refresh();
|
||||
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*bob_wlt, "Bob", 5 * TESTS_DEFAULT_FEE, INVALID_BALANCE_VAL, 5 * TESTS_DEFAULT_FEE), false, "");
|
||||
|
||||
std::shared_ptr<tools::wallet2> carol_wlt = init_playtime_test_wallet(events, c, m_accounts[CAROL_ACC_IDX]);
|
||||
carol_wlt->refresh();
|
||||
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*carol_wlt, "Carol", COIN, INVALID_BALANCE_VAL, COIN), false, "");
|
||||
|
||||
|
||||
// 1. Alice should be able to mine a PoS block with 1 decoys (ring size == 2)
|
||||
size_t top_block_height = c.get_top_block_height();
|
||||
|
||||
r = alice_wlt->try_mint_pos(m_accounts[ALICE_ACC_IDX].get_public_address());
|
||||
CHECK_AND_ASSERT_MES(r, false, "try_mint_pos failed");
|
||||
|
||||
{
|
||||
block b{};
|
||||
CHECK_AND_ASSERT_MES(c.get_blockchain_storage().get_top_block(b), false, "");
|
||||
CHECK_AND_ASSERT_MES(get_block_height(b) == top_block_height + 1, false, "unexpected top block height");
|
||||
|
||||
txin_to_key& intk = boost::get<txin_to_key>(b.miner_tx.vin[1]);
|
||||
CHECK_AND_ASSERT_MES(intk.amount == 47 * TESTS_DEFAULT_FEE, false, "incorrect amount: " << intk.amount);
|
||||
CHECK_AND_ASSERT_MES(intk.key_offsets.size() == 2, false, "unexpected ring size: " << intk.key_offsets.size());
|
||||
}
|
||||
|
||||
|
||||
// 2. Bob should only be able to mine a PoS block with zero decoys (ring size == 1)
|
||||
top_block_height = c.get_top_block_height();
|
||||
|
||||
r = bob_wlt->try_mint_pos(m_accounts[BOB_ACC_IDX].get_public_address());
|
||||
CHECK_AND_ASSERT_MES(r, false, "try_mint_pos failed");
|
||||
|
||||
{
|
||||
block b{};
|
||||
CHECK_AND_ASSERT_MES(c.get_blockchain_storage().get_top_block(b), false, "");
|
||||
CHECK_AND_ASSERT_MES(get_block_height(b) == top_block_height + 1, false, "unexpected top block height");
|
||||
|
||||
txin_to_key& intk = boost::get<txin_to_key>(b.miner_tx.vin[1]);
|
||||
CHECK_AND_ASSERT_MES(intk.amount == 5 * TESTS_DEFAULT_FEE, false, "incorrect amount: " << intk.amount);
|
||||
CHECK_AND_ASSERT_MES(intk.key_offsets.size() == 1, false, "unexpected ring size: " << intk.key_offsets.size());
|
||||
}
|
||||
|
||||
|
||||
// 3. Carol should only be able to mine a PoS block with CURRENCY_DEFAULT_DECOY_SET_SIZE decoys (ring size == CURRENCY_DEFAULT_DECOY_SET_SIZE + 1)
|
||||
top_block_height = c.get_top_block_height();
|
||||
|
||||
r = carol_wlt->try_mint_pos(m_accounts[CAROL_ACC_IDX].get_public_address());
|
||||
CHECK_AND_ASSERT_MES(r, false, "try_mint_pos failed");
|
||||
|
||||
{
|
||||
block b{};
|
||||
CHECK_AND_ASSERT_MES(c.get_blockchain_storage().get_top_block(b), false, "");
|
||||
CHECK_AND_ASSERT_MES(get_block_height(b) == top_block_height + 1, false, "unexpected top block height");
|
||||
|
||||
txin_to_key& intk = boost::get<txin_to_key>(b.miner_tx.vin[1]);
|
||||
CHECK_AND_ASSERT_MES(intk.amount == COIN, false, "incorrect amount: " << intk.amount);
|
||||
CHECK_AND_ASSERT_MES(intk.key_offsets.size() == CURRENCY_DEFAULT_DECOY_SET_SIZE + 1, false, "unexpected ring size: " << intk.key_offsets.size());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#pragma once
|
||||
#include "chaingen.h"
|
||||
#include "wallet_tests_basic.h"
|
||||
|
||||
struct gen_pos_basic_tests : public test_chain_unit_base
|
||||
{
|
||||
|
|
@ -22,3 +23,12 @@ struct gen_pos_basic_tests : public test_chain_unit_base
|
|||
bool check_exchange_1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct pos_mining_with_decoys : public wallet_test
|
||||
{
|
||||
pos_mining_with_decoys();
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
bool configure_core(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