mirror of
https://github.com/monero-project/monero.git
synced 2024-11-17 16:27:39 +00:00
667 lines
24 KiB
C++
667 lines
24 KiB
C++
// Copyright (c) 2014-2016, The Monero Project
|
|
//
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without modification, are
|
|
// permitted provided that the following conditions are met:
|
|
//
|
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
// conditions and the following disclaimer.
|
|
//
|
|
// 2. 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.
|
|
//
|
|
// 3. Neither the name of the copyright holder 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 HOLDER OR CONTRIBUTORS 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.
|
|
//
|
|
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
|
|
|
#include <vector>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
|
|
#include "include_base_utils.h"
|
|
|
|
#include "console_handler.h"
|
|
|
|
#include "p2p/net_node.h"
|
|
#include "cryptonote_core/cryptonote_basic.h"
|
|
#include "cryptonote_core/cryptonote_basic_impl.h"
|
|
#include "cryptonote_core/cryptonote_format_utils.h"
|
|
#include "cryptonote_core/miner.h"
|
|
|
|
#include "chaingen.h"
|
|
|
|
using namespace std;
|
|
|
|
using namespace epee;
|
|
using namespace cryptonote;
|
|
|
|
|
|
void test_generator::get_block_chain(std::vector<block_info>& blockchain, const crypto::hash& head, size_t n) const
|
|
{
|
|
crypto::hash curr = head;
|
|
while (null_hash != curr && blockchain.size() < n)
|
|
{
|
|
auto it = m_blocks_info.find(curr);
|
|
if (m_blocks_info.end() == it)
|
|
{
|
|
throw std::runtime_error("block hash wasn't found");
|
|
}
|
|
|
|
blockchain.push_back(it->second);
|
|
curr = it->second.prev_id;
|
|
}
|
|
|
|
std::reverse(blockchain.begin(), blockchain.end());
|
|
}
|
|
|
|
void test_generator::get_last_n_block_sizes(std::vector<size_t>& block_sizes, const crypto::hash& head, size_t n) const
|
|
{
|
|
std::vector<block_info> blockchain;
|
|
get_block_chain(blockchain, head, n);
|
|
BOOST_FOREACH(auto& bi, blockchain)
|
|
{
|
|
block_sizes.push_back(bi.block_size);
|
|
}
|
|
}
|
|
|
|
uint64_t test_generator::get_already_generated_coins(const crypto::hash& blk_id) const
|
|
{
|
|
auto it = m_blocks_info.find(blk_id);
|
|
if (it == m_blocks_info.end())
|
|
throw std::runtime_error("block hash wasn't found");
|
|
|
|
return it->second.already_generated_coins;
|
|
}
|
|
|
|
uint64_t test_generator::get_already_generated_coins(const cryptonote::block& blk) const
|
|
{
|
|
crypto::hash blk_hash;
|
|
get_block_hash(blk, blk_hash);
|
|
return get_already_generated_coins(blk_hash);
|
|
}
|
|
|
|
void test_generator::add_block(const cryptonote::block& blk, size_t tsx_size, std::vector<size_t>& block_sizes, uint64_t already_generated_coins, uint8_t hf_version)
|
|
{
|
|
const size_t block_size = tsx_size + get_object_blobsize(blk.miner_tx);
|
|
uint64_t block_reward;
|
|
get_block_reward(misc_utils::median(block_sizes), block_size, already_generated_coins, block_reward, hf_version);
|
|
m_blocks_info[get_block_hash(blk)] = block_info(blk.prev_id, already_generated_coins + block_reward, block_size);
|
|
}
|
|
|
|
bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, const crypto::hash& prev_id,
|
|
const cryptonote::account_base& miner_acc, uint64_t timestamp, uint64_t already_generated_coins,
|
|
std::vector<size_t>& block_sizes, const std::list<cryptonote::transaction>& tx_list)
|
|
{
|
|
blk.major_version = CURRENT_BLOCK_MAJOR_VERSION;
|
|
blk.minor_version = CURRENT_BLOCK_MINOR_VERSION;
|
|
blk.timestamp = timestamp;
|
|
blk.prev_id = prev_id;
|
|
|
|
blk.tx_hashes.reserve(tx_list.size());
|
|
BOOST_FOREACH(const transaction &tx, tx_list)
|
|
{
|
|
crypto::hash tx_hash;
|
|
get_transaction_hash(tx, tx_hash);
|
|
blk.tx_hashes.push_back(tx_hash);
|
|
}
|
|
|
|
uint64_t total_fee = 0;
|
|
size_t txs_size = 0;
|
|
BOOST_FOREACH(auto& tx, tx_list)
|
|
{
|
|
uint64_t fee = 0;
|
|
bool r = get_tx_fee(tx, fee);
|
|
CHECK_AND_ASSERT_MES(r, false, "wrong transaction passed to construct_block");
|
|
total_fee += fee;
|
|
txs_size += get_object_blobsize(tx);
|
|
}
|
|
|
|
blk.miner_tx = AUTO_VAL_INIT(blk.miner_tx);
|
|
size_t target_block_size = txs_size + get_object_blobsize(blk.miner_tx);
|
|
while (true)
|
|
{
|
|
if (!construct_miner_tx(height, misc_utils::median(block_sizes), already_generated_coins, target_block_size, total_fee, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), 10))
|
|
return false;
|
|
|
|
size_t actual_block_size = txs_size + get_object_blobsize(blk.miner_tx);
|
|
if (target_block_size < actual_block_size)
|
|
{
|
|
target_block_size = actual_block_size;
|
|
}
|
|
else if (actual_block_size < target_block_size)
|
|
{
|
|
size_t delta = target_block_size - actual_block_size;
|
|
blk.miner_tx.extra.resize(blk.miner_tx.extra.size() + delta, 0);
|
|
actual_block_size = txs_size + get_object_blobsize(blk.miner_tx);
|
|
if (actual_block_size == target_block_size)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
CHECK_AND_ASSERT_MES(target_block_size < actual_block_size, false, "Unexpected block size");
|
|
delta = actual_block_size - target_block_size;
|
|
blk.miner_tx.extra.resize(blk.miner_tx.extra.size() - delta);
|
|
actual_block_size = txs_size + get_object_blobsize(blk.miner_tx);
|
|
if (actual_block_size == target_block_size)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
CHECK_AND_ASSERT_MES(actual_block_size < target_block_size, false, "Unexpected block size");
|
|
blk.miner_tx.extra.resize(blk.miner_tx.extra.size() + delta, 0);
|
|
target_block_size = txs_size + get_object_blobsize(blk.miner_tx);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//blk.tree_root_hash = get_tx_tree_hash(blk);
|
|
|
|
// Nonce search...
|
|
blk.nonce = 0;
|
|
while (!miner::find_nonce_for_given_block(blk, get_test_difficulty(), height))
|
|
blk.timestamp++;
|
|
|
|
add_block(blk, txs_size, block_sizes, already_generated_coins);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::account_base& miner_acc, uint64_t timestamp)
|
|
{
|
|
std::vector<size_t> block_sizes;
|
|
std::list<cryptonote::transaction> tx_list;
|
|
return construct_block(blk, 0, null_hash, miner_acc, timestamp, 0, block_sizes, tx_list);
|
|
}
|
|
|
|
bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::block& blk_prev,
|
|
const cryptonote::account_base& miner_acc,
|
|
const std::list<cryptonote::transaction>& tx_list/* = std::list<cryptonote::transaction>()*/)
|
|
{
|
|
uint64_t height = boost::get<txin_gen>(blk_prev.miner_tx.vin.front()).height + 1;
|
|
crypto::hash prev_id = get_block_hash(blk_prev);
|
|
// Keep difficulty unchanged
|
|
uint64_t timestamp = blk_prev.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN;
|
|
uint64_t already_generated_coins = get_already_generated_coins(prev_id);
|
|
std::vector<size_t> block_sizes;
|
|
get_last_n_block_sizes(block_sizes, prev_id, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
|
|
|
|
return construct_block(blk, height, prev_id, miner_acc, timestamp, already_generated_coins, block_sizes, tx_list);
|
|
}
|
|
|
|
bool test_generator::construct_block_manually(block& blk, const block& prev_block, const account_base& miner_acc,
|
|
int actual_params/* = bf_none*/, uint8_t major_ver/* = 0*/,
|
|
uint8_t minor_ver/* = 0*/, uint64_t timestamp/* = 0*/,
|
|
const crypto::hash& prev_id/* = crypto::hash()*/, const difficulty_type& diffic/* = 1*/,
|
|
const transaction& miner_tx/* = transaction()*/,
|
|
const std::vector<crypto::hash>& tx_hashes/* = std::vector<crypto::hash>()*/,
|
|
size_t txs_sizes/* = 0*/, size_t max_outs/* = 0*/, uint8_t hf_version/* = 1*/)
|
|
{
|
|
blk.major_version = actual_params & bf_major_ver ? major_ver : CURRENT_BLOCK_MAJOR_VERSION;
|
|
blk.minor_version = actual_params & bf_minor_ver ? minor_ver : CURRENT_BLOCK_MINOR_VERSION;
|
|
blk.timestamp = actual_params & bf_timestamp ? timestamp : prev_block.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN; // Keep difficulty unchanged
|
|
blk.prev_id = actual_params & bf_prev_id ? prev_id : get_block_hash(prev_block);
|
|
blk.tx_hashes = actual_params & bf_tx_hashes ? tx_hashes : std::vector<crypto::hash>();
|
|
max_outs = actual_params & bf_max_outs ? max_outs : 9999;
|
|
hf_version = actual_params & bf_hf_version ? hf_version : 1;
|
|
|
|
size_t height = get_block_height(prev_block) + 1;
|
|
uint64_t already_generated_coins = get_already_generated_coins(prev_block);
|
|
std::vector<size_t> block_sizes;
|
|
get_last_n_block_sizes(block_sizes, get_block_hash(prev_block), CRYPTONOTE_REWARD_BLOCKS_WINDOW);
|
|
if (actual_params & bf_miner_tx)
|
|
{
|
|
blk.miner_tx = miner_tx;
|
|
}
|
|
else
|
|
{
|
|
size_t current_block_size = txs_sizes + get_object_blobsize(blk.miner_tx);
|
|
// TODO: This will work, until size of constructed block is less then CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE
|
|
if (!construct_miner_tx(height, misc_utils::median(block_sizes), already_generated_coins, current_block_size, 0, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), max_outs, hf_version))
|
|
return false;
|
|
}
|
|
|
|
//blk.tree_root_hash = get_tx_tree_hash(blk);
|
|
|
|
difficulty_type a_diffic = actual_params & bf_diffic ? diffic : get_test_difficulty();
|
|
fill_nonce(blk, a_diffic, height);
|
|
|
|
add_block(blk, txs_sizes, block_sizes, already_generated_coins, hf_version);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool test_generator::construct_block_manually_tx(cryptonote::block& blk, const cryptonote::block& prev_block,
|
|
const cryptonote::account_base& miner_acc,
|
|
const std::vector<crypto::hash>& tx_hashes, size_t txs_size)
|
|
{
|
|
return construct_block_manually(blk, prev_block, miner_acc, bf_tx_hashes, 0, 0, 0, crypto::hash(), 0, transaction(), tx_hashes, txs_size);
|
|
}
|
|
|
|
|
|
struct output_index {
|
|
const cryptonote::txout_target_v out;
|
|
uint64_t amount;
|
|
size_t blk_height; // block height
|
|
size_t tx_no; // index of transaction in block
|
|
size_t out_no; // index of out in transaction
|
|
size_t idx;
|
|
bool spent;
|
|
const cryptonote::block *p_blk;
|
|
const cryptonote::transaction *p_tx;
|
|
|
|
output_index(const cryptonote::txout_target_v &_out, uint64_t _a, size_t _h, size_t tno, size_t ono, const cryptonote::block *_pb, const cryptonote::transaction *_pt)
|
|
: out(_out), amount(_a), blk_height(_h), tx_no(tno), out_no(ono), idx(0), spent(false), p_blk(_pb), p_tx(_pt) { }
|
|
|
|
output_index(const output_index &other)
|
|
: out(other.out), amount(other.amount), blk_height(other.blk_height), tx_no(other.tx_no), out_no(other.out_no), idx(other.idx), spent(other.spent), p_blk(other.p_blk), p_tx(other.p_tx) { }
|
|
|
|
const std::string toString() const {
|
|
std::stringstream ss;
|
|
|
|
ss << "output_index{blk_height=" << blk_height
|
|
<< " tx_no=" << tx_no
|
|
<< " out_no=" << out_no
|
|
<< " amount=" << amount
|
|
<< " idx=" << idx
|
|
<< " spent=" << spent
|
|
<< "}";
|
|
|
|
return ss.str();
|
|
}
|
|
|
|
output_index& operator=(const output_index& other)
|
|
{
|
|
new(this) output_index(other);
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
typedef std::map<uint64_t, std::vector<size_t> > map_output_t;
|
|
typedef std::map<uint64_t, std::vector<output_index> > map_output_idx_t;
|
|
typedef pair<uint64_t, size_t> outloc_t;
|
|
|
|
namespace
|
|
{
|
|
uint64_t get_inputs_amount(const vector<tx_source_entry> &s)
|
|
{
|
|
uint64_t r = 0;
|
|
BOOST_FOREACH(const tx_source_entry &e, s)
|
|
{
|
|
r += e.amount;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
}
|
|
|
|
bool init_output_indices(map_output_idx_t& outs, std::map<uint64_t, std::vector<size_t> >& outs_mine, const std::vector<cryptonote::block>& blockchain, const map_hash2tx_t& mtx, const cryptonote::account_base& from) {
|
|
|
|
BOOST_FOREACH (const block& blk, blockchain) {
|
|
vector<const transaction*> vtx;
|
|
vtx.push_back(&blk.miner_tx);
|
|
|
|
BOOST_FOREACH(const crypto::hash &h, blk.tx_hashes) {
|
|
const map_hash2tx_t::const_iterator cit = mtx.find(h);
|
|
if (mtx.end() == cit)
|
|
throw std::runtime_error("block contains an unknown tx hash");
|
|
|
|
vtx.push_back(cit->second);
|
|
}
|
|
|
|
//vtx.insert(vtx.end(), blk.);
|
|
// TODO: add all other txes
|
|
for (size_t i = 0; i < vtx.size(); i++) {
|
|
const transaction &tx = *vtx[i];
|
|
|
|
for (size_t j = 0; j < tx.vout.size(); ++j) {
|
|
const tx_out &out = tx.vout[j];
|
|
|
|
output_index oi(out.target, out.amount, boost::get<txin_gen>(*blk.miner_tx.vin.begin()).height, i, j, &blk, vtx[i]);
|
|
|
|
if (2 == out.target.which()) { // out_to_key
|
|
outs[out.amount].push_back(oi);
|
|
size_t tx_global_idx = outs[out.amount].size() - 1;
|
|
outs[out.amount][tx_global_idx].idx = tx_global_idx;
|
|
// Is out to me?
|
|
if (is_out_to_acc(from.get_keys(), boost::get<txout_to_key>(out.target), get_tx_pub_key_from_extra(tx), j)) {
|
|
outs_mine[out.amount].push_back(tx_global_idx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool init_spent_output_indices(map_output_idx_t& outs, map_output_t& outs_mine, const std::vector<cryptonote::block>& blockchain, const map_hash2tx_t& mtx, const cryptonote::account_base& from) {
|
|
|
|
BOOST_FOREACH (const map_output_t::value_type &o, outs_mine) {
|
|
for (size_t i = 0; i < o.second.size(); ++i) {
|
|
output_index &oi = outs[o.first][o.second[i]];
|
|
|
|
// construct key image for this output
|
|
crypto::key_image img;
|
|
keypair in_ephemeral;
|
|
generate_key_image_helper(from.get_keys(), get_tx_pub_key_from_extra(*oi.p_tx), oi.out_no, in_ephemeral, img);
|
|
|
|
// lookup for this key image in the events vector
|
|
BOOST_FOREACH(auto& tx_pair, mtx) {
|
|
const transaction& tx = *tx_pair.second;
|
|
BOOST_FOREACH(const txin_v &in, tx.vin) {
|
|
if (typeid(txin_to_key) == in.type()) {
|
|
const txin_to_key &itk = boost::get<txin_to_key>(in);
|
|
if (itk.k_image == img) {
|
|
oi.spent = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool fill_output_entries(std::vector<output_index>& out_indices, size_t sender_out, size_t nmix, size_t& real_entry_idx, std::vector<tx_source_entry::output_entry>& output_entries)
|
|
{
|
|
if (out_indices.size() <= nmix)
|
|
return false;
|
|
|
|
bool sender_out_found = false;
|
|
size_t rest = nmix;
|
|
for (size_t i = 0; i < out_indices.size() && (0 < rest || !sender_out_found); ++i)
|
|
{
|
|
const output_index& oi = out_indices[i];
|
|
if (oi.spent)
|
|
continue;
|
|
|
|
bool append = false;
|
|
if (i == sender_out)
|
|
{
|
|
append = true;
|
|
sender_out_found = true;
|
|
real_entry_idx = output_entries.size();
|
|
}
|
|
else if (0 < rest)
|
|
{
|
|
--rest;
|
|
append = true;
|
|
}
|
|
|
|
if (append)
|
|
{
|
|
const txout_to_key& otk = boost::get<txout_to_key>(oi.out);
|
|
output_entries.push_back(tx_source_entry::output_entry(oi.idx, rct::ctkey({rct::pk2rct(otk.key), rct::identity()})));
|
|
}
|
|
}
|
|
|
|
return 0 == rest && sender_out_found;
|
|
}
|
|
|
|
bool fill_tx_sources(std::vector<tx_source_entry>& sources, const std::vector<test_event_entry>& events,
|
|
const block& blk_head, const cryptonote::account_base& from, uint64_t amount, size_t nmix)
|
|
{
|
|
map_output_idx_t outs;
|
|
map_output_t outs_mine;
|
|
|
|
std::vector<cryptonote::block> blockchain;
|
|
map_hash2tx_t mtx;
|
|
if (!find_block_chain(events, blockchain, mtx, get_block_hash(blk_head)))
|
|
return false;
|
|
|
|
if (!init_output_indices(outs, outs_mine, blockchain, mtx, from))
|
|
return false;
|
|
|
|
if (!init_spent_output_indices(outs, outs_mine, blockchain, mtx, from))
|
|
return false;
|
|
|
|
// Iterate in reverse is more efficiency
|
|
uint64_t sources_amount = 0;
|
|
bool sources_found = false;
|
|
BOOST_REVERSE_FOREACH(const map_output_t::value_type o, outs_mine)
|
|
{
|
|
for (size_t i = 0; i < o.second.size() && !sources_found; ++i)
|
|
{
|
|
size_t sender_out = o.second[i];
|
|
const output_index& oi = outs[o.first][sender_out];
|
|
if (oi.spent)
|
|
continue;
|
|
|
|
cryptonote::tx_source_entry ts;
|
|
ts.amount = oi.amount;
|
|
ts.real_output_in_tx_index = oi.out_no;
|
|
ts.real_out_tx_key = get_tx_pub_key_from_extra(*oi.p_tx); // incoming tx public key
|
|
size_t realOutput;
|
|
if (!fill_output_entries(outs[o.first], sender_out, nmix, realOutput, ts.outputs))
|
|
continue;
|
|
|
|
ts.real_output = realOutput;
|
|
|
|
sources.push_back(ts);
|
|
|
|
sources_amount += ts.amount;
|
|
sources_found = amount <= sources_amount;
|
|
}
|
|
|
|
if (sources_found)
|
|
break;
|
|
}
|
|
|
|
return sources_found;
|
|
}
|
|
|
|
bool fill_tx_destination(tx_destination_entry &de, const cryptonote::account_base &to, uint64_t amount) {
|
|
de.addr = to.get_keys().m_account_address;
|
|
de.amount = amount;
|
|
return true;
|
|
}
|
|
|
|
void fill_tx_sources_and_destinations(const std::vector<test_event_entry>& events, const block& blk_head,
|
|
const cryptonote::account_base& from, const cryptonote::account_base& to,
|
|
uint64_t amount, uint64_t fee, size_t nmix, std::vector<tx_source_entry>& sources,
|
|
std::vector<tx_destination_entry>& destinations)
|
|
{
|
|
sources.clear();
|
|
destinations.clear();
|
|
|
|
if (!fill_tx_sources(sources, events, blk_head, from, amount + fee, nmix))
|
|
throw std::runtime_error("couldn't fill transaction sources");
|
|
|
|
tx_destination_entry de;
|
|
if (!fill_tx_destination(de, to, amount))
|
|
throw std::runtime_error("couldn't fill transaction destination");
|
|
destinations.push_back(de);
|
|
|
|
tx_destination_entry de_change;
|
|
uint64_t cache_back = get_inputs_amount(sources) - (amount + fee);
|
|
if (0 < cache_back)
|
|
{
|
|
if (!fill_tx_destination(de_change, from, cache_back))
|
|
throw std::runtime_error("couldn't fill transaction cache back destination");
|
|
destinations.push_back(de_change);
|
|
}
|
|
}
|
|
|
|
void fill_nonce(cryptonote::block& blk, const difficulty_type& diffic, uint64_t height)
|
|
{
|
|
blk.nonce = 0;
|
|
while (!miner::find_nonce_for_given_block(blk, diffic, height))
|
|
blk.timestamp++;
|
|
}
|
|
|
|
bool construct_miner_tx_manually(size_t height, uint64_t already_generated_coins,
|
|
const account_public_address& miner_address, transaction& tx, uint64_t fee,
|
|
keypair* p_txkey/* = 0*/)
|
|
{
|
|
keypair txkey;
|
|
txkey = keypair::generate();
|
|
add_tx_pub_key_to_extra(tx, txkey.pub);
|
|
|
|
if (0 != p_txkey)
|
|
*p_txkey = txkey;
|
|
|
|
txin_gen in;
|
|
in.height = height;
|
|
tx.vin.push_back(in);
|
|
|
|
// This will work, until size of constructed block is less then CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE
|
|
uint64_t block_reward;
|
|
if (!get_block_reward(0, 0, already_generated_coins, block_reward, 1))
|
|
{
|
|
LOG_PRINT_L0("Block is too big");
|
|
return false;
|
|
}
|
|
block_reward += fee;
|
|
|
|
crypto::key_derivation derivation;
|
|
crypto::public_key out_eph_public_key;
|
|
crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation);
|
|
crypto::derive_public_key(derivation, 0, miner_address.m_spend_public_key, out_eph_public_key);
|
|
|
|
tx_out out;
|
|
out.amount = block_reward;
|
|
out.target = txout_to_key(out_eph_public_key);
|
|
tx.vout.push_back(out);
|
|
|
|
tx.version = 1;
|
|
tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool construct_tx_to_key(const std::vector<test_event_entry>& events, cryptonote::transaction& tx, const block& blk_head,
|
|
const cryptonote::account_base& from, const cryptonote::account_base& to, uint64_t amount,
|
|
uint64_t fee, size_t nmix)
|
|
{
|
|
vector<tx_source_entry> sources;
|
|
vector<tx_destination_entry> destinations;
|
|
fill_tx_sources_and_destinations(events, blk_head, from, to, amount, fee, nmix, sources, destinations);
|
|
|
|
return construct_tx(from.get_keys(), sources, destinations, std::vector<uint8_t>(), tx, 0);
|
|
}
|
|
|
|
transaction construct_tx_with_fee(std::vector<test_event_entry>& events, const block& blk_head,
|
|
const account_base& acc_from, const account_base& acc_to, uint64_t amount, uint64_t fee)
|
|
{
|
|
transaction tx;
|
|
construct_tx_to_key(events, tx, blk_head, acc_from, acc_to, amount, fee, 0);
|
|
events.push_back(tx);
|
|
return tx;
|
|
}
|
|
|
|
uint64_t get_balance(const cryptonote::account_base& addr, const std::vector<cryptonote::block>& blockchain, const map_hash2tx_t& mtx) {
|
|
uint64_t res = 0;
|
|
std::map<uint64_t, std::vector<output_index> > outs;
|
|
std::map<uint64_t, std::vector<size_t> > outs_mine;
|
|
|
|
map_hash2tx_t confirmed_txs;
|
|
get_confirmed_txs(blockchain, mtx, confirmed_txs);
|
|
|
|
if (!init_output_indices(outs, outs_mine, blockchain, confirmed_txs, addr))
|
|
return false;
|
|
|
|
if (!init_spent_output_indices(outs, outs_mine, blockchain, confirmed_txs, addr))
|
|
return false;
|
|
|
|
BOOST_FOREACH (const map_output_t::value_type &o, outs_mine) {
|
|
for (size_t i = 0; i < o.second.size(); ++i) {
|
|
if (outs[o.first][o.second[i]].spent)
|
|
continue;
|
|
|
|
res += outs[o.first][o.second[i]].amount;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
void get_confirmed_txs(const std::vector<cryptonote::block>& blockchain, const map_hash2tx_t& mtx, map_hash2tx_t& confirmed_txs)
|
|
{
|
|
std::unordered_set<crypto::hash> confirmed_hashes;
|
|
BOOST_FOREACH(const block& blk, blockchain)
|
|
{
|
|
BOOST_FOREACH(const crypto::hash& tx_hash, blk.tx_hashes)
|
|
{
|
|
confirmed_hashes.insert(tx_hash);
|
|
}
|
|
}
|
|
|
|
BOOST_FOREACH(const auto& tx_pair, mtx)
|
|
{
|
|
if (0 != confirmed_hashes.count(tx_pair.first))
|
|
{
|
|
confirmed_txs.insert(tx_pair);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool find_block_chain(const std::vector<test_event_entry>& events, std::vector<cryptonote::block>& blockchain, map_hash2tx_t& mtx, const crypto::hash& head) {
|
|
std::unordered_map<crypto::hash, const block*> block_index;
|
|
BOOST_FOREACH(const test_event_entry& ev, events)
|
|
{
|
|
if (typeid(block) == ev.type())
|
|
{
|
|
const block* blk = &boost::get<block>(ev);
|
|
block_index[get_block_hash(*blk)] = blk;
|
|
}
|
|
else if (typeid(transaction) == ev.type())
|
|
{
|
|
const transaction& tx = boost::get<transaction>(ev);
|
|
mtx[get_transaction_hash(tx)] = &tx;
|
|
}
|
|
}
|
|
|
|
bool b_success = false;
|
|
crypto::hash id = head;
|
|
for (auto it = block_index.find(id); block_index.end() != it; it = block_index.find(id))
|
|
{
|
|
blockchain.push_back(*it->second);
|
|
id = it->second->prev_id;
|
|
if (null_hash == id)
|
|
{
|
|
b_success = true;
|
|
break;
|
|
}
|
|
}
|
|
reverse(blockchain.begin(), blockchain.end());
|
|
|
|
return b_success;
|
|
}
|
|
|
|
|
|
void test_chain_unit_base::register_callback(const std::string& cb_name, verify_callback cb)
|
|
{
|
|
m_callbacks[cb_name] = cb;
|
|
}
|
|
bool test_chain_unit_base::verify(const std::string& cb_name, cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry> &events)
|
|
{
|
|
auto cb_it = m_callbacks.find(cb_name);
|
|
if(cb_it == m_callbacks.end())
|
|
{
|
|
LOG_ERROR("Failed to find callback " << cb_name);
|
|
return false;
|
|
}
|
|
return cb_it->second(c, ev_index, events);
|
|
}
|