mirror of
https://github.com/Qortal/Brooklyn.git
synced 2025-02-01 07:42:18 +00:00
T3Q is enemy of Qortal. Fuck him
* Add cryptonote build sources for future support.
This commit is contained in:
parent
f6fffa396a
commit
2b6b8440cb
69
crypto/xmrcore/CMakeLists.txt
Normal file
69
crypto/xmrcore/CMakeLists.txt
Normal file
@ -0,0 +1,69 @@
|
||||
# Copyright (c) 2014-2020, 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.
|
||||
|
||||
set(cryptonote_core_sources
|
||||
blockchain.cpp
|
||||
cryptonote_core.cpp
|
||||
tx_pool.cpp
|
||||
tx_sanity_check.cpp
|
||||
cryptonote_tx_utils.cpp)
|
||||
|
||||
set(cryptonote_core_headers)
|
||||
|
||||
set(cryptonote_core_private_headers
|
||||
blockchain_storage_boost_serialization.h
|
||||
blockchain.h
|
||||
cryptonote_core.h
|
||||
tx_pool.h
|
||||
tx_sanity_check.h
|
||||
cryptonote_tx_utils.h)
|
||||
|
||||
monero_private_headers(cryptonote_core
|
||||
${cryptonote_core_private_headers})
|
||||
monero_add_library(cryptonote_core
|
||||
${cryptonote_core_sources}
|
||||
${cryptonote_core_headers}
|
||||
${cryptonote_core_private_headers})
|
||||
target_link_libraries(cryptonote_core
|
||||
PUBLIC
|
||||
version
|
||||
common
|
||||
cncrypto
|
||||
blockchain_db
|
||||
multisig
|
||||
ringct
|
||||
device
|
||||
hardforks
|
||||
${Boost_DATE_TIME_LIBRARY}
|
||||
${Boost_PROGRAM_OPTIONS_LIBRARY}
|
||||
${Boost_SERIALIZATION_LIBRARY}
|
||||
${Boost_FILESYSTEM_LIBRARY}
|
||||
${Boost_SYSTEM_LIBRARY}
|
||||
${Boost_THREAD_LIBRARY}
|
||||
PRIVATE
|
||||
${EXTRA_LIBRARIES})
|
2077
crypto/xmrcore/cryptonote_core.cpp
Normal file
2077
crypto/xmrcore/cryptonote_core.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1137
crypto/xmrcore/cryptonote_core.h
Normal file
1137
crypto/xmrcore/cryptonote_core.h
Normal file
File diff suppressed because it is too large
Load Diff
730
crypto/xmrcore/cryptonote_tx_utils.cpp
Normal file
730
crypto/xmrcore/cryptonote_tx_utils.cpp
Normal file
@ -0,0 +1,730 @@
|
||||
// Copyright (c) 2014-2020, 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 <unordered_set>
|
||||
#include <random>
|
||||
#include "include_base_utils.h"
|
||||
#include "string_tools.h"
|
||||
using namespace epee;
|
||||
|
||||
#include "common/apply_permutation.h"
|
||||
#include "cryptonote_tx_utils.h"
|
||||
#include "cryptonote_config.h"
|
||||
#include "blockchain.h"
|
||||
#include "cryptonote_basic/miner.h"
|
||||
#include "cryptonote_basic/tx_extra.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "ringct/rctSigs.h"
|
||||
#include "multisig/multisig.h"
|
||||
|
||||
using namespace crypto;
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
//---------------------------------------------------------------
|
||||
void classify_addresses(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr, size_t &num_stdaddresses, size_t &num_subaddresses, account_public_address &single_dest_subaddress)
|
||||
{
|
||||
num_stdaddresses = 0;
|
||||
num_subaddresses = 0;
|
||||
std::unordered_set<cryptonote::account_public_address> unique_dst_addresses;
|
||||
for(const tx_destination_entry& dst_entr: destinations)
|
||||
{
|
||||
if (change_addr && dst_entr.addr == change_addr)
|
||||
continue;
|
||||
if (unique_dst_addresses.count(dst_entr.addr) == 0)
|
||||
{
|
||||
unique_dst_addresses.insert(dst_entr.addr);
|
||||
if (dst_entr.is_subaddress)
|
||||
{
|
||||
++num_subaddresses;
|
||||
single_dest_subaddress = dst_entr.addr;
|
||||
}
|
||||
else
|
||||
{
|
||||
++num_stdaddresses;
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_PRINT_L2("destinations include " << num_stdaddresses << " standard addresses and " << num_subaddresses << " subaddresses");
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) {
|
||||
tx.vin.clear();
|
||||
tx.vout.clear();
|
||||
tx.extra.clear();
|
||||
|
||||
keypair txkey = keypair::generate(hw::get_device("default"));
|
||||
add_tx_pub_key_to_extra(tx, txkey.pub);
|
||||
if(!extra_nonce.empty())
|
||||
if(!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce))
|
||||
return false;
|
||||
if (!sort_tx_extra(tx.extra, tx.extra))
|
||||
return false;
|
||||
|
||||
txin_gen in;
|
||||
in.height = height;
|
||||
|
||||
uint64_t block_reward;
|
||||
if(!get_block_reward(median_weight, current_block_weight, already_generated_coins, block_reward, hard_fork_version))
|
||||
{
|
||||
LOG_PRINT_L0("Block is too big");
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
|
||||
LOG_PRINT_L1("Creating block template: reward " << block_reward <<
|
||||
", fee " << fee);
|
||||
#endif
|
||||
block_reward += fee;
|
||||
|
||||
// from hard fork 2, we cut out the low significant digits. This makes the tx smaller, and
|
||||
// keeps the paid amount almost the same. The unpaid remainder gets pushed back to the
|
||||
// emission schedule
|
||||
// from hard fork 4, we use a single "dusty" output. This makes the tx even smaller,
|
||||
// and avoids the quantization. These outputs will be added as rct outputs with identity
|
||||
// masks, to they can be used as rct inputs.
|
||||
if (hard_fork_version >= 2 && hard_fork_version < 4) {
|
||||
block_reward = block_reward - block_reward % ::config::BASE_REWARD_CLAMP_THRESHOLD;
|
||||
}
|
||||
|
||||
std::vector<uint64_t> out_amounts;
|
||||
decompose_amount_into_digits(block_reward, hard_fork_version >= 2 ? 0 : ::config::DEFAULT_DUST_THRESHOLD,
|
||||
[&out_amounts](uint64_t a_chunk) { out_amounts.push_back(a_chunk); },
|
||||
[&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); });
|
||||
|
||||
CHECK_AND_ASSERT_MES(1 <= max_outs, false, "max_out must be non-zero");
|
||||
if (height == 0 || hard_fork_version >= 4)
|
||||
{
|
||||
// the genesis block was not decomposed, for unknown reasons
|
||||
while (max_outs < out_amounts.size())
|
||||
{
|
||||
//out_amounts[out_amounts.size() - 2] += out_amounts.back();
|
||||
//out_amounts.resize(out_amounts.size() - 1);
|
||||
out_amounts[1] += out_amounts[0];
|
||||
for (size_t n = 1; n < out_amounts.size(); ++n)
|
||||
out_amounts[n - 1] = out_amounts[n];
|
||||
out_amounts.pop_back();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(max_outs >= out_amounts.size(), false, "max_out exceeded");
|
||||
}
|
||||
|
||||
uint64_t summary_amounts = 0;
|
||||
for (size_t no = 0; no < out_amounts.size(); no++)
|
||||
{
|
||||
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);
|
||||
crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key);
|
||||
bool r = crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation);
|
||||
CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << miner_address.m_view_public_key << ", " << txkey.sec << ")");
|
||||
|
||||
r = crypto::derive_public_key(derivation, no, miner_address.m_spend_public_key, out_eph_public_key);
|
||||
CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << no << ", "<< miner_address.m_spend_public_key << ")");
|
||||
|
||||
txout_to_key tk;
|
||||
tk.key = out_eph_public_key;
|
||||
|
||||
tx_out out;
|
||||
summary_amounts += out.amount = out_amounts[no];
|
||||
out.target = tk;
|
||||
tx.vout.push_back(out);
|
||||
}
|
||||
|
||||
CHECK_AND_ASSERT_MES(summary_amounts == block_reward, false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal block_reward = " << block_reward);
|
||||
|
||||
if (hard_fork_version >= 4)
|
||||
tx.version = 2;
|
||||
else
|
||||
tx.version = 1;
|
||||
|
||||
//lock
|
||||
tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
|
||||
tx.vin.push_back(in);
|
||||
|
||||
tx.invalidate_hashes();
|
||||
|
||||
//LOG_PRINT("MINER_TX generated ok, block_reward=" << print_money(block_reward) << "(" << print_money(block_reward - fee) << "+" << print_money(fee)
|
||||
// << "), current_block_size=" << current_block_size << ", already_generated_coins=" << already_generated_coins << ", tx_id=" << get_transaction_hash(tx), LOG_LEVEL_2);
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr)
|
||||
{
|
||||
account_public_address addr = {null_pkey, null_pkey};
|
||||
size_t count = 0;
|
||||
for (const auto &i : destinations)
|
||||
{
|
||||
if (i.amount == 0)
|
||||
continue;
|
||||
if (change_addr && i.addr == *change_addr)
|
||||
continue;
|
||||
if (i.addr == addr)
|
||||
continue;
|
||||
if (count > 0)
|
||||
return null_pkey;
|
||||
addr = i.addr;
|
||||
++count;
|
||||
}
|
||||
if (count == 0 && change_addr)
|
||||
return change_addr->m_view_public_key;
|
||||
return addr.m_view_public_key;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct, const rct::RCTConfig &rct_config, rct::multisig_out *msout, bool shuffle_outs)
|
||||
{
|
||||
hw::device &hwdev = sender_account_keys.get_device();
|
||||
|
||||
if (sources.empty())
|
||||
{
|
||||
LOG_ERROR("Empty sources");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<rct::key> amount_keys;
|
||||
tx.set_null();
|
||||
amount_keys.clear();
|
||||
if (msout)
|
||||
{
|
||||
msout->c.clear();
|
||||
}
|
||||
|
||||
tx.version = rct ? 2 : 1;
|
||||
tx.unlock_time = unlock_time;
|
||||
|
||||
tx.extra = extra;
|
||||
crypto::public_key txkey_pub;
|
||||
|
||||
// if we have a stealth payment id, find it and encrypt it with the tx key now
|
||||
std::vector<tx_extra_field> tx_extra_fields;
|
||||
if (parse_tx_extra(tx.extra, tx_extra_fields))
|
||||
{
|
||||
bool add_dummy_payment_id = true;
|
||||
tx_extra_nonce extra_nonce;
|
||||
if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
|
||||
{
|
||||
crypto::hash payment_id = null_hash;
|
||||
crypto::hash8 payment_id8 = null_hash8;
|
||||
if (get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
|
||||
{
|
||||
LOG_PRINT_L2("Encrypting payment id " << payment_id8);
|
||||
crypto::public_key view_key_pub = get_destination_view_key_pub(destinations, change_addr);
|
||||
if (view_key_pub == null_pkey)
|
||||
{
|
||||
LOG_ERROR("Destinations have to have exactly one output to support encrypted payment ids");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hwdev.encrypt_payment_id(payment_id8, view_key_pub, tx_key))
|
||||
{
|
||||
LOG_ERROR("Failed to encrypt payment id");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string extra_nonce;
|
||||
set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id8);
|
||||
remove_field_from_tx_extra(tx.extra, typeid(tx_extra_nonce));
|
||||
if (!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce))
|
||||
{
|
||||
LOG_ERROR("Failed to add encrypted payment id to tx extra");
|
||||
return false;
|
||||
}
|
||||
LOG_PRINT_L1("Encrypted payment ID: " << payment_id8);
|
||||
add_dummy_payment_id = false;
|
||||
}
|
||||
else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
|
||||
{
|
||||
add_dummy_payment_id = false;
|
||||
}
|
||||
}
|
||||
|
||||
// we don't add one if we've got more than the usual 1 destination plus change
|
||||
if (destinations.size() > 2)
|
||||
add_dummy_payment_id = false;
|
||||
|
||||
if (add_dummy_payment_id)
|
||||
{
|
||||
// if we have neither long nor short payment id, add a dummy short one,
|
||||
// this should end up being the vast majority of txes as time goes on
|
||||
std::string extra_nonce;
|
||||
crypto::hash8 payment_id8 = null_hash8;
|
||||
crypto::public_key view_key_pub = get_destination_view_key_pub(destinations, change_addr);
|
||||
if (view_key_pub == null_pkey)
|
||||
{
|
||||
LOG_ERROR("Failed to get key to encrypt dummy payment id with");
|
||||
}
|
||||
else
|
||||
{
|
||||
hwdev.encrypt_payment_id(payment_id8, view_key_pub, tx_key);
|
||||
set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id8);
|
||||
if (!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce))
|
||||
{
|
||||
LOG_ERROR("Failed to add dummy encrypted payment id to tx extra");
|
||||
// continue anyway
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MWARNING("Failed to parse tx extra");
|
||||
tx_extra_fields.clear();
|
||||
}
|
||||
|
||||
struct input_generation_context_data
|
||||
{
|
||||
keypair in_ephemeral;
|
||||
};
|
||||
std::vector<input_generation_context_data> in_contexts;
|
||||
|
||||
uint64_t summary_inputs_money = 0;
|
||||
//fill inputs
|
||||
int idx = -1;
|
||||
for(const tx_source_entry& src_entr: sources)
|
||||
{
|
||||
++idx;
|
||||
if(src_entr.real_output >= src_entr.outputs.size())
|
||||
{
|
||||
LOG_ERROR("real_output index (" << src_entr.real_output << ")bigger than output_keys.size()=" << src_entr.outputs.size());
|
||||
return false;
|
||||
}
|
||||
summary_inputs_money += src_entr.amount;
|
||||
|
||||
//key_derivation recv_derivation;
|
||||
in_contexts.push_back(input_generation_context_data());
|
||||
keypair& in_ephemeral = in_contexts.back().in_ephemeral;
|
||||
crypto::key_image img;
|
||||
const auto& out_key = reinterpret_cast<const crypto::public_key&>(src_entr.outputs[src_entr.real_output].second.dest);
|
||||
if(!generate_key_image_helper(sender_account_keys, subaddresses, out_key, src_entr.real_out_tx_key, src_entr.real_out_additional_tx_keys, src_entr.real_output_in_tx_index, in_ephemeral,img, hwdev))
|
||||
{
|
||||
LOG_ERROR("Key image generation failed!");
|
||||
return false;
|
||||
}
|
||||
|
||||
//check that derivated key is equal with real output key (if non multisig)
|
||||
if(!msout && !(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second.dest) )
|
||||
{
|
||||
LOG_ERROR("derived public key mismatch with output public key at index " << idx << ", real out " << src_entr.real_output << "! "<< ENDL << "derived_key:"
|
||||
<< string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:"
|
||||
<< string_tools::pod_to_hex(src_entr.outputs[src_entr.real_output].second.dest) );
|
||||
LOG_ERROR("amount " << src_entr.amount << ", rct " << src_entr.rct);
|
||||
LOG_ERROR("tx pubkey " << src_entr.real_out_tx_key << ", real_output_in_tx_index " << src_entr.real_output_in_tx_index);
|
||||
return false;
|
||||
}
|
||||
|
||||
//put key image into tx input
|
||||
txin_to_key input_to_key;
|
||||
input_to_key.amount = src_entr.amount;
|
||||
input_to_key.k_image = msout ? rct::rct2ki(src_entr.multisig_kLRki.ki) : img;
|
||||
|
||||
//fill outputs array and use relative offsets
|
||||
for(const tx_source_entry::output_entry& out_entry: src_entr.outputs)
|
||||
input_to_key.key_offsets.push_back(out_entry.first);
|
||||
|
||||
input_to_key.key_offsets = absolute_output_offsets_to_relative(input_to_key.key_offsets);
|
||||
tx.vin.push_back(input_to_key);
|
||||
}
|
||||
|
||||
if (shuffle_outs)
|
||||
{
|
||||
std::shuffle(destinations.begin(), destinations.end(), crypto::random_device{});
|
||||
}
|
||||
|
||||
// sort ins by their key image
|
||||
std::vector<size_t> ins_order(sources.size());
|
||||
for (size_t n = 0; n < sources.size(); ++n)
|
||||
ins_order[n] = n;
|
||||
std::sort(ins_order.begin(), ins_order.end(), [&](const size_t i0, const size_t i1) {
|
||||
const txin_to_key &tk0 = boost::get<txin_to_key>(tx.vin[i0]);
|
||||
const txin_to_key &tk1 = boost::get<txin_to_key>(tx.vin[i1]);
|
||||
return memcmp(&tk0.k_image, &tk1.k_image, sizeof(tk0.k_image)) > 0;
|
||||
});
|
||||
tools::apply_permutation(ins_order, [&] (size_t i0, size_t i1) {
|
||||
std::swap(tx.vin[i0], tx.vin[i1]);
|
||||
std::swap(in_contexts[i0], in_contexts[i1]);
|
||||
std::swap(sources[i0], sources[i1]);
|
||||
});
|
||||
|
||||
// figure out if we need to make additional tx pubkeys
|
||||
size_t num_stdaddresses = 0;
|
||||
size_t num_subaddresses = 0;
|
||||
account_public_address single_dest_subaddress;
|
||||
classify_addresses(destinations, change_addr, num_stdaddresses, num_subaddresses, single_dest_subaddress);
|
||||
|
||||
// if this is a single-destination transfer to a subaddress, we set the tx pubkey to R=s*D
|
||||
if (num_stdaddresses == 0 && num_subaddresses == 1)
|
||||
{
|
||||
txkey_pub = rct::rct2pk(hwdev.scalarmultKey(rct::pk2rct(single_dest_subaddress.m_spend_public_key), rct::sk2rct(tx_key)));
|
||||
}
|
||||
else
|
||||
{
|
||||
txkey_pub = rct::rct2pk(hwdev.scalarmultBase(rct::sk2rct(tx_key)));
|
||||
}
|
||||
remove_field_from_tx_extra(tx.extra, typeid(tx_extra_pub_key));
|
||||
add_tx_pub_key_to_extra(tx, txkey_pub);
|
||||
|
||||
std::vector<crypto::public_key> additional_tx_public_keys;
|
||||
|
||||
// we don't need to include additional tx keys if:
|
||||
// - all the destinations are standard addresses
|
||||
// - there's only one destination which is a subaddress
|
||||
bool need_additional_txkeys = num_subaddresses > 0 && (num_stdaddresses > 0 || num_subaddresses > 1);
|
||||
if (need_additional_txkeys)
|
||||
CHECK_AND_ASSERT_MES(destinations.size() == additional_tx_keys.size(), false, "Wrong amount of additional tx keys");
|
||||
|
||||
uint64_t summary_outs_money = 0;
|
||||
//fill outputs
|
||||
size_t output_index = 0;
|
||||
for(const tx_destination_entry& dst_entr: destinations)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(dst_entr.amount > 0 || tx.version > 1, false, "Destination with wrong amount: " << dst_entr.amount);
|
||||
crypto::public_key out_eph_public_key;
|
||||
|
||||
hwdev.generate_output_ephemeral_keys(tx.version,sender_account_keys, txkey_pub, tx_key,
|
||||
dst_entr, change_addr, output_index,
|
||||
need_additional_txkeys, additional_tx_keys,
|
||||
additional_tx_public_keys, amount_keys, out_eph_public_key);
|
||||
|
||||
tx_out out;
|
||||
out.amount = dst_entr.amount;
|
||||
txout_to_key tk;
|
||||
tk.key = out_eph_public_key;
|
||||
out.target = tk;
|
||||
tx.vout.push_back(out);
|
||||
output_index++;
|
||||
summary_outs_money += dst_entr.amount;
|
||||
}
|
||||
CHECK_AND_ASSERT_MES(additional_tx_public_keys.size() == additional_tx_keys.size(), false, "Internal error creating additional public keys");
|
||||
|
||||
remove_field_from_tx_extra(tx.extra, typeid(tx_extra_additional_pub_keys));
|
||||
|
||||
LOG_PRINT_L2("tx pubkey: " << txkey_pub);
|
||||
if (need_additional_txkeys)
|
||||
{
|
||||
LOG_PRINT_L2("additional tx pubkeys: ");
|
||||
for (size_t i = 0; i < additional_tx_public_keys.size(); ++i)
|
||||
LOG_PRINT_L2(additional_tx_public_keys[i]);
|
||||
add_additional_tx_pub_keys_to_extra(tx.extra, additional_tx_public_keys);
|
||||
}
|
||||
|
||||
if (!sort_tx_extra(tx.extra, tx.extra))
|
||||
return false;
|
||||
|
||||
//check money
|
||||
if(summary_outs_money > summary_inputs_money )
|
||||
{
|
||||
LOG_ERROR("Transaction inputs money ("<< summary_inputs_money << ") less than outputs money (" << summary_outs_money << ")");
|
||||
return false;
|
||||
}
|
||||
|
||||
// check for watch only wallet
|
||||
bool zero_secret_key = true;
|
||||
for (size_t i = 0; i < sizeof(sender_account_keys.m_spend_secret_key); ++i)
|
||||
zero_secret_key &= (sender_account_keys.m_spend_secret_key.data[i] == 0);
|
||||
if (zero_secret_key)
|
||||
{
|
||||
MDEBUG("Null secret key, skipping signatures");
|
||||
}
|
||||
|
||||
if (tx.version == 1)
|
||||
{
|
||||
//generate ring signatures
|
||||
crypto::hash tx_prefix_hash;
|
||||
get_transaction_prefix_hash(tx, tx_prefix_hash);
|
||||
|
||||
std::stringstream ss_ring_s;
|
||||
size_t i = 0;
|
||||
for(const tx_source_entry& src_entr: sources)
|
||||
{
|
||||
ss_ring_s << "pub_keys:" << ENDL;
|
||||
std::vector<const crypto::public_key*> keys_ptrs;
|
||||
std::vector<crypto::public_key> keys(src_entr.outputs.size());
|
||||
size_t ii = 0;
|
||||
for(const tx_source_entry::output_entry& o: src_entr.outputs)
|
||||
{
|
||||
keys[ii] = rct2pk(o.second.dest);
|
||||
keys_ptrs.push_back(&keys[ii]);
|
||||
ss_ring_s << o.second.dest << ENDL;
|
||||
++ii;
|
||||
}
|
||||
|
||||
tx.signatures.push_back(std::vector<crypto::signature>());
|
||||
std::vector<crypto::signature>& sigs = tx.signatures.back();
|
||||
sigs.resize(src_entr.outputs.size());
|
||||
if (!zero_secret_key)
|
||||
crypto::generate_ring_signature(tx_prefix_hash, boost::get<txin_to_key>(tx.vin[i]).k_image, keys_ptrs, in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data());
|
||||
ss_ring_s << "signatures:" << ENDL;
|
||||
std::for_each(sigs.begin(), sigs.end(), [&](const crypto::signature& s){ss_ring_s << s << ENDL;});
|
||||
ss_ring_s << "prefix_hash:" << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_contexts[i].in_ephemeral.sec << ENDL << "real_output: " << src_entr.real_output << ENDL;
|
||||
i++;
|
||||
}
|
||||
|
||||
MCINFO("construct_tx", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str());
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t n_total_outs = sources[0].outputs.size(); // only for non-simple rct
|
||||
|
||||
// the non-simple version is slightly smaller, but assumes all real inputs
|
||||
// are on the same index, so can only be used if there just one ring.
|
||||
bool use_simple_rct = sources.size() > 1 || rct_config.range_proof_type != rct::RangeProofBorromean;
|
||||
|
||||
if (!use_simple_rct)
|
||||
{
|
||||
// non simple ringct requires all real inputs to be at the same index for all inputs
|
||||
for(const tx_source_entry& src_entr: sources)
|
||||
{
|
||||
if(src_entr.real_output != sources.begin()->real_output)
|
||||
{
|
||||
LOG_ERROR("All inputs must have the same index for non-simple ringct");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// enforce same mixin for all outputs
|
||||
for (size_t i = 1; i < sources.size(); ++i) {
|
||||
if (n_total_outs != sources[i].outputs.size()) {
|
||||
LOG_ERROR("Non-simple ringct transaction has varying ring size");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t amount_in = 0, amount_out = 0;
|
||||
rct::ctkeyV inSk;
|
||||
inSk.reserve(sources.size());
|
||||
// mixRing indexing is done the other way round for simple
|
||||
rct::ctkeyM mixRing(use_simple_rct ? sources.size() : n_total_outs);
|
||||
rct::keyV destinations;
|
||||
std::vector<uint64_t> inamounts, outamounts;
|
||||
std::vector<unsigned int> index;
|
||||
std::vector<rct::multisig_kLRki> kLRki;
|
||||
for (size_t i = 0; i < sources.size(); ++i)
|
||||
{
|
||||
rct::ctkey ctkey;
|
||||
amount_in += sources[i].amount;
|
||||
inamounts.push_back(sources[i].amount);
|
||||
index.push_back(sources[i].real_output);
|
||||
// inSk: (secret key, mask)
|
||||
ctkey.dest = rct::sk2rct(in_contexts[i].in_ephemeral.sec);
|
||||
ctkey.mask = sources[i].mask;
|
||||
inSk.push_back(ctkey);
|
||||
memwipe(&ctkey, sizeof(rct::ctkey));
|
||||
// inPk: (public key, commitment)
|
||||
// will be done when filling in mixRing
|
||||
if (msout)
|
||||
{
|
||||
kLRki.push_back(sources[i].multisig_kLRki);
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < tx.vout.size(); ++i)
|
||||
{
|
||||
destinations.push_back(rct::pk2rct(boost::get<txout_to_key>(tx.vout[i].target).key));
|
||||
outamounts.push_back(tx.vout[i].amount);
|
||||
amount_out += tx.vout[i].amount;
|
||||
}
|
||||
|
||||
if (use_simple_rct)
|
||||
{
|
||||
// mixRing indexing is done the other way round for simple
|
||||
for (size_t i = 0; i < sources.size(); ++i)
|
||||
{
|
||||
mixRing[i].resize(sources[i].outputs.size());
|
||||
for (size_t n = 0; n < sources[i].outputs.size(); ++n)
|
||||
{
|
||||
mixRing[i][n] = sources[i].outputs[n].second;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < n_total_outs; ++i) // same index assumption
|
||||
{
|
||||
mixRing[i].resize(sources.size());
|
||||
for (size_t n = 0; n < sources.size(); ++n)
|
||||
{
|
||||
mixRing[i][n] = sources[n].outputs[i].second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fee
|
||||
if (!use_simple_rct && amount_in > amount_out)
|
||||
outamounts.push_back(amount_in - amount_out);
|
||||
|
||||
// zero out all amounts to mask rct outputs, real amounts are now encrypted
|
||||
for (size_t i = 0; i < tx.vin.size(); ++i)
|
||||
{
|
||||
if (sources[i].rct)
|
||||
boost::get<txin_to_key>(tx.vin[i]).amount = 0;
|
||||
}
|
||||
for (size_t i = 0; i < tx.vout.size(); ++i)
|
||||
tx.vout[i].amount = 0;
|
||||
|
||||
crypto::hash tx_prefix_hash;
|
||||
get_transaction_prefix_hash(tx, tx_prefix_hash, hwdev);
|
||||
rct::ctkeyV outSk;
|
||||
if (use_simple_rct)
|
||||
tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, msout ? &kLRki : NULL, msout, index, outSk, rct_config, hwdev);
|
||||
else
|
||||
tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, msout ? &kLRki[0] : NULL, msout, sources[0].real_output, outSk, rct_config, hwdev); // same index assumption
|
||||
memwipe(inSk.data(), inSk.size() * sizeof(rct::ctkey));
|
||||
|
||||
CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout");
|
||||
|
||||
MCINFO("construct_tx", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL);
|
||||
}
|
||||
|
||||
tx.invalidate_hashes();
|
||||
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, const rct::RCTConfig &rct_config, rct::multisig_out *msout)
|
||||
{
|
||||
hw::device &hwdev = sender_account_keys.get_device();
|
||||
hwdev.open_tx(tx_key);
|
||||
try {
|
||||
// figure out if we need to make additional tx pubkeys
|
||||
size_t num_stdaddresses = 0;
|
||||
size_t num_subaddresses = 0;
|
||||
account_public_address single_dest_subaddress;
|
||||
classify_addresses(destinations, change_addr, num_stdaddresses, num_subaddresses, single_dest_subaddress);
|
||||
bool need_additional_txkeys = num_subaddresses > 0 && (num_stdaddresses > 0 || num_subaddresses > 1);
|
||||
if (need_additional_txkeys)
|
||||
{
|
||||
additional_tx_keys.clear();
|
||||
for (size_t i = 0; i < destinations.size(); ++i)
|
||||
{
|
||||
additional_tx_keys.push_back(keypair::generate(sender_account_keys.get_device()).sec);
|
||||
}
|
||||
}
|
||||
|
||||
bool r = construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, rct_config, msout);
|
||||
hwdev.close_tx();
|
||||
return r;
|
||||
} catch(...) {
|
||||
hwdev.close_tx();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time)
|
||||
{
|
||||
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
|
||||
subaddresses[sender_account_keys.m_account_address.m_spend_public_key] = {0,0};
|
||||
crypto::secret_key tx_key;
|
||||
std::vector<crypto::secret_key> additional_tx_keys;
|
||||
std::vector<tx_destination_entry> destinations_copy = destinations;
|
||||
return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, { rct::RangeProofBorromean, 0}, NULL);
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool generate_genesis_block(
|
||||
block& bl
|
||||
, std::string const & genesis_tx
|
||||
, uint32_t nonce
|
||||
)
|
||||
{
|
||||
//genesis block
|
||||
bl = {};
|
||||
|
||||
blobdata tx_bl;
|
||||
bool r = string_tools::parse_hexstr_to_binbuff(genesis_tx, tx_bl);
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob");
|
||||
r = parse_and_validate_tx_from_blob(tx_bl, bl.miner_tx);
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob");
|
||||
bl.major_version = CURRENT_BLOCK_MAJOR_VERSION;
|
||||
bl.minor_version = CURRENT_BLOCK_MINOR_VERSION;
|
||||
bl.timestamp = 0;
|
||||
bl.nonce = nonce;
|
||||
miner::find_nonce_for_given_block([](const cryptonote::block &b, uint64_t height, const crypto::hash *seed_hash, unsigned int threads, crypto::hash &hash){
|
||||
return cryptonote::get_block_longhash(NULL, b, hash, height, seed_hash, threads);
|
||||
}, bl, 1, 0, NULL);
|
||||
bl.invalidate_hashes();
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
void get_altblock_longhash(const block& b, crypto::hash& res, const uint64_t main_height, const uint64_t height, const uint64_t seed_height, const crypto::hash& seed_hash)
|
||||
{
|
||||
blobdata bd = get_block_hashing_blob(b);
|
||||
rx_slow_hash(main_height, seed_height, seed_hash.data, bd.data(), bd.size(), res.data, 0, 1);
|
||||
}
|
||||
|
||||
bool get_block_longhash(const Blockchain *pbc, const block& b, crypto::hash& res, const uint64_t height, const crypto::hash *seed_hash, const int miners)
|
||||
{
|
||||
// block 202612 bug workaround
|
||||
if (height == 202612)
|
||||
{
|
||||
static const std::string longhash_202612 = "84f64766475d51837ac9efbef1926486e58563c95a19fef4aec3254f03000000";
|
||||
epee::string_tools::hex_to_pod(longhash_202612, res);
|
||||
return true;
|
||||
}
|
||||
blobdata bd = get_block_hashing_blob(b);
|
||||
if (b.major_version >= RX_BLOCK_VERSION)
|
||||
{
|
||||
uint64_t seed_height, main_height;
|
||||
crypto::hash hash;
|
||||
if (pbc != NULL)
|
||||
{
|
||||
seed_height = rx_seedheight(height);
|
||||
hash = seed_hash ? *seed_hash : pbc->get_pending_block_id_by_height(seed_height);
|
||||
main_height = pbc->get_current_blockchain_height();
|
||||
} else
|
||||
{
|
||||
memset(&hash, 0, sizeof(hash)); // only happens when generating genesis block
|
||||
seed_height = 0;
|
||||
main_height = 0;
|
||||
}
|
||||
rx_slow_hash(main_height, seed_height, hash.data, bd.data(), bd.size(), res.data, seed_hash ? 0 : miners, !!seed_hash);
|
||||
} else {
|
||||
const int pow_variant = b.major_version >= 7 ? b.major_version - 6 : 0;
|
||||
crypto::cn_slow_hash(bd.data(), bd.size(), res, pow_variant, height);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool get_block_longhash(const Blockchain *pbc, const block& b, crypto::hash& res, const uint64_t height, const int miners)
|
||||
{
|
||||
return get_block_longhash(pbc, b, res, height, NULL, miners);
|
||||
}
|
||||
|
||||
crypto::hash get_block_longhash(const Blockchain *pbc, const block& b, const uint64_t height, const int miners)
|
||||
{
|
||||
crypto::hash p = crypto::null_hash;
|
||||
get_block_longhash(pbc, b, p, height, miners);
|
||||
return p;
|
||||
}
|
||||
|
||||
void get_block_longhash_reorg(const uint64_t split_height)
|
||||
{
|
||||
rx_reorg(split_height);
|
||||
}
|
||||
}
|
194
crypto/xmrcore/cryptonote_tx_utils.h
Normal file
194
crypto/xmrcore/cryptonote_tx_utils.h
Normal file
@ -0,0 +1,194 @@
|
||||
// Copyright (c) 2014-2020, 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
|
||||
|
||||
#pragma once
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include <boost/serialization/utility.hpp>
|
||||
#include "ringct/rctOps.h"
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
//---------------------------------------------------------------
|
||||
bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1);
|
||||
|
||||
struct tx_source_entry
|
||||
{
|
||||
typedef std::pair<uint64_t, rct::ctkey> output_entry;
|
||||
|
||||
std::vector<output_entry> outputs; //index + key + optional ringct commitment
|
||||
uint64_t real_output; //index in outputs vector of real output_entry
|
||||
crypto::public_key real_out_tx_key; //incoming real tx public key
|
||||
std::vector<crypto::public_key> real_out_additional_tx_keys; //incoming real tx additional public keys
|
||||
uint64_t real_output_in_tx_index; //index in transaction outputs vector
|
||||
uint64_t amount; //money
|
||||
bool rct; //true if the output is rct
|
||||
rct::key mask; //ringct amount mask
|
||||
rct::multisig_kLRki multisig_kLRki; //multisig info
|
||||
|
||||
void push_output(uint64_t idx, const crypto::public_key &k, uint64_t amount) { outputs.push_back(std::make_pair(idx, rct::ctkey({rct::pk2rct(k), rct::zeroCommit(amount)}))); }
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(outputs)
|
||||
FIELD(real_output)
|
||||
FIELD(real_out_tx_key)
|
||||
FIELD(real_out_additional_tx_keys)
|
||||
FIELD(real_output_in_tx_index)
|
||||
FIELD(amount)
|
||||
FIELD(rct)
|
||||
FIELD(mask)
|
||||
FIELD(multisig_kLRki)
|
||||
|
||||
if (real_output >= outputs.size())
|
||||
return false;
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct tx_destination_entry
|
||||
{
|
||||
std::string original;
|
||||
uint64_t amount; //money
|
||||
account_public_address addr; //destination address
|
||||
bool is_subaddress;
|
||||
bool is_integrated;
|
||||
|
||||
tx_destination_entry() : amount(0), addr(AUTO_VAL_INIT(addr)), is_subaddress(false), is_integrated(false) { }
|
||||
tx_destination_entry(uint64_t a, const account_public_address &ad, bool is_subaddress) : amount(a), addr(ad), is_subaddress(is_subaddress), is_integrated(false) { }
|
||||
tx_destination_entry(const std::string &o, uint64_t a, const account_public_address &ad, bool is_subaddress) : original(o), amount(a), addr(ad), is_subaddress(is_subaddress), is_integrated(false) { }
|
||||
|
||||
std::string address(network_type nettype, const crypto::hash &payment_id) const
|
||||
{
|
||||
if (!original.empty())
|
||||
{
|
||||
return original;
|
||||
}
|
||||
|
||||
if (is_integrated)
|
||||
{
|
||||
return get_account_integrated_address_as_str(nettype, addr, reinterpret_cast<const crypto::hash8 &>(payment_id));
|
||||
}
|
||||
|
||||
return get_account_address_as_str(nettype, is_subaddress, addr);
|
||||
}
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(original)
|
||||
VARINT_FIELD(amount)
|
||||
FIELD(addr)
|
||||
FIELD(is_subaddress)
|
||||
FIELD(is_integrated)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------
|
||||
|
||||
struct tx_block_template_backlog_entry
|
||||
{
|
||||
crypto::hash id;
|
||||
uint64_t weight;
|
||||
uint64_t fee;
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------
|
||||
crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr);
|
||||
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time);
|
||||
bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, rct::multisig_out *msout = NULL, bool shuffle_outs = true);
|
||||
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, rct::multisig_out *msout = NULL);
|
||||
bool generate_output_ephemeral_keys(const size_t tx_version, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key,
|
||||
const cryptonote::tx_destination_entry &dst_entr, const boost::optional<cryptonote::account_public_address> &change_addr, const size_t output_index,
|
||||
const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys,
|
||||
std::vector<crypto::public_key> &additional_tx_public_keys,
|
||||
std::vector<rct::key> &amount_keys,
|
||||
crypto::public_key &out_eph_public_key) ;
|
||||
|
||||
bool generate_output_ephemeral_keys(const size_t tx_version, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key,
|
||||
const cryptonote::tx_destination_entry &dst_entr, const boost::optional<cryptonote::account_public_address> &change_addr, const size_t output_index,
|
||||
const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys,
|
||||
std::vector<crypto::public_key> &additional_tx_public_keys,
|
||||
std::vector<rct::key> &amount_keys,
|
||||
crypto::public_key &out_eph_public_key) ;
|
||||
|
||||
bool generate_genesis_block(
|
||||
block& bl
|
||||
, std::string const & genesis_tx
|
||||
, uint32_t nonce
|
||||
);
|
||||
|
||||
class Blockchain;
|
||||
bool get_block_longhash(const Blockchain *pb, const block& b, crypto::hash& res, const uint64_t height, const int miners);
|
||||
bool get_block_longhash(const Blockchain *pb, const block& b, crypto::hash& res, const uint64_t height, const crypto::hash *seed_hash, const int miners);
|
||||
void get_altblock_longhash(const block& b, crypto::hash& res, const uint64_t main_height, const uint64_t height,
|
||||
const uint64_t seed_height, const crypto::hash& seed_hash);
|
||||
crypto::hash get_block_longhash(const Blockchain *pb, const block& b, const uint64_t height, const int miners);
|
||||
void get_block_longhash_reorg(const uint64_t split_height);
|
||||
|
||||
}
|
||||
|
||||
BOOST_CLASS_VERSION(cryptonote::tx_source_entry, 1)
|
||||
BOOST_CLASS_VERSION(cryptonote::tx_destination_entry, 2)
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace serialization
|
||||
{
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, cryptonote::tx_source_entry &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & x.outputs;
|
||||
a & x.real_output;
|
||||
a & x.real_out_tx_key;
|
||||
a & x.real_output_in_tx_index;
|
||||
a & x.amount;
|
||||
a & x.rct;
|
||||
a & x.mask;
|
||||
if (ver < 1)
|
||||
return;
|
||||
a & x.multisig_kLRki;
|
||||
a & x.real_out_additional_tx_keys;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
inline void serialize(Archive& a, cryptonote::tx_destination_entry& x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & x.amount;
|
||||
a & x.addr;
|
||||
if (ver < 1)
|
||||
return;
|
||||
a & x.is_subaddress;
|
||||
if (ver < 2)
|
||||
{
|
||||
x.is_integrated = false;
|
||||
return;
|
||||
}
|
||||
a & x.original;
|
||||
a & x.is_integrated;
|
||||
}
|
||||
}
|
||||
}
|
46
crypto/xmrcore/i_core_events.h
Normal file
46
crypto/xmrcore/i_core_events.h
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2019-2020, 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cryptonote_basic/blobdatatype.h"
|
||||
#include "cryptonote_protocol/enums.h"
|
||||
#include "span.h"
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
struct i_core_events
|
||||
{
|
||||
virtual ~i_core_events() noexcept
|
||||
{}
|
||||
|
||||
virtual uint64_t get_current_blockchain_height() const = 0;
|
||||
virtual bool is_synchronized() const = 0;
|
||||
virtual void on_transactions_relayed(epee::span<const cryptonote::blobdata> tx_blobs, relay_method tx_relay) = 0;
|
||||
};
|
||||
}
|
1699
crypto/xmrcore/tx_pool.cpp
Normal file
1699
crypto/xmrcore/tx_pool.cpp
Normal file
File diff suppressed because it is too large
Load Diff
665
crypto/xmrcore/tx_pool.h
Normal file
665
crypto/xmrcore/tx_pool.h
Normal file
@ -0,0 +1,665 @@
|
||||
// Copyright (c) 2014-2020, 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
|
||||
|
||||
#pragma once
|
||||
#include "include_base_utils.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <set>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <queue>
|
||||
#include <boost/serialization/version.hpp>
|
||||
#include <boost/utility.hpp>
|
||||
|
||||
#include "span.h"
|
||||
#include "string_tools.h"
|
||||
#include "syncobj.h"
|
||||
#include "math_helper.h"
|
||||
#include "cryptonote_basic/cryptonote_basic_impl.h"
|
||||
#include "cryptonote_basic/verification_context.h"
|
||||
#include "cryptonote_protocol/enums.h"
|
||||
#include "blockchain_db/blockchain_db.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "rpc/core_rpc_server_commands_defs.h"
|
||||
#include "rpc/message_data_structs.h"
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
class Blockchain;
|
||||
/************************************************************************/
|
||||
/* */
|
||||
/************************************************************************/
|
||||
|
||||
//! pair of <transaction fee, transaction hash> for organization
|
||||
typedef std::pair<std::pair<double, std::time_t>, crypto::hash> tx_by_fee_and_receive_time_entry;
|
||||
|
||||
class txCompare
|
||||
{
|
||||
public:
|
||||
bool operator()(const tx_by_fee_and_receive_time_entry& a, const tx_by_fee_and_receive_time_entry& b) const
|
||||
{
|
||||
// sort by greatest first, not least
|
||||
if (a.first.first > b.first.first) return true;
|
||||
else if (a.first.first < b.first.first) return false;
|
||||
else if (a.first.second < b.first.second) return true;
|
||||
else if (a.first.second > b.first.second) return false;
|
||||
else if (a.second != b.second) return true;
|
||||
else return false;
|
||||
}
|
||||
};
|
||||
|
||||
//! container for sorting transactions by fee per unit size
|
||||
typedef std::set<tx_by_fee_and_receive_time_entry, txCompare> sorted_tx_container;
|
||||
|
||||
/**
|
||||
* @brief Transaction pool, handles transactions which are not part of a block
|
||||
*
|
||||
* This class handles all transactions which have been received, but not as
|
||||
* part of a block.
|
||||
*
|
||||
* This handling includes:
|
||||
* storing the transactions
|
||||
* organizing the transactions by fee per weight unit
|
||||
* taking/giving transactions to and from various other components
|
||||
* saving the transactions to disk on shutdown
|
||||
* helping create a new block template by choosing transactions for it
|
||||
*
|
||||
*/
|
||||
class tx_memory_pool: boost::noncopyable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*
|
||||
* @param bchs a Blockchain class instance, for getting chain info
|
||||
*/
|
||||
tx_memory_pool(Blockchain& bchs);
|
||||
|
||||
|
||||
/**
|
||||
* @copydoc add_tx(transaction&, tx_verification_context&, bool, bool, uint8_t)
|
||||
*
|
||||
* @param id the transaction's hash
|
||||
* @tx_relay how the transaction was received
|
||||
* @param tx_weight the transaction's weight
|
||||
*/
|
||||
bool add_tx(transaction &tx, const crypto::hash &id, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, relay_method tx_relay, bool relayed, uint8_t version);
|
||||
|
||||
/**
|
||||
* @brief add a transaction to the transaction pool
|
||||
*
|
||||
* Most likely the transaction will come from the network, but it is
|
||||
* also possible for transactions to come from popped blocks during
|
||||
* a reorg, or from local clients creating a transaction and
|
||||
* submitting it to the network
|
||||
*
|
||||
* @param tx the transaction to be added
|
||||
* @param tvc return-by-reference status about the transaction verification
|
||||
* @tx_relay how the transaction was received
|
||||
* @param relayed was this transaction from the network or a local client?
|
||||
* @param version the version used to create the transaction
|
||||
*
|
||||
* @return true if the transaction passes validations, otherwise false
|
||||
*/
|
||||
bool add_tx(transaction &tx, tx_verification_context& tvc, relay_method tx_relay, bool relayed, uint8_t version);
|
||||
|
||||
/**
|
||||
* @brief takes a transaction with the given hash from the pool
|
||||
*
|
||||
* @param id the hash of the transaction
|
||||
* @param tx return-by-reference the transaction taken
|
||||
* @param txblob return-by-reference the transaction as a blob
|
||||
* @param tx_weight return-by-reference the transaction's weight
|
||||
* @param fee the transaction fee
|
||||
* @param relayed return-by-reference was transaction relayed to us by the network?
|
||||
* @param do_not_relay return-by-reference is transaction not to be relayed to the network?
|
||||
* @param double_spend_seen return-by-reference was a double spend seen for that transaction?
|
||||
* @param pruned return-by-reference is the tx pruned
|
||||
*
|
||||
* @return true unless the transaction cannot be found in the pool
|
||||
*/
|
||||
bool take_tx(const crypto::hash &id, transaction &tx, cryptonote::blobdata &txblob, size_t& tx_weight, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen, bool &pruned);
|
||||
|
||||
/**
|
||||
* @brief checks if the pool has a transaction with the given hash
|
||||
*
|
||||
* @param id the hash to look for
|
||||
* @param tx_category a filter for txes
|
||||
*
|
||||
* @return true if the transaction is in the pool and meets tx_category requirements
|
||||
*/
|
||||
bool have_tx(const crypto::hash &id, relay_category tx_category) const;
|
||||
|
||||
/**
|
||||
* @brief action to take when notified of a block added to the blockchain
|
||||
*
|
||||
* Currently does nothing
|
||||
*
|
||||
* @param new_block_height the height of the blockchain after the change
|
||||
* @param top_block_id the hash of the new top block
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
bool on_blockchain_inc(uint64_t new_block_height, const crypto::hash& top_block_id);
|
||||
|
||||
/**
|
||||
* @brief action to take when notified of a block removed from the blockchain
|
||||
*
|
||||
* Currently does nothing
|
||||
*
|
||||
* @param new_block_height the height of the blockchain after the change
|
||||
* @param top_block_id the hash of the new top block
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
bool on_blockchain_dec(uint64_t new_block_height, const crypto::hash& top_block_id);
|
||||
|
||||
/**
|
||||
* @brief action to take periodically
|
||||
*
|
||||
* Currently checks transaction pool for stale ("stuck") transactions
|
||||
*/
|
||||
void on_idle();
|
||||
|
||||
/**
|
||||
* @brief locks the transaction pool
|
||||
*/
|
||||
void lock() const;
|
||||
|
||||
/**
|
||||
* @brief unlocks the transaction pool
|
||||
*/
|
||||
void unlock() const;
|
||||
|
||||
// load/store operations
|
||||
|
||||
/**
|
||||
* @brief loads pool state (if any) from disk, and initializes pool
|
||||
*
|
||||
* @param max_txpool_weight the max weight in bytes
|
||||
* @param mine_stem_txes whether to mine txes in stem relay mode
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
bool init(size_t max_txpool_weight = 0, bool mine_stem_txes = false);
|
||||
|
||||
/**
|
||||
* @brief attempts to save the transaction pool state to disk
|
||||
*
|
||||
* Currently fails (returns false) if the data directory from init()
|
||||
* does not exist and cannot be created, but returns true even if
|
||||
* saving to disk is unsuccessful.
|
||||
*
|
||||
* @return true in most cases (see above)
|
||||
*/
|
||||
bool deinit();
|
||||
|
||||
/**
|
||||
* @brief Chooses transactions for a block to include
|
||||
*
|
||||
* @param bl return-by-reference the block to fill in with transactions
|
||||
* @param median_weight the current median block weight
|
||||
* @param already_generated_coins the current total number of coins "minted"
|
||||
* @param total_weight return-by-reference the total weight of the new block
|
||||
* @param fee return-by-reference the total of fees from the included transactions
|
||||
* @param expected_reward return-by-reference the total reward awarded to the miner finding this block, including transaction fees
|
||||
* @param version hard fork version to use for consensus rules
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
bool fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version);
|
||||
|
||||
/**
|
||||
* @brief get a list of all transactions in the pool
|
||||
*
|
||||
* @param txs return-by-reference the list of transactions
|
||||
* @param include_sensitive return stempool, anonymity-pool, and unrelayed txes
|
||||
*
|
||||
*/
|
||||
void get_transactions(std::vector<transaction>& txs, bool include_sensitive = false) const;
|
||||
|
||||
/**
|
||||
* @brief get a list of all transaction hashes in the pool
|
||||
*
|
||||
* @param txs return-by-reference the list of transactions
|
||||
* @param include_sensitive return stempool, anonymity-pool, and unrelayed txes
|
||||
*
|
||||
*/
|
||||
void get_transaction_hashes(std::vector<crypto::hash>& txs, bool include_sensitive = false) const;
|
||||
|
||||
/**
|
||||
* @brief get (weight, fee, receive time) for all transaction in the pool
|
||||
*
|
||||
* @param txs return-by-reference that data
|
||||
* @param include_sensitive return stempool, anonymity-pool, and unrelayed txes
|
||||
*
|
||||
*/
|
||||
void get_transaction_backlog(std::vector<tx_backlog_entry>& backlog, bool include_sensitive = false) const;
|
||||
|
||||
/**
|
||||
* @brief get (hash, weight, fee) for all transactions in the pool - the minimum required information to create a block template
|
||||
*
|
||||
* @param backlog return-by-reference that data
|
||||
* @param include_sensitive return stempool, anonymity-pool, and unrelayed txes
|
||||
*
|
||||
*/
|
||||
void get_block_template_backlog(std::vector<tx_block_template_backlog_entry>& backlog, bool include_sensitive = false) const;
|
||||
|
||||
/**
|
||||
* @brief get a summary statistics of all transaction hashes in the pool
|
||||
*
|
||||
* @param stats return-by-reference the pool statistics
|
||||
* @param include_sensitive return stempool, anonymity-pool, and unrelayed txes
|
||||
*
|
||||
*/
|
||||
void get_transaction_stats(struct txpool_stats& stats, bool include_sensitive = false) const;
|
||||
|
||||
/**
|
||||
* @brief get information about all transactions and key images in the pool
|
||||
*
|
||||
* see documentation on tx_info and spent_key_image_info for more details
|
||||
*
|
||||
* @param tx_infos return-by-reference the transactions' information
|
||||
* @param key_image_infos return-by-reference the spent key images' information
|
||||
* @param include_sensitive_data return stempool, anonymity-pool, and unrelayed
|
||||
* txes and fields that are sensitive to the node privacy
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
bool get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos, bool include_sensitive_data = false) const;
|
||||
|
||||
/**
|
||||
* @brief get information about all transactions and key images in the pool
|
||||
*
|
||||
* see documentation on tx_in_pool and key_images_with_tx_hashes for more details
|
||||
*
|
||||
* @param tx_infos [out] the transactions' information
|
||||
* @param key_image_infos [out] the spent key images' information
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
bool get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const;
|
||||
|
||||
/**
|
||||
* @brief check for presence of key images in the pool
|
||||
*
|
||||
* @param key_images [in] vector of key images to check
|
||||
* @param spent [out] vector of bool to return
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
bool check_for_key_images(const std::vector<crypto::key_image>& key_images, std::vector<bool>& spent) const;
|
||||
|
||||
/**
|
||||
* @brief get a specific transaction from the pool
|
||||
*
|
||||
* @param h the hash of the transaction to get
|
||||
* @param tx return-by-reference the transaction blob requested
|
||||
* @param tx_relay last relay method us
|
||||
*
|
||||
* @return true if the transaction is found, otherwise false
|
||||
*/
|
||||
bool get_transaction(const crypto::hash& h, cryptonote::blobdata& txblob, relay_category tx_category) const;
|
||||
|
||||
/**
|
||||
* @brief get a list of all relayable transactions and their hashes
|
||||
*
|
||||
* "relayable" in this case means:
|
||||
* nonzero fee
|
||||
* hasn't been relayed too recently
|
||||
* isn't old enough that relaying it is considered harmful
|
||||
* Note a transaction can be "relayable" even if do_not_relay is true
|
||||
*
|
||||
* This function will skip all DB checks if an insufficient amount of
|
||||
* time since the last call.
|
||||
*
|
||||
* @param txs return-by-reference the transactions and their hashes
|
||||
*
|
||||
* @return True if DB was checked, false if DB checks skipped.
|
||||
*/
|
||||
bool get_relayable_transactions(std::vector<std::tuple<crypto::hash, cryptonote::blobdata, relay_method>>& txs);
|
||||
|
||||
/**
|
||||
* @brief tell the pool that certain transactions were just relayed
|
||||
*
|
||||
* @param hashes list of tx hashes that are about to be relayed
|
||||
* @param tx_relay update how the tx left this node
|
||||
*/
|
||||
void set_relayed(epee::span<const crypto::hash> hashes, relay_method tx_relay);
|
||||
|
||||
/**
|
||||
* @brief get the total number of transactions in the pool
|
||||
*
|
||||
* @return the number of transactions in the pool
|
||||
*/
|
||||
size_t get_transactions_count(bool include_sensitive = false) const;
|
||||
|
||||
/**
|
||||
* @brief get a string containing human-readable pool information
|
||||
*
|
||||
* @param short_format whether to use a shortened format for the info
|
||||
*
|
||||
* @return the string
|
||||
*/
|
||||
std::string print_pool(bool short_format) const;
|
||||
|
||||
/**
|
||||
* @brief remove transactions from the pool which are no longer valid
|
||||
*
|
||||
* With new versions of the currency, what conditions render a transaction
|
||||
* invalid may change. This function clears those which were received
|
||||
* before a version change and no longer conform to requirements.
|
||||
*
|
||||
* @param version the version the transactions must conform to
|
||||
*
|
||||
* @return the number of transactions removed
|
||||
*/
|
||||
size_t validate(uint8_t version);
|
||||
|
||||
/**
|
||||
* @brief return the cookie
|
||||
*
|
||||
* @return the cookie
|
||||
*/
|
||||
uint64_t cookie() const { return m_cookie; }
|
||||
|
||||
/**
|
||||
* @brief get the cumulative txpool weight in bytes
|
||||
*
|
||||
* @return the cumulative txpool weight in bytes
|
||||
*/
|
||||
size_t get_txpool_weight() const;
|
||||
|
||||
/**
|
||||
* @brief set the max cumulative txpool weight in bytes
|
||||
*
|
||||
* @param bytes the max cumulative txpool weight in bytes
|
||||
*/
|
||||
void set_txpool_max_weight(size_t bytes);
|
||||
|
||||
#define CURRENT_MEMPOOL_ARCHIVE_VER 11
|
||||
#define CURRENT_MEMPOOL_TX_DETAILS_ARCHIVE_VER 13
|
||||
|
||||
/**
|
||||
* @brief information about a single transaction
|
||||
*/
|
||||
struct tx_details
|
||||
{
|
||||
transaction tx; //!< the transaction
|
||||
size_t blob_size; //!< the transaction's size
|
||||
size_t weight; //!< the transaction's weight
|
||||
uint64_t fee; //!< the transaction's fee amount
|
||||
crypto::hash max_used_block_id; //!< the hash of the highest block referenced by an input
|
||||
uint64_t max_used_block_height; //!< the height of the highest block referenced by an input
|
||||
|
||||
//! whether or not the transaction has been in a block before
|
||||
/*! if the transaction was returned to the pool from the blockchain
|
||||
* due to a reorg, then this will be true
|
||||
*/
|
||||
bool kept_by_block;
|
||||
|
||||
//! the highest block the transaction referenced when last checking it failed
|
||||
/*! if verifying a transaction's inputs fails, it's possible this is due
|
||||
* to a reorg since it was created (if it used recently created outputs
|
||||
* as inputs).
|
||||
*/
|
||||
uint64_t last_failed_height;
|
||||
|
||||
//! the hash of the highest block the transaction referenced when last checking it failed
|
||||
/*! if verifying a transaction's inputs fails, it's possible this is due
|
||||
* to a reorg since it was created (if it used recently created outputs
|
||||
* as inputs).
|
||||
*/
|
||||
crypto::hash last_failed_id;
|
||||
|
||||
time_t receive_time; //!< the time when the transaction entered the pool
|
||||
|
||||
time_t last_relayed_time; //!< the last time the transaction was relayed to the network
|
||||
bool relayed; //!< whether or not the transaction has been relayed to the network
|
||||
bool do_not_relay; //!< to avoid relay this transaction to the network
|
||||
|
||||
bool double_spend_seen; //!< true iff another tx was seen double spending this one
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief get infornation about a single transaction
|
||||
*/
|
||||
bool get_transaction_info(const crypto::hash &txid, tx_details &td) const;
|
||||
|
||||
/**
|
||||
* @brief get transactions not in the passed set
|
||||
*/
|
||||
bool get_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes) const;
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief insert key images into m_spent_key_images
|
||||
*
|
||||
* @return true on success, false on error
|
||||
*/
|
||||
bool insert_key_images(const transaction_prefix &tx, const crypto::hash &txid, relay_method tx_relay);
|
||||
|
||||
/**
|
||||
* @brief remove old transactions from the pool
|
||||
*
|
||||
* After a certain time, it is assumed that a transaction which has not
|
||||
* yet been mined will likely not be mined. These transactions are removed
|
||||
* from the pool to avoid buildup.
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
bool remove_stuck_transactions();
|
||||
|
||||
/**
|
||||
* @brief check if a transaction in the pool has a given spent key image
|
||||
*
|
||||
* @param key_im the spent key image to look for
|
||||
* @param txid hash of the new transaction where `key_im` was seen.
|
||||
*
|
||||
* @return true if the spent key image is present, otherwise false
|
||||
*/
|
||||
bool have_tx_keyimg_as_spent(const crypto::key_image& key_im, const crypto::hash& txid) const;
|
||||
|
||||
/**
|
||||
* @brief check if any spent key image in a transaction is in the pool
|
||||
*
|
||||
* Checks if any of the spent key images in a given transaction are present
|
||||
* in any of the transactions in the transaction pool.
|
||||
*
|
||||
* @note see tx_pool::have_tx_keyimg_as_spent
|
||||
*
|
||||
* @param tx the transaction to check spent key images of
|
||||
* @param txid hash of `tx`.
|
||||
*
|
||||
* @return true if any spent key images are present in the pool, otherwise false
|
||||
*/
|
||||
bool have_tx_keyimges_as_spent(const transaction& tx, const crypto::hash& txid) const;
|
||||
|
||||
/**
|
||||
* @brief forget a transaction's spent key images
|
||||
*
|
||||
* Spent key images are stored separately from transactions for
|
||||
* convenience/speed, so this is part of the process of removing
|
||||
* a transaction from the pool.
|
||||
*
|
||||
* @param tx the transaction
|
||||
* @param txid the transaction's hash
|
||||
*
|
||||
* @return false if any key images to be removed cannot be found, otherwise true
|
||||
*/
|
||||
bool remove_transaction_keyimages(const transaction_prefix& tx, const crypto::hash &txid);
|
||||
|
||||
/**
|
||||
* @brief check if any of a transaction's spent key images are present in a given set
|
||||
*
|
||||
* @param kic the set of key images to check against
|
||||
* @param tx the transaction to check
|
||||
*
|
||||
* @return true if any key images present in the set, otherwise false
|
||||
*/
|
||||
static bool have_key_images(const std::unordered_set<crypto::key_image>& kic, const transaction_prefix& tx);
|
||||
|
||||
/**
|
||||
* @brief append the key images from a transaction to the given set
|
||||
*
|
||||
* @param kic the set of key images to append to
|
||||
* @param tx the transaction
|
||||
*
|
||||
* @return false if any append fails, otherwise true
|
||||
*/
|
||||
static bool append_key_images(std::unordered_set<crypto::key_image>& kic, const transaction_prefix& tx);
|
||||
|
||||
/**
|
||||
* @brief check if a transaction is a valid candidate for inclusion in a block
|
||||
*
|
||||
* @param txd the transaction to check (and info about it)
|
||||
* @param txid the txid of the transaction to check
|
||||
* @param txblob the transaction blob to check
|
||||
* @param tx the parsed transaction, if successful
|
||||
*
|
||||
* @return true if the transaction is good to go, otherwise false
|
||||
*/
|
||||
bool is_transaction_ready_to_go(txpool_tx_meta_t& txd, const crypto::hash &txid, const cryptonote::blobdata_ref &txblob, transaction&tx) const;
|
||||
bool is_transaction_ready_to_go(txpool_tx_meta_t& txd, const crypto::hash &txid, const cryptonote::blobdata &txblob, transaction&tx) const;
|
||||
|
||||
/**
|
||||
* @brief mark all transactions double spending the one passed
|
||||
*/
|
||||
void mark_double_spend(const transaction &tx);
|
||||
|
||||
/**
|
||||
* @brief prune lowest fee/byte txes till we're not above bytes
|
||||
*
|
||||
* if bytes is 0, use m_txpool_max_weight
|
||||
*/
|
||||
void prune(size_t bytes = 0);
|
||||
|
||||
//TODO: confirm the below comments and investigate whether or not this
|
||||
// is the desired behavior
|
||||
//! map key images to transactions which spent them
|
||||
/*! this seems odd, but it seems that multiple transactions can exist
|
||||
* in the pool which both have the same spent key. This would happen
|
||||
* in the event of a reorg where someone creates a new/different
|
||||
* transaction on the assumption that the original will not be in a
|
||||
* block again.
|
||||
*/
|
||||
typedef std::unordered_map<crypto::key_image, std::unordered_set<crypto::hash>> key_images_container;
|
||||
|
||||
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
|
||||
public:
|
||||
#endif
|
||||
mutable epee::critical_section m_transactions_lock; //!< lock for the pool
|
||||
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
|
||||
private:
|
||||
#endif
|
||||
|
||||
//! container for spent key images from the transactions in the pool
|
||||
key_images_container m_spent_key_images;
|
||||
|
||||
//TODO: this time should be a named constant somewhere, not hard-coded
|
||||
//! interval on which to check for stale/"stuck" transactions
|
||||
epee::math_helper::once_a_time_seconds<30> m_remove_stuck_tx_interval;
|
||||
|
||||
//TODO: look into doing this better
|
||||
//!< container for transactions organized by fee per size and receive time
|
||||
sorted_tx_container m_txs_by_fee_and_receive_time;
|
||||
|
||||
std::atomic<uint64_t> m_cookie; //!< incremented at each change
|
||||
|
||||
/**
|
||||
* @brief get an iterator to a transaction in the sorted container
|
||||
*
|
||||
* @param id the hash of the transaction to look for
|
||||
*
|
||||
* @return an iterator, possibly to the end of the container if not found
|
||||
*/
|
||||
sorted_tx_container::iterator find_tx_in_sorted_container(const crypto::hash& id) const;
|
||||
|
||||
//! cache/call Blockchain::check_tx_inputs results
|
||||
bool check_tx_inputs(const std::function<cryptonote::transaction&(void)> &get_tx, const crypto::hash &txid, uint64_t &max_used_block_height, crypto::hash &max_used_block_id, tx_verification_context &tvc, bool kept_by_block = false) const;
|
||||
|
||||
//! transactions which are unlikely to be included in blocks
|
||||
/*! These transactions are kept in RAM in case they *are* included
|
||||
* in a block eventually, but this container is not saved to disk.
|
||||
*/
|
||||
std::unordered_set<crypto::hash> m_timed_out_transactions;
|
||||
|
||||
Blockchain& m_blockchain; //!< reference to the Blockchain object
|
||||
|
||||
size_t m_txpool_max_weight;
|
||||
size_t m_txpool_weight;
|
||||
bool m_mine_stem_txes;
|
||||
|
||||
mutable std::unordered_map<crypto::hash, std::tuple<bool, tx_verification_context, uint64_t, crypto::hash>> m_input_cache;
|
||||
|
||||
std::unordered_map<crypto::hash, transaction> m_parsed_tx_cache;
|
||||
|
||||
//! Next timestamp that a DB check for relayable txes is allowed
|
||||
std::atomic<time_t> m_next_check;
|
||||
};
|
||||
}
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace serialization
|
||||
{
|
||||
template<class archive_t>
|
||||
void serialize(archive_t & ar, cryptonote::tx_memory_pool::tx_details& td, const unsigned int version)
|
||||
{
|
||||
ar & td.blob_size;
|
||||
ar & td.fee;
|
||||
ar & td.tx;
|
||||
ar & td.max_used_block_height;
|
||||
ar & td.max_used_block_id;
|
||||
ar & td.last_failed_height;
|
||||
ar & td.last_failed_id;
|
||||
ar & td.receive_time;
|
||||
ar & td.last_relayed_time;
|
||||
ar & td.relayed;
|
||||
if (version < 11)
|
||||
return;
|
||||
ar & td.kept_by_block;
|
||||
if (version < 12)
|
||||
return;
|
||||
ar & td.do_not_relay;
|
||||
if (version < 13)
|
||||
return;
|
||||
ar & td.weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
BOOST_CLASS_VERSION(cryptonote::tx_memory_pool, CURRENT_MEMPOOL_ARCHIVE_VER)
|
||||
BOOST_CLASS_VERSION(cryptonote::tx_memory_pool::tx_details, CURRENT_MEMPOOL_TX_DETAILS_ARCHIVE_VER)
|
||||
|
||||
|
||||
|
104
crypto/xmrcore/tx_sanity_check.cpp
Normal file
104
crypto/xmrcore/tx_sanity_check.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
// Copyright (c) 2019-2020, 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.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
#include "cryptonote_basic/cryptonote_basic.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "blockchain.h"
|
||||
#include "tx_sanity_check.h"
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "verify"
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
|
||||
bool tx_sanity_check(const cryptonote::blobdata &tx_blob, uint64_t rct_outs_available)
|
||||
{
|
||||
cryptonote::transaction tx;
|
||||
|
||||
if (!cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx))
|
||||
{
|
||||
MERROR("Failed to parse transaction");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cryptonote::is_coinbase(tx))
|
||||
{
|
||||
MERROR("Transaction is coinbase");
|
||||
return false;
|
||||
}
|
||||
std::set<uint64_t> rct_indices;
|
||||
size_t n_indices = 0;
|
||||
|
||||
for (const auto &txin : tx.vin)
|
||||
{
|
||||
if (txin.type() != typeid(cryptonote::txin_to_key))
|
||||
continue;
|
||||
const cryptonote::txin_to_key &in_to_key = boost::get<cryptonote::txin_to_key>(txin);
|
||||
if (in_to_key.amount != 0)
|
||||
continue;
|
||||
const std::vector<uint64_t> absolute = cryptonote::relative_output_offsets_to_absolute(in_to_key.key_offsets);
|
||||
for (uint64_t offset: absolute)
|
||||
rct_indices.insert(offset);
|
||||
n_indices += in_to_key.key_offsets.size();
|
||||
}
|
||||
|
||||
return tx_sanity_check(rct_indices, n_indices, rct_outs_available);
|
||||
}
|
||||
|
||||
bool tx_sanity_check(const std::set<uint64_t> &rct_indices, size_t n_indices, uint64_t rct_outs_available)
|
||||
{
|
||||
if (n_indices <= 10)
|
||||
{
|
||||
MDEBUG("n_indices is only " << n_indices << ", not checking");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (rct_outs_available < 10000)
|
||||
return true;
|
||||
|
||||
if (rct_indices.size() < n_indices * 8 / 10)
|
||||
{
|
||||
MERROR("amount of unique indices is too low (amount of rct indices is " << rct_indices.size() << ", out of total " << n_indices << "indices.");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint64_t> offsets(rct_indices.begin(), rct_indices.end());
|
||||
uint64_t median = epee::misc_utils::median(offsets);
|
||||
if (median < rct_outs_available * 6 / 10)
|
||||
{
|
||||
MERROR("median offset index is too low (median is " << median << " out of total " << rct_outs_available << "offsets). Transactions should contain a higher fraction of recent outputs.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
36
crypto/xmrcore/tx_sanity_check.h
Normal file
36
crypto/xmrcore/tx_sanity_check.h
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2019-2020, 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.
|
||||
|
||||
#include <set>
|
||||
#include "cryptonote_basic/blobdatatype.h"
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
bool tx_sanity_check(const cryptonote::blobdata &tx_blob, uint64_t rct_outs_available);
|
||||
bool tx_sanity_check(const std::set<uint64_t> &rct_indices, size_t n_indices, uint64_t rct_outs_available);
|
||||
}
|
Loading…
Reference in New Issue
Block a user