mirror of
https://codeberg.org/anoncontributorxmr/monero.git
synced 2024-11-30 03:03:17 +00:00
functional_tests: add more blockchain related tests
Related to emission, reorgs, getting tx data back, output distribution and histogram
This commit is contained in:
parent
21b1ac1dd2
commit
064ab12340
11 changed files with 399 additions and 77 deletions
|
@ -142,6 +142,16 @@ namespace cryptonote
|
|||
std::string print_money(uint64_t amount, unsigned int decimal_point = -1);
|
||||
//---------------------------------------------------------------
|
||||
template<class t_object>
|
||||
bool t_serializable_object_from_blob(t_object& to, const blobdata& b_blob)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << b_blob;
|
||||
binary_archive<false> ba(ss);
|
||||
bool r = ::serialization::serialize(ba, to);
|
||||
return r;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
template<class t_object>
|
||||
bool t_serializable_object_to_blob(const t_object& to, blobdata& b_blob)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
|
|
@ -576,7 +576,8 @@ namespace cryptonote
|
|||
//we lucky!
|
||||
++m_config.current_extra_message_index;
|
||||
MGINFO_GREEN("Found block " << get_block_hash(b) << " at height " << height << " for difficulty: " << local_diff);
|
||||
if(!m_phandler->handle_block_found(b))
|
||||
cryptonote::block_verification_context bvc;
|
||||
if(!m_phandler->handle_block_found(b, bvc) || !bvc.m_added_to_main_chain)
|
||||
{
|
||||
--m_config.current_extra_message_index;
|
||||
}else
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <boost/logic/tribool_fwd.hpp>
|
||||
#include <atomic>
|
||||
#include "cryptonote_basic.h"
|
||||
#include "verification_context.h"
|
||||
#include "difficulty.h"
|
||||
#include "math_helper.h"
|
||||
#ifdef _WIN32
|
||||
|
@ -45,7 +46,7 @@ namespace cryptonote
|
|||
|
||||
struct i_miner_handler
|
||||
{
|
||||
virtual bool handle_block_found(block& b) = 0;
|
||||
virtual bool handle_block_found(block& b, block_verification_context &bvc) = 0;
|
||||
virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce) = 0;
|
||||
protected:
|
||||
~i_miner_handler(){};
|
||||
|
|
|
@ -1008,7 +1008,7 @@ bool Blockchain::rollback_blockchain_switching(std::list<block>& original_chain,
|
|||
//------------------------------------------------------------------
|
||||
// This function attempts to switch to an alternate chain, returning
|
||||
// boolean based on success therein.
|
||||
bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::iterator>& alt_chain, bool discard_disconnected_chain)
|
||||
bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::const_iterator>& alt_chain, bool discard_disconnected_chain)
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
|
@ -1109,7 +1109,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::
|
|||
//------------------------------------------------------------------
|
||||
// This function calculates the difficulty target for the block being added to
|
||||
// an alternate chain.
|
||||
difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::iterator>& alt_chain, block_extended_info& bei) const
|
||||
difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::const_iterator>& alt_chain, block_extended_info& bei) const
|
||||
{
|
||||
if (m_fixed_difficulty)
|
||||
{
|
||||
|
@ -1351,7 +1351,7 @@ uint64_t Blockchain::get_current_cumulative_block_weight_median() const
|
|||
// in a lot of places. That flag is not referenced in any of the code
|
||||
// nor any of the makefiles, howeve. Need to look into whether or not it's
|
||||
// necessary at all.
|
||||
bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce)
|
||||
bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce)
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
size_t median_weight;
|
||||
|
@ -1361,8 +1361,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
|
|||
m_tx_pool.lock();
|
||||
const auto unlock_guard = epee::misc_utils::create_scope_leave_handler([&]() { m_tx_pool.unlock(); });
|
||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
height = m_db->height();
|
||||
if (m_btc_valid) {
|
||||
if (m_btc_valid && !from_block) {
|
||||
// The pool cookie is atomic. The lack of locking is OK, as if it changes
|
||||
// just as we compare it, we'll just use a slightly old template, but
|
||||
// this would be the case anyway if we'd lock, and the change happened
|
||||
|
@ -1376,13 +1375,75 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
|
|||
expected_reward = m_btc_expected_reward;
|
||||
return true;
|
||||
}
|
||||
MDEBUG("Not using cached template: address " << (!memcmp(&miner_address, &m_btc_address, sizeof(cryptonote::account_public_address))) << ", nonce " << (m_btc_nonce == ex_nonce) << ", cookie " << (m_btc_pool_cookie == m_tx_pool.cookie()));
|
||||
MDEBUG("Not using cached template: address " << (!memcmp(&miner_address, &m_btc_address, sizeof(cryptonote::account_public_address))) << ", nonce " << (m_btc_nonce == ex_nonce) << ", cookie " << (m_btc_pool_cookie == m_tx_pool.cookie()) << ", from_block " << (!!from_block));
|
||||
invalidate_block_template_cache();
|
||||
}
|
||||
|
||||
if (from_block)
|
||||
{
|
||||
//build alternative subchain, front -> mainchain, back -> alternative head
|
||||
//block is not related with head of main chain
|
||||
//first of all - look in alternative chains container
|
||||
auto it_prev = m_alternative_chains.find(*from_block);
|
||||
bool parent_in_main = m_db->block_exists(*from_block);
|
||||
if(it_prev == m_alternative_chains.end() && !parent_in_main)
|
||||
{
|
||||
MERROR("Unknown from block");
|
||||
return false;
|
||||
}
|
||||
|
||||
//we have new block in alternative chain
|
||||
std::list<blocks_ext_by_hash::const_iterator> alt_chain;
|
||||
block_verification_context bvc = boost::value_initialized<block_verification_context>();
|
||||
std::vector<uint64_t> timestamps;
|
||||
if (!build_alt_chain(*from_block, alt_chain, timestamps, bvc))
|
||||
return false;
|
||||
|
||||
if (parent_in_main)
|
||||
{
|
||||
cryptonote::block prev_block;
|
||||
CHECK_AND_ASSERT_MES(get_block_by_hash(*from_block, prev_block), false, "From block not found"); // TODO
|
||||
uint64_t from_block_height = cryptonote::get_block_height(prev_block);
|
||||
height = from_block_height + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
height = alt_chain.back()->second.height + 1;
|
||||
}
|
||||
b.major_version = m_hardfork->get_ideal_version(height);
|
||||
b.minor_version = m_hardfork->get_ideal_version();
|
||||
b.prev_id = *from_block;
|
||||
|
||||
// cheat and use the weight of the block we start from, virtually certain to be acceptable
|
||||
// and use 1.9 times rather than 2 times so we're even more sure
|
||||
if (parent_in_main)
|
||||
{
|
||||
median_weight = m_db->get_block_weight(height - 1);
|
||||
already_generated_coins = m_db->get_block_already_generated_coins(height - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
median_weight = it_prev->second.block_cumulative_weight - it_prev->second.block_cumulative_weight / 20;
|
||||
already_generated_coins = alt_chain.back()->second.already_generated_coins;
|
||||
}
|
||||
|
||||
// FIXME: consider moving away from block_extended_info at some point
|
||||
block_extended_info bei = boost::value_initialized<block_extended_info>();
|
||||
bei.bl = b;
|
||||
bei.height = alt_chain.size() ? it_prev->second.height + 1 : m_db->get_block_height(*from_block) + 1;
|
||||
|
||||
diffic = get_next_difficulty_for_alternative_chain(alt_chain, bei);
|
||||
}
|
||||
else
|
||||
{
|
||||
height = m_db->height();
|
||||
b.major_version = m_hardfork->get_current_version();
|
||||
b.minor_version = m_hardfork->get_ideal_version();
|
||||
b.prev_id = get_tail_id();
|
||||
median_weight = m_current_block_cumul_weight_limit / 2;
|
||||
diffic = get_difficulty_for_next_block();
|
||||
already_generated_coins = m_db->get_block_already_generated_coins(height - 1);
|
||||
}
|
||||
b.timestamp = time(NULL);
|
||||
|
||||
uint64_t median_ts;
|
||||
|
@ -1391,15 +1452,11 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
|
|||
b.timestamp = median_ts;
|
||||
}
|
||||
|
||||
diffic = get_difficulty_for_next_block();
|
||||
CHECK_AND_ASSERT_MES(diffic, false, "difficulty overhead.");
|
||||
|
||||
median_weight = m_current_block_cumul_weight_limit / 2;
|
||||
already_generated_coins = m_db->get_block_already_generated_coins(height - 1);
|
||||
|
||||
size_t txs_weight;
|
||||
uint64_t fee;
|
||||
if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, m_hardfork->get_current_version()))
|
||||
if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, b.major_version))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -1462,7 +1519,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
|
|||
block weight, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block weight
|
||||
*/
|
||||
//make blocks coin-base tx looks close to real coinbase tx to get truthful blob weight
|
||||
uint8_t hf_version = m_hardfork->get_current_version();
|
||||
uint8_t hf_version = b.major_version;
|
||||
size_t max_outs = hf_version >= 4 ? 1 : 11;
|
||||
bool r = construct_miner_tx(height, median_weight, already_generated_coins, txs_weight, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, first chance");
|
||||
|
@ -1517,6 +1574,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
|
|||
", cumulative weight " << cumulative_weight << " is now good");
|
||||
#endif
|
||||
|
||||
if (!from_block)
|
||||
cache_block_template(b, miner_address, ex_nonce, diffic, height, expected_reward, pool_cookie);
|
||||
return true;
|
||||
}
|
||||
|
@ -1524,9 +1582,14 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
|
|||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce)
|
||||
{
|
||||
return create_block_template(b, NULL, miner_address, diffic, height, expected_reward, ex_nonce);
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
// for an alternate chain, get the timestamps from the main chain to complete
|
||||
// the needed number of timestamps for the BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW.
|
||||
bool Blockchain::complete_timestamps_vector(uint64_t start_top_height, std::vector<uint64_t>& timestamps)
|
||||
bool Blockchain::complete_timestamps_vector(uint64_t start_top_height, std::vector<uint64_t>& timestamps) const
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
|
||||
|
@ -1546,6 +1609,52 @@ bool Blockchain::complete_timestamps_vector(uint64_t start_top_height, std::vect
|
|||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool Blockchain::build_alt_chain(const crypto::hash &prev_id, std::list<blocks_ext_by_hash::const_iterator>& alt_chain, std::vector<uint64_t> ×tamps, block_verification_context& bvc) const
|
||||
{
|
||||
//build alternative subchain, front -> mainchain, back -> alternative head
|
||||
blocks_ext_by_hash::const_iterator alt_it = m_alternative_chains.find(prev_id);
|
||||
timestamps.clear();
|
||||
while(alt_it != m_alternative_chains.end())
|
||||
{
|
||||
alt_chain.push_front(alt_it);
|
||||
timestamps.push_back(alt_it->second.bl.timestamp);
|
||||
alt_it = m_alternative_chains.find(alt_it->second.bl.prev_id);
|
||||
}
|
||||
|
||||
// if block to be added connects to known blocks that aren't part of the
|
||||
// main chain -- that is, if we're adding on to an alternate chain
|
||||
if(!alt_chain.empty())
|
||||
{
|
||||
// make sure alt chain doesn't somehow start past the end of the main chain
|
||||
CHECK_AND_ASSERT_MES(m_db->height() > alt_chain.front()->second.height, false, "main blockchain wrong height");
|
||||
|
||||
// make sure that the blockchain contains the block that should connect
|
||||
// this alternate chain with it.
|
||||
if (!m_db->block_exists(alt_chain.front()->second.bl.prev_id))
|
||||
{
|
||||
MERROR("alternate chain does not appear to connect to main chain...");
|
||||
return false;
|
||||
}
|
||||
|
||||
// make sure block connects correctly to the main chain
|
||||
auto h = m_db->get_block_hash_from_height(alt_chain.front()->second.height - 1);
|
||||
CHECK_AND_ASSERT_MES(h == alt_chain.front()->second.bl.prev_id, false, "alternative chain has wrong connection to main chain");
|
||||
complete_timestamps_vector(m_db->get_block_height(alt_chain.front()->second.bl.prev_id), timestamps);
|
||||
}
|
||||
// if block not associated with known alternate chain
|
||||
else
|
||||
{
|
||||
// if block parent is not part of main chain or an alternate chain,
|
||||
// we ignore it
|
||||
bool parent_in_main = m_db->block_exists(prev_id);
|
||||
CHECK_AND_ASSERT_MES(parent_in_main, false, "internal error: broken imperative condition: parent_in_main");
|
||||
|
||||
complete_timestamps_vector(m_db->get_block_height(prev_id), timestamps);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
// If a block is to be added and its parent block is not the current
|
||||
// main chain top block, then we need to see if we know about its parent block.
|
||||
// If its parent block is part of a known forked chain, then we need to see
|
||||
|
@ -1590,47 +1699,18 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
|
|||
if(it_prev != m_alternative_chains.end() || parent_in_main)
|
||||
{
|
||||
//we have new block in alternative chain
|
||||
|
||||
//build alternative subchain, front -> mainchain, back -> alternative head
|
||||
blocks_ext_by_hash::iterator alt_it = it_prev; //m_alternative_chains.find()
|
||||
std::list<blocks_ext_by_hash::iterator> alt_chain;
|
||||
std::list<blocks_ext_by_hash::const_iterator> alt_chain;
|
||||
std::vector<uint64_t> timestamps;
|
||||
while(alt_it != m_alternative_chains.end())
|
||||
{
|
||||
alt_chain.push_front(alt_it);
|
||||
timestamps.push_back(alt_it->second.bl.timestamp);
|
||||
alt_it = m_alternative_chains.find(alt_it->second.bl.prev_id);
|
||||
}
|
||||
|
||||
// if block to be added connects to known blocks that aren't part of the
|
||||
// main chain -- that is, if we're adding on to an alternate chain
|
||||
if(!alt_chain.empty())
|
||||
{
|
||||
// make sure alt chain doesn't somehow start past the end of the main chain
|
||||
CHECK_AND_ASSERT_MES(m_db->height() > alt_chain.front()->second.height, false, "main blockchain wrong height");
|
||||
|
||||
// make sure that the blockchain contains the block that should connect
|
||||
// this alternate chain with it.
|
||||
if (!m_db->block_exists(alt_chain.front()->second.bl.prev_id))
|
||||
{
|
||||
MERROR("alternate chain does not appear to connect to main chain...");
|
||||
if (!build_alt_chain(b.prev_id, alt_chain, timestamps, bvc))
|
||||
return false;
|
||||
}
|
||||
|
||||
// make sure block connects correctly to the main chain
|
||||
auto h = m_db->get_block_hash_from_height(alt_chain.front()->second.height - 1);
|
||||
CHECK_AND_ASSERT_MES(h == alt_chain.front()->second.bl.prev_id, false, "alternative chain has wrong connection to main chain");
|
||||
complete_timestamps_vector(m_db->get_block_height(alt_chain.front()->second.bl.prev_id), timestamps);
|
||||
}
|
||||
// if block not associated with known alternate chain
|
||||
else
|
||||
{
|
||||
// if block parent is not part of main chain or an alternate chain,
|
||||
// we ignore it
|
||||
CHECK_AND_ASSERT_MES(parent_in_main, false, "internal error: broken imperative condition: parent_in_main");
|
||||
|
||||
complete_timestamps_vector(m_db->get_block_height(b.prev_id), timestamps);
|
||||
}
|
||||
// FIXME: consider moving away from block_extended_info at some point
|
||||
block_extended_info bei = boost::value_initialized<block_extended_info>();
|
||||
bei.bl = b;
|
||||
const uint64_t prev_height = alt_chain.size() ? it_prev->second.height : m_db->get_block_height(b.prev_id);
|
||||
bei.height = prev_height + 1;
|
||||
uint64_t block_reward = get_outs_money_amount(b.miner_tx);
|
||||
bei.already_generated_coins = block_reward + (alt_chain.size() ? it_prev->second.already_generated_coins : m_db->get_block_already_generated_coins(prev_height));
|
||||
|
||||
// verify that the block's timestamp is within the acceptable range
|
||||
// (not earlier than the median of the last X blocks)
|
||||
|
@ -1641,11 +1721,6 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
|
|||
return false;
|
||||
}
|
||||
|
||||
// FIXME: consider moving away from block_extended_info at some point
|
||||
block_extended_info bei = boost::value_initialized<block_extended_info>();
|
||||
bei.bl = b;
|
||||
bei.height = alt_chain.size() ? it_prev->second.height + 1 : m_db->get_block_height(b.prev_id) + 1;
|
||||
|
||||
bool is_a_checkpoint;
|
||||
if(!m_checkpoints.check_block(bei.height, id, is_a_checkpoint))
|
||||
{
|
||||
|
|
|
@ -336,6 +336,7 @@ namespace cryptonote
|
|||
* @brief creates a new block to mine against
|
||||
*
|
||||
* @param b return-by-reference block to be filled in
|
||||
* @param from_block optional block hash to start mining from (main chain tip if NULL)
|
||||
* @param miner_address address new coins for the block will go to
|
||||
* @param di return-by-reference tells the miner what the difficulty target is
|
||||
* @param height return-by-reference tells the miner what height it's mining against
|
||||
|
@ -345,6 +346,7 @@ namespace cryptonote
|
|||
* @return true if block template filled in successfully, else false
|
||||
*/
|
||||
bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce);
|
||||
bool create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce);
|
||||
|
||||
/**
|
||||
* @brief checks if a block is known about with a given hash
|
||||
|
@ -1180,7 +1182,7 @@ namespace cryptonote
|
|||
*
|
||||
* @return false if the reorganization fails, otherwise true
|
||||
*/
|
||||
bool switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::iterator>& alt_chain, bool discard_disconnected_chain);
|
||||
bool switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::const_iterator>& alt_chain, bool discard_disconnected_chain);
|
||||
|
||||
/**
|
||||
* @brief removes the most recent block from the blockchain
|
||||
|
@ -1233,6 +1235,18 @@ namespace cryptonote
|
|||
*/
|
||||
bool handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc);
|
||||
|
||||
/**
|
||||
* @brief builds a list of blocks connecting a block to the main chain
|
||||
*
|
||||
* @param prev_id the block hash of the tip of the alt chain
|
||||
* @param alt_chain the chain to be added to
|
||||
* @param timestamps returns the timestamps of previous blocks
|
||||
* @param bvc the block verification context for error return
|
||||
*
|
||||
* @return true on success, false otherwise
|
||||
*/
|
||||
bool build_alt_chain(const crypto::hash &prev_id, std::list<blocks_ext_by_hash::const_iterator>& alt_chain, std::vector<uint64_t> ×tamps, block_verification_context& bvc) const;
|
||||
|
||||
/**
|
||||
* @brief gets the difficulty requirement for a new block on an alternate chain
|
||||
*
|
||||
|
@ -1241,7 +1255,7 @@ namespace cryptonote
|
|||
*
|
||||
* @return the difficulty requirement
|
||||
*/
|
||||
difficulty_type get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::iterator>& alt_chain, block_extended_info& bei) const;
|
||||
difficulty_type get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::const_iterator>& alt_chain, block_extended_info& bei) const;
|
||||
|
||||
/**
|
||||
* @brief sanity checks a miner transaction before validating an entire block
|
||||
|
@ -1401,7 +1415,7 @@ namespace cryptonote
|
|||
*
|
||||
* @return true unless start_height is greater than the current blockchain height
|
||||
*/
|
||||
bool complete_timestamps_vector(uint64_t start_height, std::vector<uint64_t>& timestamps);
|
||||
bool complete_timestamps_vector(uint64_t start_height, std::vector<uint64_t>& timestamps) const;
|
||||
|
||||
/**
|
||||
* @brief calculate the block weight limit for the next block to be added
|
||||
|
|
|
@ -1268,6 +1268,11 @@ namespace cryptonote
|
|||
return m_blockchain_storage.create_block_template(b, adr, diffic, height, expected_reward, ex_nonce);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce)
|
||||
{
|
||||
return m_blockchain_storage.create_block_template(b, prev_block, adr, diffic, height, expected_reward, ex_nonce);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const
|
||||
{
|
||||
return m_blockchain_storage.find_blockchain_supplement(qblock_ids, resp);
|
||||
|
@ -1321,9 +1326,9 @@ namespace cryptonote
|
|||
return bce;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::handle_block_found(block& b)
|
||||
bool core::handle_block_found(block& b, block_verification_context &bvc)
|
||||
{
|
||||
block_verification_context bvc = boost::value_initialized<block_verification_context>();
|
||||
bvc = boost::value_initialized<block_verification_context>();
|
||||
m_miner.pause();
|
||||
std::vector<block_complete_entry> blocks;
|
||||
try
|
||||
|
@ -1373,7 +1378,7 @@ namespace cryptonote
|
|||
|
||||
m_pprotocol->relay_block(arg, exclude_context);
|
||||
}
|
||||
return bvc.m_added_to_main_chain;
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
void core::on_synchronized()
|
||||
|
|
|
@ -195,10 +195,11 @@ namespace cryptonote
|
|||
* the network.
|
||||
*
|
||||
* @param b the block found
|
||||
* @param bvc returns the block verification flags
|
||||
*
|
||||
* @return true if the block was added to the main chain, otherwise false
|
||||
*/
|
||||
virtual bool handle_block_found( block& b);
|
||||
virtual bool handle_block_found(block& b, block_verification_context &bvc);
|
||||
|
||||
/**
|
||||
* @copydoc Blockchain::create_block_template
|
||||
|
@ -206,6 +207,7 @@ namespace cryptonote
|
|||
* @note see Blockchain::create_block_template
|
||||
*/
|
||||
virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce);
|
||||
virtual bool get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce);
|
||||
|
||||
/**
|
||||
* @brief called when a transaction is relayed
|
||||
|
|
|
@ -496,6 +496,7 @@ namespace cryptonote
|
|||
|
||||
cryptonote::COMMAND_RPC_GET_OUTPUTS_BIN::request req_bin;
|
||||
req_bin.outputs = req.outputs;
|
||||
req_bin.get_txid = req.get_txid;
|
||||
cryptonote::COMMAND_RPC_GET_OUTPUTS_BIN::response res_bin;
|
||||
if(!m_core.get_outs(req_bin, res_bin))
|
||||
{
|
||||
|
@ -1259,7 +1260,17 @@ namespace cryptonote
|
|||
cryptonote::blobdata blob_reserve;
|
||||
blob_reserve.resize(req.reserve_size, 0);
|
||||
cryptonote::difficulty_type wdiff;
|
||||
if(!m_core.get_block_template(b, info.address, wdiff, res.height, res.expected_reward, blob_reserve))
|
||||
crypto::hash prev_block;
|
||||
if (!req.prev_block.empty())
|
||||
{
|
||||
if (!epee::string_tools::hex_to_pod(req.prev_block, prev_block))
|
||||
{
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
|
||||
error_resp.message = "Invalid prev_block";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(!m_core.get_block_template(b, req.prev_block.empty() ? NULL : &prev_block, info.address, wdiff, res.height, res.expected_reward, blob_reserve))
|
||||
{
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
|
||||
error_resp.message = "Internal error: failed to create block template";
|
||||
|
@ -1345,7 +1356,8 @@ namespace cryptonote
|
|||
return false;
|
||||
}
|
||||
|
||||
if(!m_core.handle_block_found(b))
|
||||
block_verification_context bvc;
|
||||
if(!m_core.handle_block_found(b, bvc))
|
||||
{
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED;
|
||||
error_resp.message = "Block not accepted";
|
||||
|
@ -1377,15 +1389,17 @@ namespace cryptonote
|
|||
|
||||
template_req.reserve_size = 1;
|
||||
template_req.wallet_address = req.wallet_address;
|
||||
template_req.prev_block = req.prev_block;
|
||||
submit_req.push_back(boost::value_initialized<std::string>());
|
||||
res.height = m_core.get_blockchain_storage().get_current_blockchain_height();
|
||||
|
||||
bool r;
|
||||
bool r = CORE_RPC_STATUS_OK;
|
||||
|
||||
for(size_t i = 0; i < req.amount_of_blocks; i++)
|
||||
{
|
||||
r = on_getblocktemplate(template_req, template_res, error_resp, ctx);
|
||||
res.status = template_res.status;
|
||||
template_req.prev_block.clear();
|
||||
|
||||
if (!r) return false;
|
||||
|
||||
|
@ -1403,6 +1417,7 @@ namespace cryptonote
|
|||
error_resp.message = "Wrong block blob";
|
||||
return false;
|
||||
}
|
||||
b.nonce = req.starting_nonce;
|
||||
miner::find_nonce_for_given_block(b, template_res.difficulty, template_res.height);
|
||||
|
||||
submit_req.front() = string_tools::buff_to_hex_nodelimer(block_to_blob(b));
|
||||
|
@ -1411,6 +1426,8 @@ namespace cryptonote
|
|||
|
||||
if (!r) return false;
|
||||
|
||||
res.blocks.push_back(epee::string_tools::pod_to_hex(get_block_hash(b)));
|
||||
template_req.prev_block = res.blocks.back();
|
||||
res.height = template_res.height;
|
||||
}
|
||||
|
||||
|
|
|
@ -530,9 +530,11 @@ namespace cryptonote
|
|||
struct request_t
|
||||
{
|
||||
std::vector<get_outputs_out> outputs;
|
||||
bool get_txid;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(outputs)
|
||||
KV_SERIALIZE(get_txid)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<request_t> request;
|
||||
|
@ -904,10 +906,12 @@ namespace cryptonote
|
|||
{
|
||||
uint64_t reserve_size; //max 255 bytes
|
||||
std::string wallet_address;
|
||||
std::string prev_block;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(reserve_size)
|
||||
KV_SERIALIZE(wallet_address)
|
||||
KV_SERIALIZE(prev_block)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<request_t> request;
|
||||
|
@ -964,10 +968,14 @@ namespace cryptonote
|
|||
{
|
||||
uint64_t amount_of_blocks;
|
||||
std::string wallet_address;
|
||||
std::string prev_block;
|
||||
uint32_t starting_nonce;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(amount_of_blocks)
|
||||
KV_SERIALIZE(wallet_address)
|
||||
KV_SERIALIZE(prev_block)
|
||||
KV_SERIALIZE_OPT(starting_nonce, (uint32_t)0)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<request_t> request;
|
||||
|
@ -975,10 +983,12 @@ namespace cryptonote
|
|||
struct response_t
|
||||
{
|
||||
uint64_t height;
|
||||
std::vector<std::string> blocks;
|
||||
std::string status;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(height)
|
||||
KV_SERIALIZE(blocks)
|
||||
KV_SERIALIZE(status)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
|
|
@ -46,6 +46,7 @@ from framework.daemon import Daemon
|
|||
class BlockchainTest():
|
||||
def run_test(self):
|
||||
self._test_generateblocks(5)
|
||||
self._test_alt_chains()
|
||||
|
||||
def _test_generateblocks(self, blocks):
|
||||
assert blocks >= 2
|
||||
|
@ -152,6 +153,130 @@ class BlockchainTest():
|
|||
except: ok = True
|
||||
assert ok
|
||||
|
||||
# get transactions
|
||||
res = daemon.get_info()
|
||||
assert res.height == height + blocks - 1
|
||||
nblocks = height + blocks - 1
|
||||
res = daemon.getblockheadersrange(0, nblocks - 1)
|
||||
assert len(res.headers) == nblocks
|
||||
assert res.headers[-1] == block_header
|
||||
txids = [x.miner_tx_hash for x in res.headers]
|
||||
res = daemon.get_transactions(txs_hashes = txids)
|
||||
assert len(res.txs) == nblocks
|
||||
assert not 'missed_txs' in res or len(res.missed_txs) == 0
|
||||
running_output_index = 0
|
||||
for i in range(len(txids)):
|
||||
tx = res.txs[i]
|
||||
assert tx.tx_hash == txids[i]
|
||||
assert not tx.double_spend_seen
|
||||
assert not tx.in_pool
|
||||
assert tx.block_height == i
|
||||
if i > 0:
|
||||
for idx in tx.output_indices:
|
||||
assert idx == running_output_index
|
||||
running_output_index += 1
|
||||
res_out = daemon.get_outs([{'amount': 0, 'index': i} for i in tx.output_indices], get_txid = True)
|
||||
assert len(res_out.outs) == len(tx.output_indices)
|
||||
for out in res_out.outs:
|
||||
assert len(out.key) == 64
|
||||
assert len(out.mask) == 64
|
||||
assert not out.unlocked
|
||||
assert out.height == i + 1
|
||||
assert out.txid == txids[i + 1]
|
||||
|
||||
for i in range(height + nblocks - 1):
|
||||
res_sum = daemon.get_coinbase_tx_sum(i, 1)
|
||||
res_header = daemon.getblockheaderbyheight(i)
|
||||
assert res_sum.emission_amount == res_header.block_header.reward
|
||||
|
||||
res = daemon.get_coinbase_tx_sum(0, 1)
|
||||
assert res.emission_amount == 17592186044415
|
||||
assert res.fee_amount == 0
|
||||
sum_blocks = height + nblocks - 1
|
||||
res = daemon.get_coinbase_tx_sum(0, sum_blocks)
|
||||
extrapolated = 17592186044415 + 17592186044415 * 2 * (sum_blocks - 1)
|
||||
assert res.emission_amount < extrapolated and res.emission_amount > extrapolated - 1e12
|
||||
assert res.fee_amount == 0
|
||||
sum_blocks_emission = res.emission_amount
|
||||
res = daemon.get_coinbase_tx_sum(1, sum_blocks)
|
||||
assert res.emission_amount == sum_blocks_emission - 17592186044415
|
||||
assert res.fee_amount == 0
|
||||
|
||||
res = daemon.get_output_distribution([0, 1, 17592186044415], 0, 0)
|
||||
assert len(res.distributions) == 3
|
||||
for a in range(3):
|
||||
assert res.distributions[a].amount == [0, 1, 17592186044415][a]
|
||||
assert res.distributions[a].start_height == 0
|
||||
assert res.distributions[a].base == 0
|
||||
assert len(res.distributions[a].distribution) == height + nblocks - 1
|
||||
assert res.distributions[a].binary == False
|
||||
for i in range(height + nblocks - 1):
|
||||
assert res.distributions[a].distribution[i] == (1 if i > 0 and a == 0 else 1 if a == 2 and i == 0 else 0)
|
||||
|
||||
res = daemon.get_output_histogram([], min_count = 0, max_count = 0)
|
||||
assert len(res.histogram) == 2
|
||||
for i in range(2):
|
||||
assert res.histogram[i].amount in [0, 17592186044415]
|
||||
assert res.histogram[i].total_instances in [height + nblocks - 2, 1]
|
||||
assert res.histogram[i].unlocked_instances == 0
|
||||
assert res.histogram[i].recent_instances == 0
|
||||
|
||||
def _test_alt_chains(self):
|
||||
print('Testing alt chains')
|
||||
daemon = Daemon()
|
||||
res = daemon.get_info()
|
||||
height = res.height
|
||||
prev_hash = res.top_block_hash
|
||||
res_template = daemon.getblocktemplate('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm')
|
||||
nonce = 0
|
||||
|
||||
# 5 siblings
|
||||
alt_blocks = [None] * 5
|
||||
for i in range(len(alt_blocks)):
|
||||
res = daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 1, prev_block = prev_hash, starting_nonce = nonce)
|
||||
assert res.height == height
|
||||
assert len(res.blocks) == 1
|
||||
txid = res.blocks[0]
|
||||
res = daemon.getblockheaderbyhash(txid)
|
||||
nonce = res.block_header.nonce
|
||||
print('mined ' + ('alt' if res.block_header.orphan_status else 'tip') + ' block ' + str(height) + ', nonce ' + str(nonce))
|
||||
assert res.block_header.prev_hash == prev_hash
|
||||
assert res.block_header.orphan_status == (i > 0)
|
||||
alt_blocks[i] = txid
|
||||
nonce += 1
|
||||
|
||||
print 'mining 3 on 1'
|
||||
# three more on [1]
|
||||
res = daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 3, prev_block = alt_blocks[1], starting_nonce = nonce)
|
||||
assert res.height == height + 3
|
||||
assert len(res.blocks) == 3
|
||||
blk_hash = res.blocks[2]
|
||||
res = daemon.getblockheaderbyhash(blk_hash)
|
||||
nonce = res.block_header.nonce
|
||||
print('mined 3 blocks to height ' + str(height + 3))
|
||||
assert not res.block_header.orphan_status
|
||||
nonce += 1
|
||||
|
||||
print 'mining 4 on 3'
|
||||
# 4 more on [3], the chain will reorg when we mine the 4th
|
||||
top_block_hash = blk_hash
|
||||
prev_block = alt_blocks[3]
|
||||
for i in range(4):
|
||||
res = daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 1, prev_block = prev_block)
|
||||
assert res.height == height + 1 + i
|
||||
assert len(res.blocks) == 1
|
||||
prev_block = res.blocks[-1]
|
||||
res = daemon.getblockheaderbyhash(res.blocks[-1])
|
||||
assert res.block_header.orphan_status == (i < 3)
|
||||
|
||||
res = daemon.get_info()
|
||||
assert res.height == ((height + 4) if i < 3 else height + 5)
|
||||
assert res.top_block_hash == (top_block_hash if i < 3 else prev_block)
|
||||
|
||||
res = daemon.get_info()
|
||||
assert res.height == height + 5
|
||||
assert res.top_block_hash == prev_block
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
BlockchainTest().run_test()
|
||||
|
|
|
@ -35,12 +35,13 @@ class Daemon(object):
|
|||
def __init__(self, protocol='http', host='127.0.0.1', port=0, idx=0):
|
||||
self.rpc = JSONRPC('{protocol}://{host}:{port}'.format(protocol=protocol, host=host, port=port if port else 18180+idx))
|
||||
|
||||
def getblocktemplate(self, address):
|
||||
def getblocktemplate(self, address, prev_block = ""):
|
||||
getblocktemplate = {
|
||||
'method': 'getblocktemplate',
|
||||
'params': {
|
||||
'wallet_address': address,
|
||||
'reserve_size' : 1
|
||||
'reserve_size' : 1,
|
||||
'prev_block' : prev_block,
|
||||
},
|
||||
'jsonrpc': '2.0',
|
||||
'id': '0'
|
||||
|
@ -143,13 +144,15 @@ class Daemon(object):
|
|||
}
|
||||
return self.rpc.send_json_rpc_request(hard_fork_info)
|
||||
|
||||
def generateblocks(self, address, blocks=1):
|
||||
def generateblocks(self, address, blocks=1, prev_block = "", starting_nonce = 0):
|
||||
generateblocks = {
|
||||
'method': 'generateblocks',
|
||||
'params': {
|
||||
'amount_of_blocks' : blocks,
|
||||
'reserve_size' : 20,
|
||||
'wallet_address': address
|
||||
'wallet_address': address,
|
||||
'prev_block': prev_block,
|
||||
'starting_nonce': starting_nonce,
|
||||
},
|
||||
'jsonrpc': '2.0',
|
||||
'id': '0'
|
||||
|
@ -238,3 +241,62 @@ class Daemon(object):
|
|||
'id': '0'
|
||||
}
|
||||
return self.rpc.send_json_rpc_request(set_bans)
|
||||
|
||||
def get_transactions(self, txs_hashes = [], decode_as_json = False, prune = False, split = False):
|
||||
get_transactions = {
|
||||
'txs_hashes': txs_hashes,
|
||||
'decode_as_json': decode_as_json,
|
||||
'prune': prune,
|
||||
'split': split,
|
||||
}
|
||||
return self.rpc.send_request('/get_transactions', get_transactions)
|
||||
|
||||
def get_outs(self, outputs = [], get_txid = False):
|
||||
get_outs = {
|
||||
'outputs': outputs,
|
||||
'get_txid': get_txid,
|
||||
}
|
||||
return self.rpc.send_request('/get_outs', get_outs)
|
||||
|
||||
def get_coinbase_tx_sum(self, height, count):
|
||||
get_coinbase_tx_sum = {
|
||||
'method': 'get_coinbase_tx_sum',
|
||||
'params': {
|
||||
'height': height,
|
||||
'count': count,
|
||||
},
|
||||
'jsonrpc': '2.0',
|
||||
'id': '0'
|
||||
}
|
||||
return self.rpc.send_json_rpc_request(get_coinbase_tx_sum)
|
||||
|
||||
def get_output_distribution(self, amounts = [], from_height = 0, to_height = 0, cumulative = False, binary = False, compress = False):
|
||||
get_output_distribution = {
|
||||
'method': 'get_output_distribution',
|
||||
'params': {
|
||||
'amounts': amounts,
|
||||
'from_height': from_height,
|
||||
'to_height': to_height,
|
||||
'cumulative': cumulative,
|
||||
'binary': binary,
|
||||
'compress': compress,
|
||||
},
|
||||
'jsonrpc': '2.0',
|
||||
'id': '0'
|
||||
}
|
||||
return self.rpc.send_json_rpc_request(get_output_distribution)
|
||||
|
||||
def get_output_histogram(self, amounts = [], min_count = 0, max_count = 0, unlocked = False, recent_cutoff = 0):
|
||||
get_output_histogram = {
|
||||
'method': 'get_output_histogram',
|
||||
'params': {
|
||||
'amounts': amounts,
|
||||
'min_count': min_count,
|
||||
'max_count': max_count,
|
||||
'unlocked': unlocked,
|
||||
'recent_cutoff': recent_cutoff,
|
||||
},
|
||||
'jsonrpc': '2.0',
|
||||
'id': '0'
|
||||
}
|
||||
return self.rpc.send_json_rpc_request(get_output_histogram)
|
||||
|
|
Loading…
Reference in a new issue