mirror of
https://github.com/monero-project/monero.git
synced 2024-12-26 23:39:25 -05:00
056c996703
- fix tx create from sources, input locking. Originally, creating a synthetic transactions with chaingen could create a transaction with outputs that are still locked in the current block, thus failing chain validation by the daemon. Simple unlock check was added. Some buggy tests were fixed as well as new unlock-checking version of tx creation rejected those, fixes are simple - mostly using correct block after a rewind to construct a transaction
798 lines
27 KiB
C++
798 lines
27 KiB
C++
// Copyright (c) 2014-2023, 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 "chaingen.h"
|
|
#include "block_validation.h"
|
|
|
|
using namespace epee;
|
|
using namespace cryptonote;
|
|
|
|
namespace
|
|
{
|
|
bool lift_up_difficulty(std::vector<test_event_entry>& events, std::vector<uint64_t>& timestamps,
|
|
std::vector<difficulty_type>& cummulative_difficulties, test_generator& generator,
|
|
size_t new_block_count, const block &blk_last, const account_base& miner_account)
|
|
{
|
|
difficulty_type commulative_diffic = cummulative_difficulties.empty() ? 0 : cummulative_difficulties.back();
|
|
block blk_prev = blk_last;
|
|
for (size_t i = 0; i < new_block_count; ++i)
|
|
{
|
|
block blk_next;
|
|
difficulty_type diffic = next_difficulty(timestamps, cummulative_difficulties,DIFFICULTY_TARGET_V1);
|
|
if (!generator.construct_block_manually(blk_next, blk_prev, miner_account,
|
|
test_generator::bf_timestamp | test_generator::bf_diffic, 0, 0, blk_prev.timestamp, crypto::hash(), diffic))
|
|
return false;
|
|
|
|
commulative_diffic += diffic;
|
|
if (timestamps.size() == DIFFICULTY_WINDOW)
|
|
{
|
|
timestamps.erase(timestamps.begin());
|
|
cummulative_difficulties.erase(cummulative_difficulties.begin());
|
|
}
|
|
timestamps.push_back(blk_next.timestamp);
|
|
cummulative_difficulties.push_back(commulative_diffic);
|
|
|
|
events.push_back(blk_next);
|
|
blk_prev = blk_next;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
#define BLOCK_VALIDATION_INIT_GENERATE() \
|
|
GENERATE_ACCOUNT(miner_account); \
|
|
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, 1338224400);
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// Tests
|
|
|
|
bool gen_block_big_major_version::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
block blk_1;
|
|
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_major_ver, 255);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_purged");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_big_minor_version::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
block blk_1;
|
|
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_minor_ver, 0, 255);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_accepted");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_ts_not_checked::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_account, BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW - 2);
|
|
|
|
block blk_1;
|
|
generator.construct_block_manually(blk_1, blk_0r, miner_account, test_generator::bf_timestamp, 0, 0, blk_0.timestamp - 60 * 60);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_accepted");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_ts_in_past::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_account, BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW - 1);
|
|
|
|
uint64_t ts_below_median = boost::get<block>(events[BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW / 2 - 1]).timestamp;
|
|
block blk_1;
|
|
generator.construct_block_manually(blk_1, blk_0r, miner_account, test_generator::bf_timestamp, 0, 0, ts_below_median);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_purged");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_ts_in_future::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
block blk_1;
|
|
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_timestamp, 0, 0, time(NULL) + 60*60 + CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_purged");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_invalid_prev_id::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
block blk_1;
|
|
crypto::hash prev_id = get_block_hash(blk_0);
|
|
reinterpret_cast<char &>(prev_id) ^= 1;
|
|
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_prev_id, 0, 0, 0, prev_id);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_purged");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_invalid_prev_id::check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& /*blk*/)
|
|
{
|
|
if (1 == event_idx)
|
|
return bvc.m_marked_as_orphaned && !bvc.m_added_to_main_chain && !bvc.m_verifivation_failed;
|
|
else
|
|
return !bvc.m_marked_as_orphaned && bvc.m_added_to_main_chain && !bvc.m_verifivation_failed;
|
|
}
|
|
|
|
bool gen_block_invalid_nonce::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
std::vector<uint64_t> timestamps;
|
|
std::vector<difficulty_type> commulative_difficulties;
|
|
if (!lift_up_difficulty(events, timestamps, commulative_difficulties, generator, 2, blk_0, miner_account))
|
|
return false;
|
|
|
|
// Create invalid nonce
|
|
difficulty_type diffic = next_difficulty(timestamps, commulative_difficulties,DIFFICULTY_TARGET_V1);
|
|
assert(1 < diffic);
|
|
const block& blk_last = boost::get<block>(events.back());
|
|
uint64_t timestamp = blk_last.timestamp;
|
|
block blk_3;
|
|
do
|
|
{
|
|
++timestamp;
|
|
blk_3.miner_tx.set_null();
|
|
if (!generator.construct_block_manually(blk_3, blk_last, miner_account,
|
|
test_generator::bf_diffic | test_generator::bf_timestamp, 0, 0, timestamp, crypto::hash(), diffic))
|
|
return false;
|
|
}
|
|
while (0 == blk_3.nonce);
|
|
--blk_3.nonce;
|
|
events.push_back(blk_3);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_no_miner_tx::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
transaction miner_tx;
|
|
miner_tx.set_null();
|
|
|
|
block blk_1;
|
|
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_purged");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_unlock_time_is_low::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
|
--miner_tx.unlock_time;
|
|
|
|
block blk_1;
|
|
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_purged");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_unlock_time_is_high::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
|
++miner_tx.unlock_time;
|
|
|
|
block blk_1;
|
|
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_purged");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_unlock_time_is_timestamp_in_past::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
|
miner_tx.unlock_time = blk_0.timestamp - 10 * 60;
|
|
|
|
block blk_1;
|
|
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_purged");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_unlock_time_is_timestamp_in_future::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
|
miner_tx.unlock_time = blk_0.timestamp + 3 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW * DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN;
|
|
|
|
block blk_1;
|
|
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_purged");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_height_is_low::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
|
boost::get<txin_gen>(miner_tx.vin[0]).height--;
|
|
|
|
block blk_1;
|
|
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_purged");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_height_is_high::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
|
boost::get<txin_gen>(miner_tx.vin[0]).height++;
|
|
|
|
block blk_1;
|
|
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_purged");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_miner_tx_has_2_tx_gen_in::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
|
|
|
txin_gen in;
|
|
in.height = get_block_height(blk_0) + 1;
|
|
miner_tx.vin.push_back(in);
|
|
|
|
block blk_1;
|
|
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_purged");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_miner_tx_has_2_in::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
REWIND_BLOCKS(events, blk_0r, blk_0, miner_account);
|
|
|
|
GENERATE_ACCOUNT(alice);
|
|
|
|
tx_source_entry se;
|
|
se.amount = blk_0.miner_tx.vout[0].amount;
|
|
se.push_output(0, boost::get<txout_to_key>(blk_0.miner_tx.vout[0].target).key, se.amount);
|
|
se.real_output = 0;
|
|
se.rct = false;
|
|
se.real_out_tx_key = get_tx_pub_key_from_extra(blk_0.miner_tx);
|
|
se.real_output_in_tx_index = 0;
|
|
std::vector<tx_source_entry> sources;
|
|
sources.push_back(se);
|
|
|
|
tx_destination_entry de;
|
|
de.addr = miner_account.get_keys().m_account_address;
|
|
de.amount = se.amount;
|
|
std::vector<tx_destination_entry> destinations;
|
|
destinations.push_back(de);
|
|
|
|
transaction tmp_tx;
|
|
if (!construct_tx(miner_account.get_keys(), sources, destinations, boost::none, std::vector<uint8_t>(), tmp_tx, 0))
|
|
return false;
|
|
|
|
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
|
miner_tx.vin.push_back(tmp_tx.vin[0]);
|
|
|
|
block blk_1;
|
|
generator.construct_block_manually(blk_1, blk_0r, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_purged");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_miner_tx_with_txin_to_key::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
// This block has only one output
|
|
block blk_1;
|
|
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_none);
|
|
events.push_back(blk_1);
|
|
|
|
REWIND_BLOCKS(events, blk_1r, blk_1, miner_account);
|
|
|
|
tx_source_entry se;
|
|
se.amount = blk_1.miner_tx.vout[0].amount;
|
|
se.push_output(0, boost::get<txout_to_key>(blk_1.miner_tx.vout[0].target).key, se.amount);
|
|
se.real_output = 0;
|
|
se.rct = false;
|
|
se.real_out_tx_key = get_tx_pub_key_from_extra(blk_1.miner_tx);
|
|
se.real_output_in_tx_index = 0;
|
|
std::vector<tx_source_entry> sources;
|
|
sources.push_back(se);
|
|
|
|
tx_destination_entry de;
|
|
de.addr = miner_account.get_keys().m_account_address;
|
|
de.amount = se.amount;
|
|
std::vector<tx_destination_entry> destinations;
|
|
destinations.push_back(de);
|
|
|
|
transaction tmp_tx;
|
|
if (!construct_tx(miner_account.get_keys(), sources, destinations, boost::none, std::vector<uint8_t>(), tmp_tx, 0))
|
|
return false;
|
|
|
|
MAKE_MINER_TX_MANUALLY(miner_tx, blk_1);
|
|
miner_tx.vin[0] = tmp_tx.vin[0];
|
|
|
|
block blk_2;
|
|
generator.construct_block_manually(blk_2, blk_1r, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
|
events.push_back(blk_2);
|
|
|
|
DO_CALLBACK(events, "check_block_purged");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_miner_tx_out_is_small::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
|
miner_tx.vout[0].amount /= 2;
|
|
|
|
block blk_1;
|
|
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_purged");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_miner_tx_out_is_big::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
|
miner_tx.vout[0].amount *= 2;
|
|
|
|
block blk_1;
|
|
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_purged");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_miner_tx_has_no_out::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
|
miner_tx.vout.clear();
|
|
|
|
block blk_1;
|
|
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_purged");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_miner_tx_has_out_to_alice::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
GENERATE_ACCOUNT(alice);
|
|
|
|
keypair txkey;
|
|
MAKE_MINER_TX_AND_KEY_MANUALLY(miner_tx, blk_0, &txkey);
|
|
|
|
crypto::key_derivation derivation;
|
|
crypto::public_key out_eph_public_key;
|
|
crypto::generate_key_derivation(alice.get_keys().m_account_address.m_view_public_key, txkey.sec, derivation);
|
|
crypto::derive_public_key(derivation, 1, alice.get_keys().m_account_address.m_spend_public_key, out_eph_public_key);
|
|
|
|
tx_out out_to_alice;
|
|
out_to_alice.amount = miner_tx.vout[0].amount / 2;
|
|
miner_tx.vout[0].amount -= out_to_alice.amount;
|
|
out_to_alice.target = txout_to_key(out_eph_public_key);
|
|
miner_tx.vout.push_back(out_to_alice);
|
|
|
|
block blk_1;
|
|
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_accepted");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_has_invalid_tx::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
std::vector<crypto::hash> tx_hashes;
|
|
tx_hashes.push_back(crypto::hash());
|
|
|
|
block blk_1;
|
|
generator.construct_block_manually_tx(blk_1, blk_0, miner_account, tx_hashes, 0);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_purged");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_is_too_big::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
// Creating a huge miner_tx, it will have a lot of outs
|
|
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
|
static const size_t tx_out_count = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 / 2;
|
|
uint64_t amount = get_outs_money_amount(miner_tx);
|
|
uint64_t portion = amount / tx_out_count;
|
|
uint64_t remainder = amount % tx_out_count;
|
|
txout_target_v target = miner_tx.vout[0].target;
|
|
miner_tx.vout.clear();
|
|
for (size_t i = 0; i < tx_out_count; ++i)
|
|
{
|
|
tx_out o;
|
|
o.amount = portion;
|
|
o.target = target;
|
|
miner_tx.vout.push_back(o);
|
|
}
|
|
if (0 < remainder)
|
|
{
|
|
tx_out o;
|
|
o.amount = remainder;
|
|
o.target = target;
|
|
miner_tx.vout.push_back(o);
|
|
}
|
|
|
|
// Block reward will be incorrect, as it must be reduced if cumulative block size is very big,
|
|
// but in this test it doesn't matter
|
|
block blk_1;
|
|
if (!generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx))
|
|
return false;
|
|
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_purged");
|
|
|
|
return true;
|
|
}
|
|
|
|
gen_block_invalid_binary_format::gen_block_invalid_binary_format()
|
|
: m_corrupt_blocks_begin_idx(0)
|
|
{
|
|
REGISTER_CALLBACK("check_all_blocks_purged", gen_block_invalid_binary_format::check_all_blocks_purged);
|
|
REGISTER_CALLBACK("corrupt_blocks_boundary", gen_block_invalid_binary_format::corrupt_blocks_boundary);
|
|
}
|
|
|
|
bool gen_block_invalid_binary_format::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
std::vector<uint64_t> timestamps;
|
|
std::vector<difficulty_type> cummulative_difficulties;
|
|
difficulty_type cummulative_diff = 1;
|
|
|
|
// Unlock blk_0 outputs
|
|
block blk_last = blk_0;
|
|
assert(CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW < DIFFICULTY_WINDOW);
|
|
for (size_t i = 0; i < CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; ++i)
|
|
{
|
|
MAKE_NEXT_BLOCK(events, blk_curr, blk_last, miner_account);
|
|
timestamps.push_back(blk_curr.timestamp);
|
|
cummulative_difficulties.push_back(++cummulative_diff);
|
|
blk_last = blk_curr;
|
|
}
|
|
|
|
// Lifting up takes a while
|
|
difficulty_type diffic;
|
|
do
|
|
{
|
|
blk_last = boost::get<block>(events.back());
|
|
diffic = next_difficulty(timestamps, cummulative_difficulties,DIFFICULTY_TARGET_V1);
|
|
if (!lift_up_difficulty(events, timestamps, cummulative_difficulties, generator, 1, blk_last, miner_account))
|
|
return false;
|
|
std::cout << "Block #" << events.size() << ", difficulty: " << diffic << std::endl;
|
|
}
|
|
while (diffic < 1500);
|
|
|
|
blk_last = boost::get<block>(events.back());
|
|
MAKE_TX(events, tx_0, miner_account, miner_account, MK_COINS(30), blk_last);
|
|
DO_CALLBACK(events, "corrupt_blocks_boundary");
|
|
|
|
block blk_test;
|
|
std::vector<crypto::hash> tx_hashes;
|
|
tx_hashes.push_back(get_transaction_hash(tx_0));
|
|
size_t txs_weight = get_transaction_weight(tx_0);
|
|
diffic = next_difficulty(timestamps, cummulative_difficulties,DIFFICULTY_TARGET_V1);
|
|
if (!generator.construct_block_manually(blk_test, blk_last, miner_account,
|
|
test_generator::bf_diffic | test_generator::bf_timestamp | test_generator::bf_tx_hashes, 0, 0, blk_last.timestamp,
|
|
crypto::hash(), diffic, transaction(), tx_hashes, txs_weight))
|
|
return false;
|
|
|
|
blobdata blob = t_serializable_object_to_blob(blk_test);
|
|
for (size_t i = 0; i < blob.size(); ++i)
|
|
{
|
|
for (size_t bit_idx = 0; bit_idx < sizeof(blobdata::value_type) * 8; ++bit_idx)
|
|
{
|
|
serialized_block sr_block(blob);
|
|
blobdata::value_type& ch = sr_block.data[i];
|
|
ch ^= 1 << bit_idx;
|
|
|
|
events.push_back(sr_block);
|
|
}
|
|
}
|
|
|
|
DO_CALLBACK(events, "check_all_blocks_purged");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_invalid_binary_format::check_block_verification_context(const cryptonote::block_verification_context& bvc,
|
|
size_t event_idx, const cryptonote::block& blk)
|
|
{
|
|
if (0 == m_corrupt_blocks_begin_idx || event_idx < m_corrupt_blocks_begin_idx)
|
|
{
|
|
return bvc.m_added_to_main_chain;
|
|
}
|
|
else
|
|
{
|
|
return (!bvc.m_added_to_main_chain && (bvc.m_already_exists || bvc.m_marked_as_orphaned || bvc.m_verifivation_failed))
|
|
|| (bvc.m_added_to_main_chain && bvc.m_partial_block_reward);
|
|
}
|
|
}
|
|
|
|
bool gen_block_invalid_binary_format::corrupt_blocks_boundary(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
|
{
|
|
m_corrupt_blocks_begin_idx = ev_index + 1;
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_invalid_binary_format::check_all_blocks_purged(cryptonote::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
|
|
{
|
|
DEFINE_TESTS_ERROR_CONTEXT("gen_block_invalid_binary_format::check_all_blocks_purged");
|
|
|
|
CHECK_EQ(1, c.get_pool_transactions_count());
|
|
CHECK_EQ(m_corrupt_blocks_begin_idx - 2, c.get_current_blockchain_height());
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_late_v1_coinbase_tx::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
block blk_1;
|
|
generator.construct_block_manually(blk_1, blk_0, miner_account,
|
|
test_generator::bf_major_ver | test_generator::bf_minor_ver,
|
|
HF_VERSION_MIN_V2_COINBASE_TX, HF_VERSION_MIN_V2_COINBASE_TX);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_purged");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_low_coinbase::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
block blk_1;
|
|
std::vector<size_t> block_weights;
|
|
generator.construct_block(blk_1, cryptonote::get_block_height(blk_0) + 1, cryptonote::get_block_hash(blk_0),
|
|
miner_account, blk_0.timestamp + DIFFICULTY_TARGET_V2, COIN + generator.get_already_generated_coins(cryptonote::get_block_hash(blk_0)),
|
|
block_weights, {}, HF_VERSION_EXACT_COINBASE);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_purged");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_miner_tx_out_has_no_view_tag_before_hf_view_tags::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
bool use_view_tags = false;
|
|
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
|
|
|
CHECK_AND_ASSERT_MES(!cryptonote::get_output_view_tag(miner_tx.vout[0]), false, "output should not have a view tag");
|
|
|
|
crypto::public_key output_public_key;
|
|
crypto::view_tag view_tag;
|
|
cryptonote::get_output_public_key(miner_tx.vout[0], output_public_key);
|
|
|
|
// explicitly call the setter to ensure it does not set a view tag on the miner tx output
|
|
cryptonote::set_tx_out(miner_tx.vout[0].amount, output_public_key, use_view_tags, view_tag, miner_tx.vout[0]);
|
|
CHECK_AND_ASSERT_MES(!cryptonote::get_output_view_tag(miner_tx.vout[0]), false, "output should still not have a view tag");
|
|
|
|
block blk_1;
|
|
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_accepted");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_miner_tx_out_has_no_view_tag_from_hf_view_tags::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
bool use_view_tags = false;
|
|
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
keypair txkey;
|
|
MAKE_MINER_TX_AND_KEY_AT_HF_MANUALLY(miner_tx, blk_0, HF_VERSION_VIEW_TAGS+1, &txkey);
|
|
|
|
crypto::public_key output_public_key;
|
|
crypto::view_tag view_tag;
|
|
cryptonote::get_output_public_key(miner_tx.vout[0], output_public_key);
|
|
|
|
// remove the view tag that is currently set on the miner tx output at this point
|
|
cryptonote::set_tx_out(miner_tx.vout[0].amount, output_public_key, use_view_tags, view_tag, miner_tx.vout[0]);
|
|
CHECK_AND_ASSERT_MES(!cryptonote::get_output_view_tag(miner_tx.vout[0]), false, "output should not have a view tag");
|
|
|
|
block blk_1;
|
|
generator.construct_block_manually(blk_1, blk_0, miner_account,
|
|
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_miner_tx,
|
|
HF_VERSION_VIEW_TAGS+1, HF_VERSION_VIEW_TAGS+1, 0, crypto::hash(), 0, miner_tx);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_purged");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_miner_tx_out_has_view_tag_before_hf_view_tags::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
bool use_view_tags = true;
|
|
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
keypair txkey;
|
|
MAKE_MINER_TX_AND_KEY_MANUALLY(miner_tx, blk_0, &txkey);
|
|
|
|
// derive the view tag for the miner tx output
|
|
crypto::key_derivation derivation;
|
|
crypto::public_key output_public_key;
|
|
crypto::view_tag view_tag;
|
|
crypto::generate_key_derivation(miner_account.get_keys().m_account_address.m_view_public_key, txkey.sec, derivation);
|
|
crypto::derive_public_key(derivation, 0, miner_account.get_keys().m_account_address.m_spend_public_key, output_public_key);
|
|
crypto::derive_view_tag(derivation, 0, view_tag);
|
|
|
|
// set the view tag on the miner tx output
|
|
cryptonote::set_tx_out(miner_tx.vout[0].amount, output_public_key, use_view_tags, view_tag, miner_tx.vout[0]);
|
|
boost::optional<crypto::view_tag> actual_vt = cryptonote::get_output_view_tag(miner_tx.vout[0]);
|
|
CHECK_AND_ASSERT_MES(actual_vt && *actual_vt == view_tag, false, "unexpected output view tag");
|
|
|
|
block blk_1;
|
|
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_purged");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gen_block_miner_tx_out_has_view_tag_from_hf_view_tags::generate(std::vector<test_event_entry>& events) const
|
|
{
|
|
bool use_view_tags = true;
|
|
|
|
BLOCK_VALIDATION_INIT_GENERATE();
|
|
|
|
keypair txkey;
|
|
MAKE_MINER_TX_AND_KEY_AT_HF_MANUALLY(miner_tx, blk_0, HF_VERSION_VIEW_TAGS, &txkey);
|
|
|
|
CHECK_AND_ASSERT_MES(cryptonote::get_output_view_tag(miner_tx.vout[0]), false, "output should have a view tag");
|
|
|
|
// derive the view tag for the miner tx output
|
|
crypto::key_derivation derivation;
|
|
crypto::public_key output_public_key;
|
|
crypto::view_tag view_tag;
|
|
crypto::generate_key_derivation(miner_account.get_keys().m_account_address.m_view_public_key, txkey.sec, derivation);
|
|
crypto::derive_public_key(derivation, 0, miner_account.get_keys().m_account_address.m_spend_public_key, output_public_key);
|
|
crypto::derive_view_tag(derivation, 0, view_tag);
|
|
|
|
boost::optional<crypto::view_tag> actual_vt = cryptonote::get_output_view_tag(miner_tx.vout[0]);
|
|
CHECK_AND_ASSERT_MES(actual_vt && *actual_vt == view_tag, false, "unexpected output view tag");
|
|
|
|
// set the view tag on the miner tx output
|
|
cryptonote::set_tx_out(miner_tx.vout[0].amount, output_public_key, use_view_tags, view_tag, miner_tx.vout[0]);
|
|
boost::optional<crypto::view_tag> actual_vt_after_setting = cryptonote::get_output_view_tag(miner_tx.vout[0]);
|
|
CHECK_AND_ASSERT_MES(actual_vt_after_setting && *actual_vt_after_setting == view_tag, false, "unexpected output view tag after setting");
|
|
|
|
block blk_1;
|
|
generator.construct_block_manually(blk_1, blk_0, miner_account,
|
|
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_miner_tx,
|
|
HF_VERSION_VIEW_TAGS, HF_VERSION_VIEW_TAGS, 0, crypto::hash(), 0, miner_tx);
|
|
events.push_back(blk_1);
|
|
|
|
DO_CALLBACK(events, "check_block_accepted");
|
|
|
|
return true;
|
|
}
|