aboutsummaryrefslogtreecommitdiff
path: root/src/cryptonote_core
diff options
context:
space:
mode:
Diffstat (limited to 'src/cryptonote_core')
-rw-r--r--src/cryptonote_core/CMakeLists.txt3
-rw-r--r--src/cryptonote_core/blockchain.cpp575
-rw-r--r--src/cryptonote_core/blockchain.h50
-rw-r--r--src/cryptonote_core/blockchain_storage.cpp1952
-rw-r--r--src/cryptonote_core/blockchain_storage.h377
-rw-r--r--src/cryptonote_core/cryptonote_basic.h81
-rw-r--r--src/cryptonote_core/cryptonote_boost_serialization.h120
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp60
-rw-r--r--src/cryptonote_core/cryptonote_core.h21
-rw-r--r--src/cryptonote_core/cryptonote_format_utils.cpp251
-rw-r--r--src/cryptonote_core/cryptonote_format_utils.h13
-rw-r--r--src/cryptonote_core/tx_pool.cpp79
-rw-r--r--src/cryptonote_core/tx_pool.h19
-rw-r--r--src/cryptonote_core/verification_context.h1
14 files changed, 1020 insertions, 2582 deletions
diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt
index 205356797..4ebfd01e3 100644
--- a/src/cryptonote_core/CMakeLists.txt
+++ b/src/cryptonote_core/CMakeLists.txt
@@ -28,7 +28,6 @@
set(cryptonote_core_sources
account.cpp
- blockchain_storage.cpp
blockchain.cpp
checkpoints.cpp
cryptonote_basic_impl.cpp
@@ -44,7 +43,6 @@ set(cryptonote_core_headers)
set(cryptonote_core_private_headers
account.h
account_boost_serialization.h
- blockchain_storage.h
blockchain_storage_boost_serialization.h
blockchain.h
checkpoints.h
@@ -80,6 +78,7 @@ target_link_libraries(cryptonote_core
crypto
otshell_utils
blockchain_db
+ ringct
${Boost_DATE_TIME_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_SERIALIZATION_LIBRARY}
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index cc6b48b6b..ef72dac59 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -51,6 +51,7 @@
#include "crypto/hash.h"
#include "cryptonote_core/checkpoints.h"
#include "cryptonote_core/cryptonote_core.h"
+#include "ringct/rctSigs.h"
#if defined(PER_BLOCK_CHECKPOINT)
#include "blocks/blocks.h"
#endif
@@ -98,6 +99,10 @@ static const struct {
// version 2 starts from block 624634, which is on or around the 23rd of November, 2015. Fork time finalised on 2015-11-20. No fork voting occurs for the v2 fork.
{ 2, 624634, 0, 1445355000 },
+
+ { 3, 800500, 0, 1472415034 },
+ { 4, 801220, 0, 1472415035 },
+ { 5, 802660, 0, 1472415036 },
};
static const uint64_t testnet_hard_fork_version_1_till = 624633;
@@ -127,7 +132,7 @@ bool Blockchain::have_tx_keyimg_as_spent(const crypto::key_image &key_im) const
// and collects the public key for each from the transaction it was included in
// via the visitor passed to it.
template <class visitor_t>
-bool Blockchain::scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t &vis, const crypto::hash &tx_prefix_hash, uint64_t* pmax_related_block_height) const
+bool Blockchain::scan_outputkeys_for_indexes(size_t tx_version, const txin_to_key& tx_in_to_key, visitor_t &vis, const crypto::hash &tx_prefix_hash, uint64_t* pmax_related_block_height) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
@@ -207,7 +212,7 @@ bool Blockchain::scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, vi
output_index = m_db->get_output_key(tx_in_to_key.amount, i);
// call to the passed boost visitor to grab the public key for the output
- if (!vis.handle_output(output_index.unlock_time, output_index.pubkey))
+ if (!vis.handle_output(output_index.unlock_time, output_index.pubkey, output_index.commitment))
{
LOG_PRINT_L0("Failed to handle_output for output no = " << count << ", with absolute offset " << i);
return false;
@@ -958,7 +963,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
money_in_use += o.amount;
partial_block_reward = false;
- if (version >= 3) {
+ if (version == 3) {
for (auto &o: b.miner_tx.vout) {
if (!is_valid_decomposed_amount(o.amount)) {
LOG_PRINT_L1("miner tx output " << print_money(o.amount) << " is not a valid decomposed amount");
@@ -969,7 +974,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
std::vector<size_t> last_blocks_sizes;
get_last_n_blocks_sizes(last_blocks_sizes, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
- if (!get_block_reward(epee::misc_utils::median(last_blocks_sizes), cumulative_block_size, already_generated_coins, base_reward, get_current_hard_fork_version()))
+ if (!get_block_reward(epee::misc_utils::median(last_blocks_sizes), cumulative_block_size, already_generated_coins, base_reward, version))
{
LOG_PRINT_L1("block size " << cumulative_block_size << " is bigger than allowed for this blockchain");
return false;
@@ -1086,14 +1091,24 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
{
LOG_ERROR("Creating block template: error: invalid transaction size");
}
- uint64_t inputs_amount;
- if (!get_inputs_money_amount(cur_tx.tx, inputs_amount))
+ if (cur_tx.tx.version == 1)
{
- LOG_ERROR("Creating block template: error: cannot get inputs amount");
+ uint64_t inputs_amount;
+ if (!get_inputs_money_amount(cur_tx.tx, inputs_amount))
+ {
+ LOG_ERROR("Creating block template: error: cannot get inputs amount");
+ }
+ else if (cur_tx.fee != inputs_amount - get_outs_money_amount(cur_tx.tx))
+ {
+ LOG_ERROR("Creating block template: error: invalid fee");
+ }
}
- else if (cur_tx.fee != inputs_amount - get_outs_money_amount(cur_tx.tx))
+ else
{
- LOG_ERROR("Creating block template: error: invalid fee");
+ if (cur_tx.fee != cur_tx.tx.txnFee)
+ {
+ LOG_ERROR("Creating block template: error: invalid fee");
+ }
}
}
if (txs_size != real_txs_size)
@@ -1117,7 +1132,9 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
block size, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block size
*/
//make blocks coin-base tx looks close to real coinbase tx to get truthful blob size
- bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, 11, m_hardfork->get_current_version());
+ uint8_t hf_version = m_hardfork->get_current_version();
+ size_t max_outs = hf_version >= 4 ? 1 : 11;
+ bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version);
CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, first chance");
size_t cumulative_size = txs_size + get_object_blobsize(b.miner_tx);
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
@@ -1126,7 +1143,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
#endif
for (size_t try_count = 0; try_count != 10; ++try_count)
{
- r = construct_miner_tx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.miner_tx, ex_nonce, 11, m_hardfork->get_current_version());
+ r = construct_miner_tx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version);
CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, second chance");
size_t coinbase_blob_size = get_object_blobsize(b.miner_tx);
@@ -1597,6 +1614,139 @@ bool Blockchain::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUT
return true;
}
//------------------------------------------------------------------
+// This function adds the ringct output at index i to the list
+// unlocked and other such checks should be done by here.
+void Blockchain::add_out_to_get_rct_random_outs(std::list<COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry>& outs, uint64_t amount, size_t i) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry& oen = *outs.insert(outs.end(), COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry());
+ oen.amount = amount;
+ oen.global_amount_index = i;
+ output_data_t data = m_db->get_output_key(amount, i);
+ oen.out_key = data.pubkey;
+ oen.commitment = data.commitment;
+}
+//------------------------------------------------------------------
+// This function takes an RPC request for mixins and creates an RPC response
+// with the requested mixins.
+// TODO: figure out why this returns boolean / if we should be returning false
+// in some cases
+bool Blockchain::get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ // for each amount that we need to get mixins for, get <n> random outputs
+ // from BlockchainDB where <n> is req.outs_count (number of mixins).
+ auto num_outs = m_db->get_num_outputs(0);
+ // ensure we don't include outputs that aren't yet eligible to be used
+ // outpouts are sorted by height
+ while (num_outs > 0)
+ {
+ const tx_out_index toi = m_db->get_output_tx_and_index(0, num_outs - 1);
+ const uint64_t height = m_db->get_tx_block_height(toi.first);
+ if (height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE <= m_db->height())
+ break;
+ --num_outs;
+ }
+
+ std::unordered_set<uint64_t> seen_indices;
+
+ // if there aren't enough outputs to mix with (or just enough),
+ // use all of them. Eventually this should become impossible.
+ if (num_outs <= req.outs_count)
+ {
+ for (uint64_t i = 0; i < num_outs; i++)
+ {
+ // get tx_hash, tx_out_index from DB
+ tx_out_index toi = m_db->get_output_tx_and_index(0, i);
+
+ // if tx is unlocked, add output to result_outs
+ if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
+ {
+ add_out_to_get_rct_random_outs(res.outs, 0, i);
+ }
+ }
+ }
+ else
+ {
+ // while we still need more mixins
+ while (res.outs.size() < req.outs_count)
+ {
+ // if we've gone through every possible output, we've gotten all we can
+ if (seen_indices.size() == num_outs)
+ {
+ break;
+ }
+
+ // get a random output index from the DB. If we've already seen it,
+ // return to the top of the loop and try again, otherwise add it to the
+ // list of output indices we've seen.
+
+ // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
+ uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
+ double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
+ uint64_t i = (uint64_t)(frac*num_outs);
+ // just in case rounding up to 1 occurs after sqrt
+ if (i == num_outs)
+ --i;
+
+ if (seen_indices.count(i))
+ {
+ continue;
+ }
+ seen_indices.emplace(i);
+
+ // get tx_hash, tx_out_index from DB
+ tx_out_index toi = m_db->get_output_tx_and_index(0, i);
+
+ // if the output's transaction is unlocked, add the output's index to
+ // our list.
+ if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
+ {
+ add_out_to_get_rct_random_outs(res.outs, 0, i);
+ }
+ }
+ }
+
+ if (res.outs.size() < req.outs_count)
+ return false;
+#if 0
+ // if we do not have enough RCT inputs, we can pick from the non RCT ones
+ // which will have a zero mask
+ if (res.outs.size() < req.outs_count)
+ {
+ LOG_PRINT_L0("Out of RCT inputs (" << res.outs.size() << "/" << req.outs_count << "), using regular ones");
+
+ // TODO: arbitrary selection, needs better
+ COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req2 = AUTO_VAL_INIT(req2);
+ COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response res2 = AUTO_VAL_INIT(res2);
+ req2.outs_count = req.outs_count - res.outs.size();
+ static const uint64_t amounts[] = {1, 10, 20, 50, 100, 200, 500, 1000, 10000};
+ for (uint64_t a: amounts)
+ req2.amounts.push_back(a);
+ if (!get_random_outs_for_amounts(req2, res2))
+ return false;
+
+ // pick random ones from there
+ while (res.outs.size() < req.outs_count)
+ {
+ int list_idx = rand() % (sizeof(amounts)/sizeof(amounts[0]));
+ if (!res2.outs[list_idx].outs.empty())
+ {
+ const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry oe = res2.outs[list_idx].outs.back();
+ res2.outs[list_idx].outs.pop_back();
+ add_out_to_get_rct_random_outs(res.outs, res2.outs[list_idx].amount, oe.global_amount_index);
+ }
+ }
+ }
+#endif
+
+ return true;
+}
+//------------------------------------------------------------------
bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
@@ -1607,11 +1757,11 @@ bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_R
for (const auto &i: req.outputs)
{
// get tx_hash, tx_out_index from DB
- crypto::public_key key = m_db->get_output_key(i.amount, i.index).pubkey;
+ const output_data_t &od = m_db->get_output_key(i.amount, i.index);
tx_out_index toi = m_db->get_output_tx_and_index(i.amount, i.index);
bool unlocked = is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first));
- res.outs.push_back({key, unlocked});
+ res.outs.push_back({od.pubkey, od.commitment, unlocked});
}
return true;
}
@@ -2012,7 +2162,7 @@ bool Blockchain::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<u
// This function overloads its sister function with
// an extra value (hash of highest block that holds an output used as input)
// as a return-by-reference.
-bool Blockchain::check_tx_inputs(const transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block)
+bool Blockchain::check_tx_inputs(transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block)
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -2055,9 +2205,24 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
// from hard fork 2, we forbid dust and compound outputs
if (m_hardfork->get_current_version() >= 2) {
for (auto &o: tx.vout) {
- if (!is_valid_decomposed_amount(o.amount)) {
- tvc.m_invalid_output = true;
- return false;
+ if (tx.version == 1)
+ {
+ if (!is_valid_decomposed_amount(o.amount)) {
+ tvc.m_invalid_output = true;
+ return false;
+ }
+ }
+ }
+ }
+
+ // in a v2 tx, all outputs must have 0 amount
+ if (m_hardfork->get_current_version() >= 3) {
+ if (tx.version >= 2) {
+ for (auto &o: tx.vout) {
+ if (o.amount != 0) {
+ tvc.m_invalid_output = true;
+ return false;
+ }
}
}
}
@@ -2076,13 +2241,83 @@ bool Blockchain::have_tx_keyimges_as_spent(const transaction &tx) const
}
return false;
}
+bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_prefix_hash, const std::vector<std::vector<rct::ctkey>> &pubkeys)
+{
+ CHECK_AND_ASSERT_MES(tx.version == 2, false, "Transaction version is not 2");
+
+ rct::rctSig &rv = tx.rct_signatures;
+
+ // message - hash of the transaction prefix
+ rv.message = rct::hash2rct(tx_prefix_hash);
+
+ // mixRing - full and simple store it in opposite ways
+ if (rv.type == rct::RCTTypeFull)
+ {
+ rv.mixRing.resize(pubkeys[0].size());
+ for (size_t m = 0; m < pubkeys[0].size(); ++m)
+ rv.mixRing[m].clear();
+ for (size_t n = 0; n < pubkeys.size(); ++n)
+ {
+ CHECK_AND_ASSERT_MES(pubkeys[n].size() <= pubkeys[0].size(), false, "More inputs that first ring");
+ for (size_t m = 0; m < pubkeys[n].size(); ++m)
+ {
+ rv.mixRing[m].push_back(pubkeys[n][m]);
+ }
+ }
+ }
+ else if (rv.type == rct::RCTTypeSimple)
+ {
+ rv.mixRing.resize(pubkeys.size());
+ for (size_t n = 0; n < pubkeys.size(); ++n)
+ {
+ rv.mixRing[n].clear();
+ for (size_t m = 0; m < pubkeys[n].size(); ++m)
+ {
+ rv.mixRing[n].push_back(pubkeys[n][m]);
+ }
+ }
+ }
+ else
+ {
+ CHECK_AND_ASSERT_MES(false, false, "Unsupported rct tx type: " + boost::lexical_cast<std::string>(rv.type));
+ }
+
+ // II
+ if (rv.type == rct::RCTTypeFull)
+ {
+ rv.p.MGs.resize(1);
+ rv.p.MGs[0].II.resize(tx.vin.size());
+ for (size_t n = 0; n < tx.vin.size(); ++n)
+ rv.p.MGs[0].II[n] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image);
+ }
+ else if (rv.type == rct::RCTTypeSimple)
+ {
+ CHECK_AND_ASSERT_MES(rv.p.MGs.size() == tx.vin.size(), false, "Bad MGs size");
+ for (size_t n = 0; n < tx.vin.size(); ++n)
+ {
+ rv.p.MGs[n].II.resize(1);
+ rv.p.MGs[n].II[0] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image);
+ }
+ }
+ else
+ {
+ CHECK_AND_ASSERT_MES(false, false, "Unsupported rct tx type: " + boost::lexical_cast<std::string>(rv.type));
+ }
+
+ // outPk
+ CHECK_AND_ASSERT_MES(rv.outPk.size() == tx.vout.size(), false, "Bad outPk size");
+ for (size_t n = 0; n < tx.rct_signatures.outPk.size(); ++n)
+ rv.outPk[n].dest = rct::pk2rct(boost::get<txout_to_key>(tx.vout[n].target).key);
+
+ return true;
+}
//------------------------------------------------------------------
// This function validates transaction inputs and their keys.
// FIXME: consider moving functionality specific to one input into
// check_tx_input() rather than here, and use this function simply
// to iterate the inputs as necessary (splitting the task
// using threads, etc.)
-bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height)
+bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height)
{
LOG_PRINT_L3("Blockchain::" << __func__);
size_t sig_index = 0;
@@ -2091,30 +2326,44 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx);
+ const uint8_t hf_version = m_hardfork->get_current_version();
+
// from hard fork 2, we require mixin at least 2 unless one output cannot mix with 2 others
// if one output cannot mix with 2 others, we accept at most 1 output that can mix
- if (m_hardfork->get_current_version() >= 2)
+ if (hf_version >= 2)
{
size_t n_unmixable = 0, n_mixable = 0;
size_t mixin = std::numeric_limits<size_t>::max();
+ const size_t min_mixin = hf_version >= 5 ? 4 : 2;
for (const auto& txin : tx.vin)
{
// non txin_to_key inputs will be rejected below
if (txin.type() == typeid(txin_to_key))
{
const txin_to_key& in_to_key = boost::get<txin_to_key>(txin);
- uint64_t n_outputs = m_db->get_num_outputs(in_to_key.amount);
- LOG_PRINT_L2("output size " << print_money(in_to_key.amount) << ": " << n_outputs << " available");
- // n_outputs includes the output we're considering
- if (n_outputs <= 2)
- ++n_unmixable;
- else
+ if (in_to_key.amount == 0)
+ {
+ // always consider rct inputs mixable. Even if there's not enough rct
+ // inputs on the chain to mix with, this is going to be the case for
+ // just a few blocks right after the fork at most
++n_mixable;
+ }
+ else
+ {
+ uint64_t n_outputs = m_db->get_num_outputs(in_to_key.amount);
+ LOG_PRINT_L2("output size " << print_money(in_to_key.amount) << ": " << n_outputs << " available");
+ // n_outputs includes the output we're considering
+ if (n_outputs <= min_mixin)
+ ++n_unmixable;
+ else
+ ++n_mixable;
+ }
if (in_to_key.key_offsets.size() - 1 < mixin)
mixin = in_to_key.key_offsets.size() - 1;
}
}
- if (mixin < 2)
+
+ if (mixin < min_mixin)
{
if (n_unmixable == 0)
{
@@ -2129,6 +2378,22 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
return false;
}
}
+
+ // min/max tx version based on HF, and we accept v1 txes if having a non mixable
+ const size_t max_tx_version = (hf_version <= 3) ? 1 : 2;
+ if (tx.version > max_tx_version)
+ {
+ LOG_PRINT_L1("transaction version " << (unsigned)tx.version << " is higher than max accepted version " << max_tx_version);
+ tvc.m_verifivation_failed = true;
+ return false;
+ }
+ const size_t min_tx_version = (n_unmixable > 0 ? 1 : (hf_version >= 5) ? 2 : 1);
+ if (tx.version < min_tx_version)
+ {
+ LOG_PRINT_L1("transaction version " << (unsigned)tx.version << " is lower than min accepted version " << min_tx_version);
+ tvc.m_verifivation_failed = true;
+ return false;
+ }
}
auto it = m_check_txin_table.find(tx_prefix_hash);
@@ -2140,7 +2405,7 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
}
uint64_t t_t1 = 0;
- std::vector<std::vector<crypto::public_key>> pubkeys(tx.vin.size());
+ std::vector<std::vector<rct::ctkey>> pubkeys(tx.vin.size());
std::vector < uint64_t > results;
results.resize(tx.vin.size(), 0);
@@ -2189,28 +2454,31 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
return false;
}
- // basically, make sure number of inputs == number of signatures
- CHECK_AND_ASSERT_MES(sig_index < tx.signatures.size(), false, "wrong transaction: not signature entry for input with index= " << sig_index);
+ if (tx.version == 1)
+ {
+ // basically, make sure number of inputs == number of signatures
+ CHECK_AND_ASSERT_MES(sig_index < tx.signatures.size(), false, "wrong transaction: not signature entry for input with index= " << sig_index);
#if defined(CACHE_VIN_RESULTS)
- auto itk = it->second.find(in_to_key.k_image);
- if(itk != it->second.end())
- {
- if(!itk->second)
+ auto itk = it->second.find(in_to_key.k_image);
+ if(itk != it->second.end())
{
- LOG_PRINT_L1("Failed ring signature for tx " << get_transaction_hash(tx) << " vin key with k_image: " << in_to_key.k_image << " sig_index: " << sig_index);
- return false;
- }
+ if(!itk->second)
+ {
+ LOG_PRINT_L1("Failed ring signature for tx " << get_transaction_hash(tx) << " vin key with k_image: " << in_to_key.k_image << " sig_index: " << sig_index);
+ return false;
+ }
- // txin has been verified already, skip
- sig_index++;
- continue;
- }
+ // txin has been verified already, skip
+ sig_index++;
+ continue;
+ }
#endif
+ }
// make sure that output being spent matches up correctly with the
// signature spending it.
- if (!check_tx_input(in_to_key, tx_prefix_hash, tx.signatures[sig_index], pubkeys[sig_index], pmax_used_block_height))
+ if (!check_tx_input(tx.version, in_to_key, tx_prefix_hash, tx.version == 1 ? tx.signatures[sig_index] : std::vector<crypto::signature>(), tx.rct_signatures, pubkeys[sig_index], pmax_used_block_height))
{
it->second[in_to_key.k_image] = false;
LOG_PRINT_L1("Failed to check ring signature for tx " << get_transaction_hash(tx) << " vin key with k_image: " << in_to_key.k_image << " sig_index: " << sig_index);
@@ -2222,28 +2490,31 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
return false;
}
- if (threads > 1)
+ if (tx.version == 1)
{
- // ND: Speedup
- // 1. Thread ring signature verification if possible.
- ioservice.dispatch(boost::bind(&Blockchain::check_ring_signature, this, std::cref(tx_prefix_hash), std::cref(in_to_key.k_image), std::cref(pubkeys[sig_index]), std::cref(tx.signatures[sig_index]), std::ref(results[sig_index])));
- }
- else
- {
- check_ring_signature(tx_prefix_hash, in_to_key.k_image, pubkeys[sig_index], tx.signatures[sig_index], results[sig_index]);
- if (!results[sig_index])
+ if (threads > 1)
{
- it->second[in_to_key.k_image] = false;
- LOG_PRINT_L1("Failed to check ring signature for tx " << get_transaction_hash(tx) << " vin key with k_image: " << in_to_key.k_image << " sig_index: " << sig_index);
-
- if (pmax_used_block_height) // a default value of NULL is used when called from Blockchain::handle_block_to_main_chain()
+ // ND: Speedup
+ // 1. Thread ring signature verification if possible.
+ ioservice.dispatch(boost::bind(&Blockchain::check_ring_signature, this, std::cref(tx_prefix_hash), std::cref(in_to_key.k_image), std::cref(pubkeys[sig_index]), std::cref(tx.signatures[sig_index]), std::ref(results[sig_index])));
+ }
+ else
+ {
+ check_ring_signature(tx_prefix_hash, in_to_key.k_image, pubkeys[sig_index], tx.signatures[sig_index], results[sig_index]);
+ if (!results[sig_index])
{
- LOG_PRINT_L1("*pmax_used_block_height: " << *pmax_used_block_height);
- }
+ it->second[in_to_key.k_image] = false;
+ LOG_PRINT_L1("Failed to check ring signature for tx " << get_transaction_hash(tx) << " vin key with k_image: " << in_to_key.k_image << " sig_index: " << sig_index);
- return false;
+ if (pmax_used_block_height) // a default value of NULL is used when called from Blockchain::handle_block_to_main_chain()
+ {
+ LOG_PRINT_L1("*pmax_used_block_height: " << *pmax_used_block_height);
+ }
+
+ return false;
+ }
+ it->second[in_to_key.k_image] = true;
}
- it->second[in_to_key.k_image] = true;
}
sig_index++;
@@ -2251,30 +2522,170 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
KILL_IOSERVICE();
- if (threads > 1)
+ if (tx.version == 1)
+ {
+ if (threads > 1)
+ {
+ // save results to table, passed or otherwise
+ bool failed = false;
+ for (size_t i = 0; i < tx.vin.size(); i++)
+ {
+ const txin_to_key& in_to_key = boost::get<txin_to_key>(tx.vin[i]);
+ it->second[in_to_key.k_image] = results[i];
+ if(!failed && !results[i])
+ failed = true;
+ }
+
+ if (failed)
+ {
+ LOG_PRINT_L1("Failed to check ring signatures!, t_loop: " << t_t1);
+ return false;
+ }
+ }
+ }
+ else
{
- // save results to table, passed or otherwise
- bool failed = false;
- for (size_t i = 0; i < tx.vin.size(); i++)
+ if (!expand_transaction_2(tx, tx_prefix_hash, pubkeys))
{
- const txin_to_key& in_to_key = boost::get<txin_to_key>(tx.vin[i]);
- it->second[in_to_key.k_image] = results[i];
- if(!failed && !results[i])
- failed = true;
+ LOG_PRINT_L1("Failed to expand rct signatures!");
+ return false;
}
- if (failed)
+ // from version 2, check ringct signatures
+ // obviously, the original and simple rct APIs use a mixRing that's indexes
+ // in opposite orders, because it'd be too simple otherwise...
+ const rct::rctSig &rv = tx.rct_signatures;
+ switch (rv.type)
{
- LOG_PRINT_L1("Failed to check ring signatures!, t_loop: " << t_t1);
+ case rct::RCTTypeNull: {
+ // we only accept no signatures for coinbase txes
+ LOG_PRINT_L1("Null rct signature on non-coinbase tx");
+ return false;
+ }
+ case rct::RCTTypeSimple: {
+ // check all this, either recontructed (so should really pass), or not
+ {
+ if (pubkeys.size() != rv.mixRing.size())
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size");
+ return false;
+ }
+ for (size_t i = 0; i < pubkeys.size(); ++i)
+ {
+ if (pubkeys[i].size() != rv.mixRing[i].size())
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size");
+ return false;
+ }
+ }
+
+ for (size_t n = 0; n < pubkeys.size(); ++n)
+ {
+ for (size_t m = 0; m < pubkeys[n].size(); ++m)
+ {
+ if (pubkeys[n][m].dest != rct::rct2pk(rv.mixRing[n][m].dest))
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkey at vin " << n << ", index " << m);
+ return false;
+ }
+ if (pubkeys[n][m].mask != rct::rct2pk(rv.mixRing[n][m].mask))
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m);
+ return false;
+ }
+ }
+ }
+ }
+
+ if (rv.p.MGs.size() != tx.vin.size())
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures: mismatched MGs/vin sizes");
+ return false;
+ }
+ for (size_t n = 0; n < tx.vin.size(); ++n)
+ {
+ if (memcmp(&boost::get<txin_to_key>(tx.vin[n]).k_image, &rv.p.MGs[n].II[0], 32))
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures: mismatched key image");
+ return false;
+ }
+ }
+
+ if (!rct::verRctSimple(rv))
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures!");
+ return false;
+ }
+ break;
+ }
+ case rct::RCTTypeFull: {
+ // check all this, either recontructed (so should really pass), or not
+ {
+ bool size_matches = true;
+ for (size_t i = 0; i < pubkeys.size(); ++i)
+ size_matches &= pubkeys[i].size() == rv.mixRing.size();
+ for (size_t i = 0; i < rv.mixRing.size(); ++i)
+ size_matches &= pubkeys.size() == rv.mixRing[i].size();
+ if (!size_matches)
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size");
+ return false;
+ }
+
+ for (size_t n = 0; n < pubkeys.size(); ++n)
+ {
+ for (size_t m = 0; m < pubkeys[n].size(); ++m)
+ {
+ if (pubkeys[n][m].dest != rct::rct2pk(rv.mixRing[m][n].dest))
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkey at vin " << n << ", index " << m);
+ return false;
+ }
+ if (pubkeys[n][m].mask != rct::rct2pk(rv.mixRing[m][n].mask))
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m);
+ return false;
+ }
+ }
+ }
+ }
+
+ if (rv.p.MGs.size() != 1)
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures: Bad MGs size");
+ return false;
+ }
+ if (rv.p.MGs[0].II.size() != tx.vin.size())
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes");
+ return false;
+ }
+ for (size_t n = 0; n < tx.vin.size(); ++n)
+ {
+ if (memcmp(&boost::get<txin_to_key>(tx.vin[n]).k_image, &rv.p.MGs[0].II[n], 32))
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes");
+ return false;
+ }
+ }
+
+ if (!rct::verRct(rv))
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures!");
+ return false;
+ }
+ break;
+ }
+ default:
+ LOG_PRINT_L1("Unsupported rct type: " << rv.type);
return false;
}
}
- LOG_PRINT_L1("t_loop: " << t_t1);
return true;
}
//------------------------------------------------------------------
-void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const crypto::key_image &key_image, const std::vector<crypto::public_key> &pubkeys, const std::vector<crypto::signature>& sig, uint64_t &result)
+void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const crypto::key_image &key_image, const std::vector<rct::ctkey> &pubkeys, const std::vector<crypto::signature>& sig, uint64_t &result)
{
if (m_is_in_checkpoint_zone)
{
@@ -2285,7 +2696,8 @@ void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const
std::vector<const crypto::public_key *> p_output_keys;
for (auto &key : pubkeys)
{
- p_output_keys.push_back(&key);
+ // rct::key and crypto::public_key have the same structure, avoid object ctor/memcpy
+ p_output_keys.push_back(&(const crypto::public_key&)key.dest);
}
result = crypto::check_ring_signature(tx_prefix_hash, key_image, p_output_keys, sig.data()) ? 1 : 0;
@@ -2321,7 +2733,7 @@ bool Blockchain::is_tx_spendtime_unlocked(uint64_t unlock_time) const
// This function locates all outputs associated with a given input (mixins)
// and validates that they exist and are usable. It also checks the ring
// signature for each input.
-bool Blockchain::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, std::vector<crypto::public_key> &output_keys, uint64_t* pmax_related_block_height)
+bool Blockchain::check_tx_input(size_t tx_version, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, const rct::rctSig &rct_signatures, std::vector<rct::ctkey> &output_keys, uint64_t* pmax_related_block_height)
{
LOG_PRINT_L3("Blockchain::" << __func__);
@@ -2331,13 +2743,13 @@ bool Blockchain::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_
struct outputs_visitor
{
- std::vector<crypto::public_key >& m_output_keys;
+ std::vector<rct::ctkey >& m_output_keys;
const Blockchain& m_bch;
- outputs_visitor(std::vector<crypto::public_key>& output_keys, const Blockchain& bch) :
+ outputs_visitor(std::vector<rct::ctkey>& output_keys, const Blockchain& bch) :
m_output_keys(output_keys), m_bch(bch)
{
}
- bool handle_output(uint64_t unlock_time, const crypto::public_key &pubkey)
+ bool handle_output(uint64_t unlock_time, const crypto::public_key &pubkey, const rct::key &commitment)
{
//check tx unlock time
if (!m_bch.is_tx_spendtime_unlocked(unlock_time))
@@ -2351,7 +2763,7 @@ bool Blockchain::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_
// but only txout_to_key outputs are stored in the DB in the first place, done in
// Blockchain*::add_output
- m_output_keys.push_back(pubkey);
+ m_output_keys.push_back(rct::ctkey({rct::pk2rct(pubkey), commitment}));
return true;
}
};
@@ -2360,7 +2772,7 @@ bool Blockchain::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_
// collect output keys
outputs_visitor vi(output_keys, *this);
- if (!scan_outputkeys_for_indexes(txin, vi, tx_prefix_hash, pmax_related_block_height))
+ if (!scan_outputkeys_for_indexes(tx_version, txin, vi, tx_prefix_hash, pmax_related_block_height))
{
LOG_PRINT_L1("Failed to get output keys for tx with amount = " << print_money(txin.amount) << " and count indexes " << txin.key_offsets.size());
return false;
@@ -2371,7 +2783,10 @@ bool Blockchain::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_
LOG_PRINT_L1("Output keys for tx with amount = " << txin.amount << " and count indexes " << txin.key_offsets.size() << " returned wrong keys count " << output_keys.size());
return false;
}
- CHECK_AND_ASSERT_MES(sig.size() == output_keys.size(), false, "internal error: tx signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size());
+ if (tx_version == 1) {
+ CHECK_AND_ASSERT_MES(sig.size() == output_keys.size(), false, "internal error: tx signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size());
+ }
+ // rct_signatures will be expanded after this
return true;
}
//------------------------------------------------------------------
@@ -2453,6 +2868,8 @@ void Blockchain::return_tx_to_pool(const std::vector<transaction> &txs)
//------------------------------------------------------------------
bool Blockchain::flush_txes_from_pool(const std::list<crypto::hash> &txids)
{
+ CRITICAL_REGION_LOCAL(m_tx_pool);
+
bool res = true;
for (const auto &txid: txids)
{
@@ -2461,7 +2878,7 @@ bool Blockchain::flush_txes_from_pool(const std::list<crypto::hash> &txids)
uint64_t fee;
bool relayed;
LOG_PRINT_L1("Removing txid " << txid << " from the pool");
- if(!m_tx_pool.take_tx(txid, tx, blob_size, fee, relayed))
+ if(m_tx_pool.have_tx(txid) && !m_tx_pool.take_tx(txid, tx, blob_size, fee, relayed))
{
LOG_PRINT_L0("Failed to remove txid " << txid << " from the pool");
res = false;
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index 1524693b8..94701608e 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -455,6 +455,23 @@ namespace cryptonote
bool get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res) const;
/**
+ * @brief gets random ringct outputs to mix with
+ *
+ * This function takes an RPC request for outputs to mix with
+ * and creates an RPC response with the resultant output indices
+ * and the matching keys.
+ *
+ * Outputs to mix with are randomly selected from the utxo set
+ * for each output amount in the request.
+ *
+ * @param req the output amounts and number of mixins to select
+ * @param res return-by-reference the resultant output indices
+ *
+ * @return true
+ */
+ bool get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const;
+
+ /**
* @brief gets the global indices for outputs from a given transaction
*
* This function gets the global indices for all outputs belonging
@@ -483,6 +500,7 @@ namespace cryptonote
* validates a transaction's inputs as correctly used and not previously
* spent. also returns the hash and height of the most recent block
* which contains an output that was used as an input to the transaction.
+ * The transaction's rct signatures, if any, are expanded.
*
* @param tx the transaction to validate
* @param pmax_used_block_height return-by-reference block height of most recent input
@@ -492,7 +510,7 @@ namespace cryptonote
*
* @return false if any input is invalid, otherwise true
*/
- bool check_tx_inputs(const transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block = false);
+ bool check_tx_inputs(transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block = false);
/**
* @brief check that a transaction's outputs conform to current standards
@@ -867,11 +885,12 @@ namespace cryptonote
* @param vis an instance of the visitor to use
* @param tx_prefix_hash the hash of the associated transaction_prefix
* @param pmax_related_block_height return-by-pointer the height of the most recent block in the input set
+ * @param tx_version version of the tx, if > 1 we also get commitments
*
* @return false if any keys are not found or any inputs are not unlocked, otherwise true
*/
template<class visitor_t>
- inline bool scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t &vis, const crypto::hash &tx_prefix_hash, uint64_t* pmax_related_block_height = NULL) const;
+ inline bool scan_outputkeys_for_indexes(size_t tx_version, const txin_to_key& tx_in_to_key, visitor_t &vis, const crypto::hash &tx_prefix_hash, uint64_t* pmax_related_block_height = NULL) const;
/**
* @brief collect output public keys of a transaction input set
@@ -883,15 +902,17 @@ namespace cryptonote
* If pmax_related_block_height is not NULL, its value is set to the height
* of the most recent block which contains an output used in the input set
*
+ * @param tx_version the transaction version
* @param txin the transaction input
* @param tx_prefix_hash the transaction prefix hash, for caching organization
* @param sig the input signature
* @param output_keys return-by-reference the public keys of the outputs in the input set
+ * @param rct_signatures the ringCT signatures, which are only valid if tx version > 1
* @param pmax_related_block_height return-by-pointer the height of the most recent block in the input set
*
* @return false if any output is not yet unlocked, or is missing, otherwise true
*/
- bool check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, std::vector<crypto::public_key> &output_keys, uint64_t* pmax_related_block_height);
+ bool check_tx_input(size_t tx_version,const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, const rct::rctSig &rct_signatures, std::vector<rct::ctkey> &output_keys, uint64_t* pmax_related_block_height);
/**
* @brief validate a transaction's inputs and their keys
@@ -899,6 +920,7 @@ namespace cryptonote
* This function validates transaction inputs and their keys. Previously
* it also performed double spend checking, but that has been moved to its
* own function.
+ * The transaction's rct signatures, if any, are expanded.
*
* If pmax_related_block_height is not NULL, its value is set to the height
* of the most recent block which contains an output used in any input set
@@ -912,7 +934,7 @@ namespace cryptonote
*
* @return false if any validation step fails, otherwise true
*/
- bool check_tx_inputs(const transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height = NULL);
+ bool check_tx_inputs(transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height = NULL);
/**
* @brief performs a blockchain reorganization according to the longest chain rule
@@ -1054,6 +1076,15 @@ namespace cryptonote
void add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) const;
/**
+ * @brief adds the given output to the requested set of random ringct outputs
+ *
+ * @param outs return-by-reference the set the output is to be added to
+ * @param amount the output amount (0 for rct inputs)
+ * @param i the rct output index
+ */
+ void add_out_to_get_rct_random_outs(std::list<COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry>& outs, uint64_t amount, size_t i) const;
+
+ /**
* @brief checks if a transaction is unlocked (its outputs spendable)
*
* This function checks to see if a transaction is unlocked.
@@ -1172,7 +1203,7 @@ namespace cryptonote
* @param result false if the ring signature is invalid, otherwise true
*/
void check_ring_signature(const crypto::hash &tx_prefix_hash, const crypto::key_image &key_image,
- const std::vector<crypto::public_key> &pubkeys, const std::vector<crypto::signature> &sig, uint64_t &result);
+ const std::vector<rct::ctkey> &pubkeys, const std::vector<crypto::signature> &sig, uint64_t &result);
/**
* @brief loads block hashes from compiled-in data set
@@ -1182,5 +1213,14 @@ namespace cryptonote
* a useful state.
*/
void load_compiled_in_block_hashes();
+
+ /**
+ * @brief expands v2 transaction data from blockchain
+ *
+ * RingCT transactions do not transmit some of their data if it
+ * can be reconstituted by the receiver. This function expands
+ * that implicit data.
+ */
+ bool expand_transaction_2(transaction &tx, const crypto::hash &tx_prefix_hash, const std::vector<std::vector<rct::ctkey>> &pubkeys);
};
} // namespace cryptonote
diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp
deleted file mode 100644
index a829b7cbe..000000000
--- a/src/cryptonote_core/blockchain_storage.cpp
+++ /dev/null
@@ -1,1952 +0,0 @@
-// Copyright (c) 2014-2016, The Monero Project
-//
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without modification, are
-// permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this list of
-// conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice, this list
-// of conditions and the following disclaimer in the documentation and/or other
-// materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its contributors may be
-// used to endorse or promote products derived from this software without specific
-// prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
-// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
-// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
-// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
-
-#include <algorithm>
-#include <cstdio>
-#include <cmath>
-#include <boost/archive/binary_oarchive.hpp>
-#include <boost/archive/binary_iarchive.hpp>
-
-#include "include_base_utils.h"
-#include "cryptonote_basic_impl.h"
-#include "blockchain_storage.h"
-#include "cryptonote_format_utils.h"
-#include "cryptonote_boost_serialization.h"
-#include "blockchain_storage_boost_serialization.h"
-#include "cryptonote_config.h"
-#include "miner.h"
-#include "misc_language.h"
-#include "profile_tools.h"
-#include "file_io_utils.h"
-#include "common/boost_serialization_helper.h"
-#include "warnings.h"
-#include "crypto/hash.h"
-#include "cryptonote_core/checkpoints.h"
-//#include "serialization/json_archive.h"
-#include "../../contrib/otshell_utils/utils.hpp"
-#include "../../src/p2p/data_logger.hpp"
-
-using namespace cryptonote;
-
-DISABLE_VS_WARNINGS(4267)
-
-//------------------------------------------------------------------
-bool blockchain_storage::have_tx(const crypto::hash &id) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- return m_transactions.find(id) != m_transactions.end();
-}
-//------------------------------------------------------------------
-bool blockchain_storage::have_tx_keyimg_as_spent(const crypto::key_image &key_im) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- return m_spent_keys.find(key_im) != m_spent_keys.end();
-}
-//------------------------------------------------------------------
-const transaction *blockchain_storage::get_tx(const crypto::hash &id) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- auto it = m_transactions.find(id);
- if (it == m_transactions.end())
- return NULL;
-
- return &it->second.tx;
-}
-//------------------------------------------------------------------
-uint64_t blockchain_storage::get_current_blockchain_height() const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- return m_blocks.size();
-}
-//------------------------------------------------------------------
-bool blockchain_storage::init(const std::string& config_folder, bool testnet)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- m_config_folder = config_folder;
- m_testnet = testnet;
- LOG_PRINT_L0("Loading blockchain...");
- const std::string filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_FILENAME;
- if(tools::unserialize_obj_from_file(*this, filename))
- {
-
- // checkpoints
-
- // mainchain
- for (size_t height=0; height < m_blocks.size(); ++height)
- {
- CHECK_AND_ASSERT_MES((!m_checkpoints.is_in_checkpoint_zone(height)) || m_checkpoints.check_block(height,get_block_hash(m_blocks[height].bl)),false,"checkpoint fail, blockchain.bin invalid");
- }
-
- // check alt chains
- #if 0
- // doesn't work when a checkpoint is added and there are already alt chains. However, the rest of the blockchain code suffers from the same issue, so ignore for now
- // see issue #118
- BOOST_FOREACH(blocks_ext_by_hash::value_type& alt_block, m_alternative_chains)
- {
- CHECK_AND_ASSERT_MES(m_checkpoints.is_alternative_block_allowed(m_blocks.size()-1,alt_block.second.height),false,"stored alternative block not allowed, blockchain.bin invalid");
- }
- #endif
- }
- else
- {
- LOG_PRINT_L0("Can't load blockchain storage from file, generating genesis block.");
- if (!store_genesis_block(testnet, true))
- return false;
- }
- if(!m_blocks.size())
- {
- LOG_PRINT_L0("Blockchain not loaded, generating genesis block.");
-
- if (!store_genesis_block(testnet, false)) {
- return false;
- }
- } else {
- cryptonote::block b;
-
- if (testnet)
- {
- generate_genesis_block(b, config::testnet::GENESIS_TX, config::testnet::GENESIS_NONCE);
- }
- else
- {
- generate_genesis_block(b, config::GENESIS_TX, config::GENESIS_NONCE);
- }
-
- crypto::hash genesis_hash = get_block_hash(m_blocks[0].bl);
- crypto::hash testnet_genesis_hash = get_block_hash(b);
- if (genesis_hash != testnet_genesis_hash) {
- LOG_ERROR("Failed to init: genesis block mismatch. Probably you set --testnet flag with data dir with non-test blockchain or another network.");
- return false;
- }
- }
- uint64_t timestamp_diff = time(NULL) - m_blocks.back().bl.timestamp;
- if(!m_blocks.back().bl.timestamp)
- timestamp_diff = time(NULL) - 1341378000;
- LOG_PRINT_GREEN("Blockchain initialized. last block: " << m_blocks.size() - 1 << ", " << epee::misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block(), LOG_LEVEL_0);
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::store_genesis_block(bool testnet, bool check_added) {
- block bl = ::boost::value_initialized<block>();
- block_verification_context bvc = boost::value_initialized<block_verification_context>();
-
- if (testnet)
- {
- generate_genesis_block(bl, config::testnet::GENESIS_TX, config::testnet::GENESIS_NONCE);
- }
- else
- {
- generate_genesis_block(bl, config::GENESIS_TX, config::GENESIS_NONCE);
- }
-
- add_new_block(bl, bvc);
- CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed && (bvc.m_added_to_main_chain || !check_added), false, "Failed to add genesis block to blockchain");
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::store_blockchain()
-{
- m_is_blockchain_storing = true;
- epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_is_blockchain_storing=false;});
-
- LOG_PRINT_L0("Storing blockchain...");
- if (!tools::create_directories_if_necessary(m_config_folder))
- {
- LOG_PRINT_L0("Failed to create data directory: " << m_config_folder);
- return false;
- }
-
- const std::string temp_filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_TEMP_FILENAME;
- // There is a chance that temp_filename and filename are hardlinks to the same file
- std::remove(temp_filename.c_str());
- if(!tools::serialize_obj_to_file(*this, temp_filename))
- {
- //achtung!
- LOG_ERROR("Failed to save blockchain data to file: " << temp_filename);
- return false;
- }
- const std::string filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_FILENAME;
- std::error_code ec = tools::replace_file(temp_filename, filename);
- if (ec)
- {
- LOG_ERROR("Failed to rename blockchain data file " << temp_filename << " to " << filename << ": " << ec.message() << ':' << ec.value());
- return false;
- }
- LOG_PRINT_L0("Blockchain stored OK.");
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::deinit()
-{
- return store_blockchain();
-}
-//------------------------------------------------------------------
-bool blockchain_storage::pop_block_from_blockchain()
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
-
- CHECK_AND_ASSERT_MES(m_blocks.size() > 1, false, "pop_block_from_blockchain: can't pop from blockchain with size = " << m_blocks.size());
- size_t h = m_blocks.size()-1;
- block_extended_info& bei = m_blocks[h];
- //crypto::hash id = get_block_hash(bei.bl);
- bool r = purge_block_data_from_blockchain(bei.bl, bei.bl.tx_hashes.size());
- CHECK_AND_ASSERT_MES(r, false, "Failed to purge_block_data_from_blockchain for block " << get_block_hash(bei.bl) << " on height " << h);
-
- //remove from index
- auto bl_ind = m_blocks_index.find(get_block_hash(bei.bl));
- CHECK_AND_ASSERT_MES(bl_ind != m_blocks_index.end(), false, "pop_block_from_blockchain: blockchain id not found in index");
- m_blocks_index.erase(bl_ind);
- //pop block from core
- m_blocks.pop_back();
- m_tx_pool->on_blockchain_dec(m_blocks.size()-1, get_tail_id());
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::reset_and_set_genesis_block(const block& b)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- m_transactions.clear();
- m_spent_keys.clear();
- m_blocks.clear();
- m_blocks_index.clear();
- m_alternative_chains.clear();
- m_outputs.clear();
-
- block_verification_context bvc = boost::value_initialized<block_verification_context>();
- add_new_block(b, bvc);
- return bvc.m_added_to_main_chain && !bvc.m_verifivation_failed;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::purge_transaction_keyimages_from_blockchain(const transaction& tx, bool strict_check)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- struct purge_transaction_visitor: public boost::static_visitor<bool>
- {
- key_images_container& m_spent_keys;
- bool m_strict_check;
- purge_transaction_visitor(key_images_container& spent_keys, bool strict_check):m_spent_keys(spent_keys), m_strict_check(strict_check){}
-
- bool operator()(const txin_to_key& inp) const
- {
- //const crypto::key_image& ki = inp.k_image;
- auto r = m_spent_keys.find(inp.k_image);
- if(r != m_spent_keys.end())
- {
- m_spent_keys.erase(r);
- }else
- {
- CHECK_AND_ASSERT_MES(!m_strict_check, false, "purge_block_data_from_blockchain: key image in transaction not found");
- }
- return true;
- }
- bool operator()(const txin_gen& inp) const
- {
- return true;
- }
- bool operator()(const txin_to_script& tx) const
- {
- return false;
- }
-
- bool operator()(const txin_to_scripthash& tx) const
- {
- return false;
- }
- };
-
- BOOST_FOREACH(const txin_v& in, tx.vin)
- {
- bool r = boost::apply_visitor(purge_transaction_visitor(m_spent_keys, strict_check), in);
- CHECK_AND_ASSERT_MES(!strict_check || r, false, "failed to process purge_transaction_visitor");
- }
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::purge_transaction_from_blockchain(const crypto::hash& tx_id)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- auto tx_index_it = m_transactions.find(tx_id);
- CHECK_AND_ASSERT_MES(tx_index_it != m_transactions.end(), false, "purge_block_data_from_blockchain: transaction not found in blockchain index!!");
- transaction& tx = tx_index_it->second.tx;
-
- purge_transaction_keyimages_from_blockchain(tx, true);
-
- if(!is_coinbase(tx))
- {
- cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
- bool r = m_tx_pool->add_tx(tx, tvc, true, true, 1);
- CHECK_AND_ASSERT_MES(r, false, "purge_block_data_from_blockchain: failed to add transaction to transaction pool");
- }
-
- bool res = pop_transaction_from_global_index(tx, tx_id);
- m_transactions.erase(tx_index_it);
- LOG_PRINT_L1("Removed transaction from blockchain history:" << tx_id << ENDL);
- return res;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::purge_block_data_from_blockchain(const block& bl, size_t processed_tx_count)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
-
- bool res = true;
- CHECK_AND_ASSERT_MES(processed_tx_count <= bl.tx_hashes.size(), false, "wrong processed_tx_count in purge_block_data_from_blockchain");
- for(size_t count = 0; count != processed_tx_count; count++)
- {
- res = purge_transaction_from_blockchain(bl.tx_hashes[(processed_tx_count -1)- count]) && res;
- }
-
- res = purge_transaction_from_blockchain(get_transaction_hash(bl.miner_tx)) && res;
-
- return res;
-}
-//------------------------------------------------------------------
-crypto::hash blockchain_storage::get_tail_id(uint64_t& height) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- height = get_current_blockchain_height()-1;
- return get_tail_id();
-}
-//------------------------------------------------------------------
-crypto::hash blockchain_storage::get_tail_id() const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- crypto::hash id = null_hash;
- if(m_blocks.size())
- {
- get_block_hash(m_blocks.back().bl, id);
- }
- return id;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::get_short_chain_history(std::list<crypto::hash>& ids) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- size_t i = 0;
- size_t current_multiplier = 1;
- size_t sz = m_blocks.size();
- if(!sz)
- return true;
- size_t current_back_offset = 1;
- bool genesis_included = false;
- while(current_back_offset < sz)
- {
- ids.push_back(get_block_hash(m_blocks[sz-current_back_offset].bl));
- if(sz-current_back_offset == 0)
- genesis_included = true;
- if(i < 10)
- {
- ++current_back_offset;
- }else
- {
- current_back_offset += current_multiplier *= 2;
- }
- ++i;
- }
- if(!genesis_included)
- ids.push_back(get_block_hash(m_blocks[0].bl));
-
- return true;
-}
-//------------------------------------------------------------------
-crypto::hash blockchain_storage::get_block_id_by_height(uint64_t height) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- if(height >= m_blocks.size())
- return null_hash;
-
- return get_block_hash(m_blocks[height].bl);
-}
-//------------------------------------------------------------------
-bool blockchain_storage::get_block_by_hash(const crypto::hash &h, block &blk) const {
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
-
- // try to find block in main chain
- blocks_by_id_index::const_iterator it = m_blocks_index.find(h);
- if (m_blocks_index.end() != it) {
- blk = m_blocks[it->second].bl;
- return true;
- }
-
- // try to find block in alternative chain
- blocks_ext_by_hash::const_iterator it_alt = m_alternative_chains.find(h);
- if (m_alternative_chains.end() != it_alt) {
- blk = it_alt->second.bl;
- return true;
- }
-
- return false;
-}
-//------------------------------------------------------------------
-void blockchain_storage::get_all_known_block_ids(std::list<crypto::hash> &main, std::list<crypto::hash> &alt, std::list<crypto::hash> &invalid) const {
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
-
- BOOST_FOREACH(const blocks_by_id_index::value_type &v, m_blocks_index)
- main.push_back(v.first);
-
- BOOST_FOREACH(const blocks_ext_by_hash::value_type &v, m_alternative_chains)
- alt.push_back(v.first);
-
- BOOST_FOREACH(const blocks_ext_by_hash::value_type &v, m_invalid_blocks)
- invalid.push_back(v.first);
-}
-//------------------------------------------------------------------
-difficulty_type blockchain_storage::get_difficulty_for_next_block() const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- std::vector<uint64_t> timestamps;
- std::vector<difficulty_type> commulative_difficulties;
- size_t offset = m_blocks.size() - std::min(m_blocks.size(), static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT));
- if(!offset)
- ++offset;//skip genesis block
- for(; offset < m_blocks.size(); offset++)
- {
- timestamps.push_back(m_blocks[offset].bl.timestamp);
- commulative_difficulties.push_back(m_blocks[offset].cumulative_difficulty);
- }
- return next_difficulty(timestamps, commulative_difficulties,DIFFICULTY_TARGET_V1);
-}
-//------------------------------------------------------------------
-bool blockchain_storage::rollback_blockchain_switching(std::list<block>& original_chain, size_t rollback_height)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
-
- // fail if rollback_height passed is too high
- if (rollback_height > m_blocks.size())
- {
- return true;
- }
-
- //remove failed subchain
- for(size_t i = m_blocks.size()-1; i >=rollback_height; i--)
- {
- bool r = pop_block_from_blockchain();
- CHECK_AND_ASSERT_MES(r, false, "PANIC! failed to remove block while chain switching during the rollback!");
- }
- //return back original chain
- BOOST_FOREACH(auto& bl, original_chain)
- {
- block_verification_context bvc = boost::value_initialized<block_verification_context>();
- bool r = handle_block_to_main_chain(bl, bvc);
- CHECK_AND_ASSERT_MES(r && bvc.m_added_to_main_chain, false, "PANIC! failed to add (again) block while chain switching during the rollback!");
- }
-
- LOG_PRINT_L1("Rollback success.");
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::iterator>& alt_chain, bool discard_disconnected_chain)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- CHECK_AND_ASSERT_MES(alt_chain.size(), false, "switch_to_alternative_blockchain: empty chain passed");
-
- size_t split_height = alt_chain.front()->second.height;
- CHECK_AND_ASSERT_MES(m_blocks.size() > split_height, false, "switch_to_alternative_blockchain: blockchain size is lower than split height");
-
- //disconnecting old chain
- std::list<block> disconnected_chain;
- for(size_t i = m_blocks.size()-1; i >=split_height; i--)
- {
- block b = m_blocks[i].bl;
- bool r = pop_block_from_blockchain();
- CHECK_AND_ASSERT_MES(r, false, "failed to remove block on chain switching");
- disconnected_chain.push_front(b);
- }
-
- //connecting new alternative chain
- for(auto alt_ch_iter = alt_chain.begin(); alt_ch_iter != alt_chain.end(); alt_ch_iter++)
- {
- auto ch_ent = *alt_ch_iter;
- block_verification_context bvc = boost::value_initialized<block_verification_context>();
- bool r = handle_block_to_main_chain(ch_ent->second.bl, bvc);
- if(!r || !bvc.m_added_to_main_chain)
- {
- LOG_PRINT_L1("Failed to switch to alternative blockchain");
- rollback_blockchain_switching(disconnected_chain, split_height);
- add_block_as_invalid(ch_ent->second, get_block_hash(ch_ent->second.bl));
- LOG_PRINT_L1("The block was inserted as invalid while connecting new alternative chain, block_id: " << get_block_hash(ch_ent->second.bl));
- m_alternative_chains.erase(ch_ent);
-
- for(auto alt_ch_to_orph_iter = ++alt_ch_iter; alt_ch_to_orph_iter != alt_chain.end(); alt_ch_to_orph_iter++)
- {
- //block_verification_context bvc = boost::value_initialized<block_verification_context>();
- add_block_as_invalid((*alt_ch_iter)->second, (*alt_ch_iter)->first);
- m_alternative_chains.erase(*alt_ch_to_orph_iter);
- }
- return false;
- }
- }
-
- if(!discard_disconnected_chain)
- {
- //pushing old chain as alternative chain
- BOOST_FOREACH(auto& old_ch_ent, disconnected_chain)
- {
- block_verification_context bvc = boost::value_initialized<block_verification_context>();
- bool r = handle_alternative_block(old_ch_ent, get_block_hash(old_ch_ent), bvc);
- if(!r)
- {
- LOG_PRINT_L1("Failed to push ex-main chain blocks to alternative chain ");
- rollback_blockchain_switching(disconnected_chain, split_height);
- return false;
- }
- }
- }
-
- //removing all_chain entries from alternative chain
- BOOST_FOREACH(auto ch_ent, alt_chain)
- {
- m_alternative_chains.erase(ch_ent);
- }
-
- LOG_PRINT_GREEN("REORGANIZE SUCCESS! on height: " << split_height << ", new blockchain size: " << m_blocks.size(), LOG_LEVEL_0);
- return true;
-}
-//------------------------------------------------------------------
-difficulty_type blockchain_storage::get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::iterator>& alt_chain, block_extended_info& bei) const
-{
- std::vector<uint64_t> timestamps;
- std::vector<difficulty_type> commulative_difficulties;
- if(alt_chain.size()< DIFFICULTY_BLOCKS_COUNT)
- {
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- size_t main_chain_stop_offset = alt_chain.size() ? alt_chain.front()->second.height : bei.height;
- size_t main_chain_count = DIFFICULTY_BLOCKS_COUNT - std::min(static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT), alt_chain.size());
- main_chain_count = std::min(main_chain_count, main_chain_stop_offset);
- size_t main_chain_start_offset = main_chain_stop_offset - main_chain_count;
-
- if(!main_chain_start_offset)
- ++main_chain_start_offset; //skip genesis block
- for(; main_chain_start_offset < main_chain_stop_offset; ++main_chain_start_offset)
- {
- timestamps.push_back(m_blocks[main_chain_start_offset].bl.timestamp);
- commulative_difficulties.push_back(m_blocks[main_chain_start_offset].cumulative_difficulty);
- }
-
- CHECK_AND_ASSERT_MES((alt_chain.size() + timestamps.size()) <= DIFFICULTY_BLOCKS_COUNT, false, "Internal error, alt_chain.size()["<< alt_chain.size()
- << "] + vtimestampsec.size()[" << timestamps.size() << "] NOT <= DIFFICULTY_WINDOW[]" << DIFFICULTY_BLOCKS_COUNT );
- BOOST_FOREACH(auto it, alt_chain)
- {
- timestamps.push_back(it->second.bl.timestamp);
- commulative_difficulties.push_back(it->second.cumulative_difficulty);
- }
- }else
- {
- timestamps.resize(std::min(alt_chain.size(), static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT)));
- commulative_difficulties.resize(std::min(alt_chain.size(), static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT)));
- size_t count = 0;
- size_t max_i = timestamps.size()-1;
- BOOST_REVERSE_FOREACH(auto it, alt_chain)
- {
- timestamps[max_i - count] = it->second.bl.timestamp;
- commulative_difficulties[max_i - count] = it->second.cumulative_difficulty;
- count++;
- if(count >= DIFFICULTY_BLOCKS_COUNT)
- break;
- }
- }
- return next_difficulty(timestamps, commulative_difficulties,DIFFICULTY_TARGET_V1);
-}
-//------------------------------------------------------------------
-bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t height) const
-{
- CHECK_AND_ASSERT_MES(b.miner_tx.vin.size() == 1, false, "coinbase transaction in the block has no inputs");
- CHECK_AND_ASSERT_MES(b.miner_tx.vin[0].type() == typeid(txin_gen), false, "coinbase transaction in the block has the wrong type");
- if(boost::get<txin_gen>(b.miner_tx.vin[0]).height != height)
- {
- LOG_PRINT_RED_L1("The miner transaction in block has invalid height: " << boost::get<txin_gen>(b.miner_tx.vin[0]).height << ", expected: " << height);
- return false;
- }
- CHECK_AND_ASSERT_MES(b.miner_tx.unlock_time == height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW,
- false,
- "coinbase transaction transaction has the wrong unlock time=" << b.miner_tx.unlock_time << ", expected " << height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW);
-
- //check outs overflow
- if(!check_outs_overflow(b.miner_tx))
- {
- LOG_PRINT_RED_L1("miner transaction has money overflow in block " << get_block_hash(b));
- return false;
- }
-
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins) const
-{
- //validate reward
- uint64_t money_in_use = 0;
- BOOST_FOREACH(auto& o, b.miner_tx.vout)
- money_in_use += o.amount;
-
- std::vector<size_t> last_blocks_sizes;
- get_last_n_blocks_sizes(last_blocks_sizes, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
- if (!get_block_reward(epee::misc_utils::median(last_blocks_sizes), cumulative_block_size, already_generated_coins, base_reward,0)) {
- LOG_PRINT_L1("block size " << cumulative_block_size << " is bigger than allowed for this blockchain");
- return false;
- }
- if(base_reward + fee < money_in_use)
- {
- LOG_PRINT_L1("coinbase transaction spend too much money (" << print_money(money_in_use) << "). Block reward is " << print_money(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee) << ")");
- return false;
- }
- if(base_reward + fee != money_in_use)
- {
- LOG_PRINT_L1("coinbase transaction doesn't use full amount of block reward: spent: "
- << print_money(money_in_use) << ", block reward " << print_money(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee) << ")");
- return false;
- }
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::get_backward_blocks_sizes(size_t from_height, std::vector<size_t>& sz, size_t count) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- CHECK_AND_ASSERT_MES(from_height < m_blocks.size(), false, "Internal error: get_backward_blocks_sizes called with from_height=" << from_height << ", blockchain height = " << m_blocks.size());
-
- size_t start_offset = (from_height+1) - std::min((from_height+1), count);
- for(size_t i = start_offset; i != from_height+1; i++)
- sz.push_back(m_blocks[i].block_cumulative_size);
-
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::get_last_n_blocks_sizes(std::vector<size_t>& sz, size_t count) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- if(!m_blocks.size())
- return true;
- return get_backward_blocks_sizes(m_blocks.size() -1, sz, count);
-}
-//------------------------------------------------------------------
-uint64_t blockchain_storage::get_current_cumulative_blocksize_limit() const
-{
- return m_current_block_cumul_sz_limit;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) const
-{
- size_t median_size;
- uint64_t already_generated_coins;
-
- CRITICAL_REGION_BEGIN(m_blockchain_lock);
- b.major_version = CURRENT_BLOCK_MAJOR_VERSION;
- b.minor_version = CURRENT_BLOCK_MINOR_VERSION;
- b.prev_id = get_tail_id();
- b.timestamp = time(NULL);
- height = m_blocks.size();
- diffic = get_difficulty_for_next_block();
- CHECK_AND_ASSERT_MES(diffic, false, "difficulty overhead.");
-
- median_size = m_current_block_cumul_sz_limit / 2;
- already_generated_coins = m_blocks.back().already_generated_coins;
-
- CRITICAL_REGION_END();
-
- size_t txs_size;
- uint64_t fee;
- if (!m_tx_pool->fill_block_template(b, median_size, already_generated_coins, txs_size, fee)) {
- return false;
- }
-#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
- size_t real_txs_size = 0;
- uint64_t real_fee = 0;
- CRITICAL_REGION_BEGIN(m_tx_pool->m_transactions_lock);
- BOOST_FOREACH(crypto::hash &cur_hash, b.tx_hashes) {
- auto cur_res = m_tx_pool->m_transactions.find(cur_hash);
- if (cur_res == m_tx_pool->m_transactions.end()) {
- LOG_ERROR("Creating block template: error: transaction not found");
- continue;
- }
- tx_memory_pool::tx_details &cur_tx = cur_res->second;
- real_txs_size += cur_tx.blob_size;
- real_fee += cur_tx.fee;
- if (cur_tx.blob_size != get_object_blobsize(cur_tx.tx)) {
- LOG_ERROR("Creating block template: error: invalid transaction size");
- }
- uint64_t inputs_amount;
- if (!get_inputs_money_amount(cur_tx.tx, inputs_amount)) {
- LOG_ERROR("Creating block template: error: cannot get inputs amount");
- } else if (cur_tx.fee != inputs_amount - get_outs_money_amount(cur_tx.tx)) {
- LOG_ERROR("Creating block template: error: invalid fee");
- }
- }
- if (txs_size != real_txs_size) {
- LOG_ERROR("Creating block template: error: wrongly calculated transaction size");
- }
- if (fee != real_fee) {
- LOG_ERROR("Creating block template: error: wrongly calculated fee");
- }
- CRITICAL_REGION_END();
- LOG_PRINT_L1("Creating block template: height " << height <<
- ", median size " << median_size <<
- ", already generated coins " << already_generated_coins <<
- ", transaction size " << txs_size <<
- ", fee " << fee);
-#endif
-
- /*
- two-phase miner transaction generation: we don't know exact block size until we prepare block, but we don't know reward until we know
- block size, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block size
- */
- //make blocks coin-base tx looks close to real coinbase tx to get truthful blob size
- bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, 11);
- CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, first chance");
- size_t cumulative_size = txs_size + get_object_blobsize(b.miner_tx);
-#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
- LOG_PRINT_L1("Creating block template: miner tx size " << get_object_blobsize(b.miner_tx) <<
- ", cumulative size " << cumulative_size);
-#endif
- for (size_t try_count = 0; try_count != 10; ++try_count) {
- r = construct_miner_tx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.miner_tx, ex_nonce, 11);
-
- CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, second chance");
- size_t coinbase_blob_size = get_object_blobsize(b.miner_tx);
- if (coinbase_blob_size > cumulative_size - txs_size) {
- cumulative_size = txs_size + coinbase_blob_size;
-#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
- LOG_PRINT_L1("Creating block template: miner tx size " << coinbase_blob_size <<
- ", cumulative size " << cumulative_size << " is greater then before");
-#endif
- continue;
- }
-
- if (coinbase_blob_size < cumulative_size - txs_size) {
- size_t delta = cumulative_size - txs_size - coinbase_blob_size;
-#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
- LOG_PRINT_L1("Creating block template: miner tx size " << coinbase_blob_size <<
- ", cumulative size " << txs_size + coinbase_blob_size <<
- " is less then before, adding " << delta << " zero bytes");
-#endif
- b.miner_tx.extra.insert(b.miner_tx.extra.end(), delta, 0);
- //here could be 1 byte difference, because of extra field counter is varint, and it can become from 1-byte len to 2-bytes len.
- if (cumulative_size != txs_size + get_object_blobsize(b.miner_tx)) {
- CHECK_AND_ASSERT_MES(cumulative_size + 1 == txs_size + get_object_blobsize(b.miner_tx), false, "unexpected case: cumulative_size=" << cumulative_size << " + 1 is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.miner_tx)=" << get_object_blobsize(b.miner_tx));
- b.miner_tx.extra.resize(b.miner_tx.extra.size() - 1);
- if (cumulative_size != txs_size + get_object_blobsize(b.miner_tx)) {
- //fuck, not lucky, -1 makes varint-counter size smaller, in that case we continue to grow with cumulative_size
- LOG_PRINT_RED("Miner tx creation has no luck with delta_extra size = " << delta << " and " << delta - 1 , LOG_LEVEL_2);
- cumulative_size += delta - 1;
- continue;
- }
- LOG_PRINT_GREEN("Setting extra for block: " << b.miner_tx.extra.size() << ", try_count=" << try_count, LOG_LEVEL_1);
- }
- }
- CHECK_AND_ASSERT_MES(cumulative_size == txs_size + get_object_blobsize(b.miner_tx), false, "unexpected case: cumulative_size=" << cumulative_size << " is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.miner_tx)=" << get_object_blobsize(b.miner_tx));
-#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
- LOG_PRINT_L1("Creating block template: miner tx size " << coinbase_blob_size <<
- ", cumulative size " << cumulative_size << " is now good");
-#endif
- return true;
- }
- LOG_ERROR("Failed to create_block_template with " << 10 << " tries");
- return false;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::complete_timestamps_vector(uint64_t start_top_height, std::vector<uint64_t>& timestamps) const
-{
-
- if(timestamps.size() >= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW)
- return true;
-
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- size_t need_elements = BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW - timestamps.size();
- CHECK_AND_ASSERT_MES(start_top_height < m_blocks.size(), false, "internal error: passed start_height = " << start_top_height << " not less then m_blocks.size()=" << m_blocks.size());
- size_t stop_offset = start_top_height > need_elements ? start_top_height - need_elements:0;
- do
- {
- timestamps.push_back(m_blocks[start_top_height].bl.timestamp);
- if(start_top_height == 0)
- break;
- --start_top_height;
- }while(start_top_height != stop_offset);
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
-
- uint64_t block_height = get_block_height(b);
- if(0 == block_height)
- {
- LOG_PRINT_L1("Block with id: " << epee::string_tools::pod_to_hex(id) << " (as alternative) has wrong miner transaction");
- bvc.m_verifivation_failed = true;
- return false;
- }
- if (!m_checkpoints.is_alternative_block_allowed(get_current_blockchain_height(), block_height))
- {
- LOG_PRINT_RED_L1("Block with id: " << id
- << ENDL << " can't be accepted for alternative chain, block height: " << block_height
- << ENDL << " blockchain height: " << get_current_blockchain_height());
- bvc.m_verifivation_failed = true;
- return false;
- }
-
- //Block is not related with head of main chain
- //first of all - look in alternative chains container
- auto it_main_prev = m_blocks_index.find(b.prev_id);
- auto it_prev = m_alternative_chains.find(b.prev_id);
- if(it_prev != m_alternative_chains.end() || it_main_prev != m_blocks_index.end())
- {
- //we have new block in alternative chain
-
- //build alternative subchain, front -> mainchain, back -> alternative head
- blocks_ext_by_hash::iterator alt_it = it_prev; //m_alternative_chains.find()
- std::list<blocks_ext_by_hash::iterator> alt_chain;
- std::vector<uint64_t> timestamps;
- while(alt_it != m_alternative_chains.end())
- {
- alt_chain.push_front(alt_it);
- timestamps.push_back(alt_it->second.bl.timestamp);
- alt_it = m_alternative_chains.find(alt_it->second.bl.prev_id);
- }
-
- if(alt_chain.size())
- {
- //make sure that it has right connection to main chain
- CHECK_AND_ASSERT_MES(m_blocks.size() > alt_chain.front()->second.height, false, "main blockchain wrong height");
- crypto::hash h = null_hash;
- get_block_hash(m_blocks[alt_chain.front()->second.height - 1].bl, h);
- CHECK_AND_ASSERT_MES(h == alt_chain.front()->second.bl.prev_id, false, "alternative chain has wrong connection to main chain");
- complete_timestamps_vector(alt_chain.front()->second.height - 1, timestamps);
- }else
- {
- CHECK_AND_ASSERT_MES(it_main_prev != m_blocks_index.end(), false, "internal error: broken imperative condition it_main_prev != m_blocks_index.end()");
- complete_timestamps_vector(it_main_prev->second, timestamps);
- }
- //check timestamp correct
- if(!check_block_timestamp(timestamps, b))
- {
- LOG_PRINT_RED_L1("Block with id: " << id
- << ENDL << " for alternative chain, has invalid timestamp: " << b.timestamp);
- //add_block_as_invalid(b, id);//do not add blocks to invalid storage before proof of work check was passed
- bvc.m_verifivation_failed = true;
- return false;
- }
-
- block_extended_info bei = boost::value_initialized<block_extended_info>();
- bei.bl = b;
- bei.height = alt_chain.size() ? it_prev->second.height + 1 : it_main_prev->second + 1;
-
- bool is_a_checkpoint;
- if(!m_checkpoints.check_block(bei.height, id, is_a_checkpoint))
- {
- LOG_ERROR("CHECKPOINT VALIDATION FAILED");
- bvc.m_verifivation_failed = true;
- return false;
- }
-
- // Always check PoW for alternative blocks
- m_is_in_checkpoint_zone = false;
- difficulty_type current_diff = get_next_difficulty_for_alternative_chain(alt_chain, bei);
- CHECK_AND_ASSERT_MES(current_diff, false, "!!!!!!! DIFFICULTY OVERHEAD !!!!!!!");
- crypto::hash proof_of_work = null_hash;
- get_block_longhash(bei.bl, proof_of_work, bei.height);
- if(!check_hash(proof_of_work, current_diff))
- {
- LOG_PRINT_RED_L1("Block with id: " << id
- << ENDL << " for alternative chain, does not have enough proof of work: " << proof_of_work
- << ENDL << " expected difficulty: " << current_diff);
- bvc.m_verifivation_failed = true;
- return false;
- }
-
- if(!prevalidate_miner_transaction(b, bei.height))
- {
- LOG_PRINT_RED_L1("Block with id: " << epee::string_tools::pod_to_hex(id)
- << " (as alternative) has incorrect miner transaction.");
- bvc.m_verifivation_failed = true;
- return false;
-
- }
-
- bei.cumulative_difficulty = alt_chain.size() ? it_prev->second.cumulative_difficulty: m_blocks[it_main_prev->second].cumulative_difficulty;
- bei.cumulative_difficulty += current_diff;
-
-#ifdef _DEBUG
- auto i_dres = m_alternative_chains.find(id);
- CHECK_AND_ASSERT_MES(i_dres == m_alternative_chains.end(), false, "insertion of new alternative block returned as it already exist");
-#endif
- auto i_res = m_alternative_chains.insert(blocks_ext_by_hash::value_type(id, bei));
- CHECK_AND_ASSERT_MES(i_res.second, false, "insertion of new alternative block returned as it already exist");
- alt_chain.push_back(i_res.first);
-
- if(is_a_checkpoint)
- {
- //do reorganize!
- LOG_PRINT_GREEN("###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_blocks.size() - 1 <<
- ", checkpoint is found in alternative chain on height " << bei.height, LOG_LEVEL_0);
- bool r = switch_to_alternative_blockchain(alt_chain, true);
- if(r) bvc.m_added_to_main_chain = true;
- else bvc.m_verifivation_failed = true;
- return r;
- }else if(m_blocks.back().cumulative_difficulty < bei.cumulative_difficulty) //check if difficulty bigger then in main chain
- {
- //do reorganize!
- LOG_PRINT_GREEN("###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_blocks.size() - 1 << " with cum_difficulty " << m_blocks.back().cumulative_difficulty
- << ENDL << " alternative blockchain size: " << alt_chain.size() << " with cum_difficulty " << bei.cumulative_difficulty, LOG_LEVEL_0);
- bool r = switch_to_alternative_blockchain(alt_chain, false);
- if(r) bvc.m_added_to_main_chain = true;
- else bvc.m_verifivation_failed = true;
- return r;
- }else
- {
- LOG_PRINT_BLUE("----- BLOCK ADDED AS ALTERNATIVE ON HEIGHT " << bei.height
- << ENDL << "id:\t" << id
- << ENDL << "PoW:\t" << proof_of_work
- << ENDL << "difficulty:\t" << current_diff, LOG_LEVEL_0);
- return true;
- }
- }else
- {
- //block orphaned
- bvc.m_marked_as_orphaned = true;
- LOG_PRINT_RED_L1("Block recognized as orphaned and rejected, id = " << id);
- }
-
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- if(start_offset >= m_blocks.size())
- return false;
- for(size_t i = start_offset; i < start_offset + count && i < m_blocks.size();i++)
- {
- blocks.push_back(m_blocks[i].bl);
- std::list<crypto::hash> missed_ids;
- get_transactions(m_blocks[i].bl.tx_hashes, txs, missed_ids);
- CHECK_AND_ASSERT_MES(!missed_ids.size(), false, "has missed transactions in own block in main blockchain");
- }
-
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- if(start_offset >= m_blocks.size())
- return false;
-
- for(size_t i = start_offset; i < start_offset + count && i < m_blocks.size();i++)
- blocks.push_back(m_blocks[i].bl);
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- rsp.current_blockchain_height = get_current_blockchain_height();
- std::list<block> blocks;
- get_blocks(arg.blocks, blocks, rsp.missed_ids);
-
- BOOST_FOREACH(const auto& bl, blocks)
- {
- std::list<crypto::hash> missed_tx_id;
- std::list<transaction> txs;
- get_transactions(bl.tx_hashes, txs, rsp.missed_ids);
- CHECK_AND_ASSERT_MES(!missed_tx_id.size(), false, "Internal error: has missed missed_tx_id.size()=" << missed_tx_id.size()
- << ENDL << "for block id = " << get_block_hash(bl));
- rsp.blocks.push_back(block_complete_entry());
- block_complete_entry& e = rsp.blocks.back();
- //pack block
- e.block = t_serializable_object_to_blob(bl);
- //pack transactions
- BOOST_FOREACH(transaction& tx, txs)
- e.txs.push_back(t_serializable_object_to_blob(tx));
-
- }
- //get another transactions, if need
- std::list<transaction> txs;
- get_transactions(arg.txs, txs, rsp.missed_ids);
- //pack aside transactions
- BOOST_FOREACH(const auto& tx, txs)
- rsp.txs.push_back(t_serializable_object_to_blob(tx));
-
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::get_alternative_blocks(std::list<block>& blocks) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
-
- BOOST_FOREACH(const auto& alt_bl, m_alternative_chains)
- {
- blocks.push_back(alt_bl.second.bl);
- }
- return true;
-}
-//------------------------------------------------------------------
-size_t blockchain_storage::get_alternative_blocks_count() const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- return m_alternative_chains.size();
-}
-//------------------------------------------------------------------
-bool blockchain_storage::add_out_to_get_random_outs(const std::vector<std::pair<crypto::hash, size_t> >& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- transactions_container::const_iterator tx_it = m_transactions.find(amount_outs[i].first);
- CHECK_AND_ASSERT_MES(tx_it != m_transactions.end(), false, "internal error: transaction with id " << amount_outs[i].first << ENDL <<
- ", used in mounts global index for amount=" << amount << ": i=" << i << "not found in transactions index");
- CHECK_AND_ASSERT_MES(tx_it->second.tx.vout.size() > amount_outs[i].second, false, "internal error: in global outs index, transaction out index="
- << amount_outs[i].second << " more than transaction outputs = " << tx_it->second.tx.vout.size() << ", for tx id = " << amount_outs[i].first);
- const transaction& tx = tx_it->second.tx;
- CHECK_AND_ASSERT_MES(tx.vout[amount_outs[i].second].target.type() == typeid(txout_to_key), false, "unknown tx out type");
-
- //check if transaction is unlocked
- if(!is_tx_spendtime_unlocked(tx.unlock_time))
- return false;
-
- COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oen = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry());
- oen.global_amount_index = i;
- oen.out_key = boost::get<txout_to_key>(tx.vout[amount_outs[i].second].target).key;
- return true;
-}
-//------------------------------------------------------------------
-size_t blockchain_storage::find_end_of_allowed_index(const std::vector<std::pair<crypto::hash, size_t> >& amount_outs) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- if(!amount_outs.size())
- return 0;
- size_t i = amount_outs.size();
- do
- {
- --i;
- transactions_container::const_iterator it = m_transactions.find(amount_outs[i].first);
- CHECK_AND_ASSERT_MES(it != m_transactions.end(), 0, "internal error: failed to find transaction from outputs index with tx_id=" << amount_outs[i].first);
- if(it->second.m_keeper_block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE <= get_current_blockchain_height() )
- return i+1;
- } while (i != 0);
- return 0;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- BOOST_FOREACH(uint64_t amount, req.amounts)
- {
- COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs = *res.outs.insert(res.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount());
- result_outs.amount = amount;
- auto it = m_outputs.find(amount);
- if(it == m_outputs.end())
- {
- LOG_PRINT_L1("COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS: not outs for amount " << amount << ", wallet should use some real outs when it lookup for some mix, so, at least one out for this amount should exist");
- continue;//actually this is strange situation, wallet should use some real outs when it lookup for some mix, so, at least one out for this amount should exist
- }
- const std::vector<std::pair<crypto::hash, size_t> >& amount_outs = it->second;
- //it is not good idea to use top fresh outs, because it increases possibility of transaction canceling on split
- //lets find upper bound of not fresh outs
- size_t up_index_limit = find_end_of_allowed_index(amount_outs);
- CHECK_AND_ASSERT_MES(up_index_limit <= amount_outs.size(), false, "internal error: find_end_of_allowed_index returned wrong index=" << up_index_limit << ", with amount_outs.size = " << amount_outs.size());
- if(amount_outs.size() > req.outs_count)
- {
- std::set<size_t> used;
- size_t try_count = 0;
- for(uint64_t j = 0; j != req.outs_count && try_count < up_index_limit;)
- {
- // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
- uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
- double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
- size_t i = (size_t)(frac*up_index_limit);
- // just in case rounding up to 1 occurs after sqrt
- if (i == up_index_limit)
- --i;
- if(used.count(i))
- continue;
- bool added = add_out_to_get_random_outs(amount_outs, result_outs, amount, i);
- used.insert(i);
- if(added)
- ++j;
- ++try_count;
- }
- }else
- {
- for(size_t i = 0; i != up_index_limit; i++)
- add_out_to_get_random_outs(amount_outs, result_outs, amount, i);
- }
- }
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, uint64_t& starter_offset) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
-
- if(!qblock_ids.size() /*|| !req.m_total_height*/)
- {
- LOG_PRINT_L1("Client sent wrong NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << qblock_ids.size() << /*", m_height=" << req.m_total_height <<*/ ", dropping connection");
- return false;
- }
- //check genesis match
- if(qblock_ids.back() != get_block_hash(m_blocks[0].bl))
- {
- LOG_PRINT_L1("Client sent wrong NOTIFY_REQUEST_CHAIN: genesis block missmatch: " << ENDL << "id: "
- << qblock_ids.back() << ", " << ENDL << "expected: " << get_block_hash(m_blocks[0].bl)
- << "," << ENDL << " dropping connection");
- return false;
- }
-
- /* Figure out what blocks we should request to get state_normal */
- size_t i = 0;
- auto bl_it = qblock_ids.begin();
- auto block_index_it = m_blocks_index.find(*bl_it);
- for(; bl_it != qblock_ids.end(); bl_it++, i++)
- {
- block_index_it = m_blocks_index.find(*bl_it);
- if(block_index_it != m_blocks_index.end())
- break;
- }
-
- if(bl_it == qblock_ids.end())
- {
- LOG_PRINT_L1("Internal error handling connection, can't find split point");
- return false;
- }
-
- if(block_index_it == m_blocks_index.end())
- {
- //this should NEVER happen, but, dose of paranoia in such cases is not too bad
- LOG_PRINT_L1("Internal error handling connection, can't find split point");
- return false;
- }
-
- //we start to put block ids INCLUDING last known id, just to make other side be sure
- starter_offset = block_index_it->second;
- return true;
-}
-//------------------------------------------------------------------
-uint64_t blockchain_storage::block_difficulty(size_t i) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- CHECK_AND_ASSERT_MES(i < m_blocks.size(), false, "wrong block index i = " << i << " at blockchain_storage::block_difficulty()");
- if(i == 0)
- return m_blocks[i].cumulative_difficulty;
-
- return m_blocks[i].cumulative_difficulty - m_blocks[i-1].cumulative_difficulty;
-}
-//------------------------------------------------------------------
-double blockchain_storage::get_avg_block_size( size_t count) const
-{
- if (count > get_current_blockchain_height()) return 500;
-
- double average = 0;
- _dbg1_c("net/blksize", "HEIGHT: " << get_current_blockchain_height());
- _dbg1_c("net/blksize", "BLOCK ID BY HEIGHT: " << get_block_id_by_height(get_current_blockchain_height()) );
- _dbg1_c("net/blksize", "BLOCK TAIL ID: " << get_tail_id() );
- std::vector<size_t> size_vector;
-
- get_backward_blocks_sizes(get_current_blockchain_height() - count, size_vector, count);
-
- std::vector<size_t>::iterator it;
- it = size_vector.begin();
- while (it != size_vector.end()) {
- average += *it;
- _dbg2_c("net/blksize", "VECTOR ELEMENT: " << (*it) );
- it++;
- }
- average = average / count;
- _dbg1_c("net/blksize", "VECTOR SIZE: " << size_vector.size() << " average=" << average);
-
- return average;
-}
-//------------------------------------------------------------------
-void blockchain_storage::print_blockchain(uint64_t start_index, uint64_t end_index) const
-{
- std::stringstream ss;
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- if(start_index >=m_blocks.size())
- {
- LOG_PRINT_L1("Wrong starter index set: " << start_index << ", expected max index " << m_blocks.size()-1);
- return;
- }
-
- for(size_t i = start_index; i != m_blocks.size() && i != end_index; i++)
- {
- ss << "height " << i << ", timestamp " << m_blocks[i].bl.timestamp << ", cumul_dif " << m_blocks[i].cumulative_difficulty << ", cumul_size " << m_blocks[i].block_cumulative_size
- << "\nid\t\t" << get_block_hash(m_blocks[i].bl)
- << "\ndifficulty\t\t" << block_difficulty(i) << ", nonce " << m_blocks[i].bl.nonce << ", tx_count " << m_blocks[i].bl.tx_hashes.size() << ENDL;
- }
- LOG_PRINT_L1("Current blockchain:" << ENDL << ss.str());
- LOG_PRINT_L0("Blockchain printed with log level 1");
-}
-//------------------------------------------------------------------
-void blockchain_storage::print_blockchain_index() const
-{
- std::stringstream ss;
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- BOOST_FOREACH(const blocks_by_id_index::value_type& v, m_blocks_index)
- ss << "id\t\t" << v.first << " height" << v.second << ENDL << "";
-
- LOG_PRINT_L0("Current blockchain index:" << ENDL << ss.str());
-}
-//------------------------------------------------------------------
-void blockchain_storage::print_blockchain_outs(const std::string& file) const
-{
- std::stringstream ss;
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- BOOST_FOREACH(const outputs_container::value_type& v, m_outputs)
- {
- const std::vector<std::pair<crypto::hash, size_t> >& vals = v.second;
- if(vals.size())
- {
- ss << "amount: " << v.first << ENDL;
- for(size_t i = 0; i != vals.size(); i++)
- ss << "\t" << vals[i].first << ": " << vals[i].second << ENDL;
- }
- }
- if(epee::file_io_utils::save_string_to_file(file, ss.str()))
- {
- LOG_PRINT_L0("Current outputs index writen to file: " << file);
- }else
- {
- LOG_PRINT_L0("Failed to write current outputs index to file: " << file);
- }
-}
-//------------------------------------------------------------------
-bool blockchain_storage::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- if(!find_blockchain_supplement(qblock_ids, resp.start_height))
- return false;
-
- resp.total_height = get_current_blockchain_height();
- size_t count = 0;
- for(size_t i = resp.start_height; i != m_blocks.size() && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++)
- resp.m_block_ids.push_back(get_block_hash(m_blocks[i].bl));
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- if(req_start_block > 0) {
- start_height = req_start_block;
- } else {
- if(!find_blockchain_supplement(qblock_ids, start_height))
- return false;
- }
-
- total_height = get_current_blockchain_height();
- size_t count = 0;
- for(size_t i = start_height; i != m_blocks.size() && count < max_count; i++, count++)
- {
- blocks.resize(blocks.size()+1);
- blocks.back().first = m_blocks[i].bl;
- std::list<crypto::hash> mis;
- get_transactions(m_blocks[i].bl.tx_hashes, blocks.back().second, mis);
- CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, transaction from block not found");
- }
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::add_block_as_invalid(const block& bl, const crypto::hash& h)
-{
- block_extended_info bei = AUTO_VAL_INIT(bei);
- bei.bl = bl;
- return add_block_as_invalid(bei, h);
-}
-//------------------------------------------------------------------
-bool blockchain_storage::add_block_as_invalid(const block_extended_info& bei, const crypto::hash& h)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- auto i_res = m_invalid_blocks.insert(std::map<crypto::hash, block_extended_info>::value_type(h, bei));
- CHECK_AND_ASSERT_MES(i_res.second, false, "at insertion invalid by tx returned status existed");
- LOG_PRINT_L1("BLOCK ADDED AS INVALID: " << h << ENDL << ", prev_id=" << bei.bl.prev_id << ", m_invalid_blocks count=" << m_invalid_blocks.size());
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::have_block(const crypto::hash& id) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- if(m_blocks_index.count(id))
- return true;
- if(m_alternative_chains.count(id))
- return true;
- /*if(m_orphaned_blocks.get<by_id>().count(id))
- return true;*/
-
- /*if(m_orphaned_by_tx.count(id))
- return true;*/
- if(m_invalid_blocks.count(id))
- return true;
-
- return false;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::handle_block_to_main_chain(const block& bl, block_verification_context& bvc)
-{
- crypto::hash id = get_block_hash(bl);
- return handle_block_to_main_chain(bl, id, bvc);
-}
-//------------------------------------------------------------------
-bool blockchain_storage::push_transaction_to_global_outs_index(const transaction& tx, const crypto::hash& tx_id, std::vector<uint64_t>& global_indexes)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- size_t i = 0;
- BOOST_FOREACH(const auto& ot, tx.vout)
- {
- outputs_container::mapped_type& amount_index = m_outputs[ot.amount];
- amount_index.push_back(std::pair<crypto::hash, size_t>(tx_id, i));
- global_indexes.push_back(amount_index.size()-1);
- ++i;
- }
- return true;
-}
-//------------------------------------------------------------------
-size_t blockchain_storage::get_total_transactions() const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- return m_transactions.size();
-}
-//------------------------------------------------------------------
-bool blockchain_storage::get_outs(uint64_t amount, std::list<crypto::public_key>& pkeys) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- auto it = m_outputs.find(amount);
- if(it == m_outputs.end())
- return true;
-
- BOOST_FOREACH(const auto& out_entry, it->second)
- {
- auto tx_it = m_transactions.find(out_entry.first);
- CHECK_AND_ASSERT_MES(tx_it != m_transactions.end(), false, "transactions outs global index consistency broken: wrong tx id in index");
- CHECK_AND_ASSERT_MES(tx_it->second.tx.vout.size() > out_entry.second, false, "transactions outs global index consistency broken: index in tx_outx more then size");
- CHECK_AND_ASSERT_MES(tx_it->second.tx.vout[out_entry.second].target.type() == typeid(txout_to_key), false, "transactions outs global index consistency broken: index in tx_outx more then size");
- pkeys.push_back(boost::get<txout_to_key>(tx_it->second.tx.vout[out_entry.second].target).key);
- }
-
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::pop_transaction_from_global_index(const transaction& tx, const crypto::hash& tx_id)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- size_t i = tx.vout.size()-1;
- BOOST_REVERSE_FOREACH(const auto& ot, tx.vout)
- {
- auto it = m_outputs.find(ot.amount);
- CHECK_AND_ASSERT_MES(it != m_outputs.end(), false, "transactions outs global index consistency broken");
- CHECK_AND_ASSERT_MES(it->second.size(), false, "transactions outs global index: empty index for amount: " << ot.amount);
- CHECK_AND_ASSERT_MES(it->second.back().first == tx_id , false, "transactions outs global index consistency broken: tx id missmatch");
- CHECK_AND_ASSERT_MES(it->second.back().second == i, false, "transactions outs global index consistency broken: in transaction index missmatch");
- it->second.pop_back();
- --i;
- }
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::add_transaction_from_block(const transaction& tx, const crypto::hash& tx_id, const crypto::hash& bl_id, uint64_t bl_height, size_t blob_size)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- struct add_transaction_input_visitor: public boost::static_visitor<bool>
- {
- key_images_container& m_spent_keys;
- const crypto::hash& m_tx_id;
- const crypto::hash& m_bl_id;
- add_transaction_input_visitor(key_images_container& spent_keys, const crypto::hash& tx_id, const crypto::hash& bl_id):m_spent_keys(spent_keys), m_tx_id(tx_id), m_bl_id(bl_id)
- {}
- bool operator()(const txin_to_key& in) const
- {
- const crypto::key_image& ki = in.k_image;
- auto r = m_spent_keys.insert(ki);
- if(!r.second)
- {
- //double spend detected
- LOG_PRINT_L1("tx with id: " << m_tx_id << " in block id: " << m_bl_id << " has input marked as spent with key image: " << ki << ", block declined");
- return false;
- }
- return true;
- }
-
- bool operator()(const txin_gen& tx) const{return true;}
- bool operator()(const txin_to_script& tx) const{return false;}
- bool operator()(const txin_to_scripthash& tx) const{return false;}
- };
-
- BOOST_FOREACH(const txin_v& in, tx.vin)
- {
- if(!boost::apply_visitor(add_transaction_input_visitor(m_spent_keys, tx_id, bl_id), in))
- {
- LOG_PRINT_L1("critical internal error: add_transaction_input_visitor failed. but here key_images should be checked");
- purge_transaction_keyimages_from_blockchain(tx, false);
- return false;
- }
- }
- transaction_chain_entry ch_e;
- ch_e.m_keeper_block_height = bl_height;
- ch_e.m_blob_size = blob_size;
- ch_e.tx = tx;
- auto i_r = m_transactions.insert(std::pair<crypto::hash, transaction_chain_entry>(tx_id, ch_e));
- if(!i_r.second)
- {
- LOG_PRINT_L1("tx with id: " << tx_id << " in block id: " << bl_id << " already in blockchain");
- return false;
- }
- bool r = push_transaction_to_global_outs_index(tx, tx_id, i_r.first->second.m_global_output_indexes);
- CHECK_AND_ASSERT_MES(r, false, "failed to return push_transaction_to_global_outs_index tx id " << tx_id);
- LOG_PRINT_L2("Added transaction to blockchain history:" << ENDL
- << "tx_id: " << tx_id << ENDL
- << "inputs: " << tx.vin.size() << ", outs: " << tx.vout.size() << ", spend money: " << print_money(get_outs_money_amount(tx)) << "(fee: " << (is_coinbase(tx) ? "0[coinbase]" : print_money(get_tx_fee(tx))) << ")");
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- auto it = m_transactions.find(tx_id);
- if(it == m_transactions.end())
- {
- LOG_PRINT_RED_L1("warning: get_tx_outputs_gindexs failed to find transaction with id = " << tx_id);
- return false;
- }
-
- CHECK_AND_ASSERT_MES(it->second.m_global_output_indexes.size(), false, "internal error: global indexes for transaction " << tx_id << " is empty");
- indexs = it->second.m_global_output_indexes;
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- bool res = check_tx_inputs(tx, &max_used_block_height);
- if(!res) return false;
- CHECK_AND_ASSERT_MES(max_used_block_height < m_blocks.size(), false, "internal error: max used block index=" << max_used_block_height << " is not less then blockchain size = " << m_blocks.size());
- get_block_hash(m_blocks[max_used_block_height].bl, max_used_block_id);
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::have_tx_keyimges_as_spent(const transaction &tx) const
-{
- BOOST_FOREACH(const txin_v& in, tx.vin)
- {
- CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, in_to_key, true);
- if(have_tx_keyimg_as_spent(in_to_key.k_image))
- return true;
- }
- return false;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height) const
-{
- crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx);
- return check_tx_inputs(tx, tx_prefix_hash, pmax_used_block_height);
-}
-//------------------------------------------------------------------
-bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height) const
-{
- size_t sig_index = 0;
- if(pmax_used_block_height)
- *pmax_used_block_height = 0;
-
- BOOST_FOREACH(const auto& txin, tx.vin)
- {
- CHECK_AND_ASSERT_MES(txin.type() == typeid(txin_to_key), false, "wrong type id in tx input at blockchain_storage::check_tx_inputs");
- const txin_to_key& in_to_key = boost::get<txin_to_key>(txin);
-
- CHECK_AND_ASSERT_MES(in_to_key.key_offsets.size(), false, "empty in_to_key.key_offsets in transaction with id " << get_transaction_hash(tx));
-
- if(have_tx_keyimg_as_spent(in_to_key.k_image))
- {
- LOG_PRINT_L1("Key image already spent in blockchain: " << epee::string_tools::pod_to_hex(in_to_key.k_image));
- return false;
- }
-
- CHECK_AND_ASSERT_MES(sig_index < tx.signatures.size(), false, "wrong transaction: not signature entry for input with index= " << sig_index);
- if(!check_tx_input(in_to_key, tx_prefix_hash, tx.signatures[sig_index], pmax_used_block_height))
- {
- LOG_PRINT_L1("Failed to check ring signature for tx " << get_transaction_hash(tx));
- return false;
- }
-
- sig_index++;
- }
-
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::is_tx_spendtime_unlocked(uint64_t unlock_time) const
-{
- if(unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER)
- {
- //interpret as block index
- if(get_current_blockchain_height()-1 + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time)
- return true;
- else
- return false;
- }else
- {
- //interpret as time
- uint64_t current_time = static_cast<uint64_t>(time(NULL));
- if(current_time + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V1 >= unlock_time)
- return true;
- else
- return false;
- }
- return false;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t* pmax_related_block_height) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
-
- struct outputs_visitor
- {
- std::vector<const crypto::public_key *>& m_results_collector;
- const blockchain_storage& m_bch;
- outputs_visitor(std::vector<const crypto::public_key *>& results_collector, const blockchain_storage& bch):m_results_collector(results_collector), m_bch(bch)
- {}
- bool handle_output(const transaction& tx, const tx_out& out)
- {
- //check tx unlock time
- if(!m_bch.is_tx_spendtime_unlocked(tx.unlock_time))
- {
- LOG_PRINT_L1("One of outputs for one of inputs has wrong tx.unlock_time = " << tx.unlock_time);
- return false;
- }
-
- if(out.target.type() != typeid(txout_to_key))
- {
- LOG_PRINT_L1("Output has wrong type id, which=" << out.target.which());
- return false;
- }
-
- m_results_collector.push_back(&boost::get<txout_to_key>(out.target).key);
- return true;
- }
- };
-
- //check ring signature
- std::vector<const crypto::public_key *> output_keys;
- outputs_visitor vi(output_keys, *this);
- if(!scan_outputkeys_for_indexes(txin, vi, pmax_related_block_height))
- {
- LOG_PRINT_L1("Failed to get output keys for tx with amount = " << print_money(txin.amount) << " and count indexes " << txin.key_offsets.size());
- return false;
- }
-
- if(txin.key_offsets.size() != output_keys.size())
- {
- LOG_PRINT_L1("Output keys for tx with amount = " << txin.amount << " and count indexes " << txin.key_offsets.size() << " returned wrong keys count " << output_keys.size());
- return false;
- }
- CHECK_AND_ASSERT_MES(sig.size() == output_keys.size(), false, "internal error: tx signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size());
- if(m_is_in_checkpoint_zone)
- return true;
- return crypto::check_ring_signature(tx_prefix_hash, txin.k_image, output_keys, sig.data());
-}
-//------------------------------------------------------------------
-uint64_t blockchain_storage::get_adjusted_time() const
-{
- //TODO: add collecting median time
- return time(NULL);
-}
-//------------------------------------------------------------------
-bool blockchain_storage::check_block_timestamp_main(const block& b) const
-{
- if(b.timestamp > get_adjusted_time() + CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT)
- {
- LOG_PRINT_L1("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than adjusted time + 2 hours");
- return false;
- }
-
- std::vector<uint64_t> timestamps;
- size_t offset = m_blocks.size() <= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW ? 0: m_blocks.size()- BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW;
- for(;offset!= m_blocks.size(); ++offset)
- timestamps.push_back(m_blocks[offset].bl.timestamp);
-
- return check_block_timestamp(std::move(timestamps), b);
-}
-//------------------------------------------------------------------
-bool blockchain_storage::check_block_timestamp(std::vector<uint64_t> timestamps, const block& b) const
-{
- if(timestamps.size() < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW)
- return true;
-
- uint64_t median_ts = epee::misc_utils::median(timestamps);
-
- if(b.timestamp < median_ts)
- {
- LOG_PRINT_L1("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", less than median of last " << BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW << " blocks, " << median_ts);
- return false;
- }
-
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc)
-{
- TIME_MEASURE_START(block_processing_time);
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- if(bl.prev_id != get_tail_id())
- {
- LOG_PRINT_L1("Block with id: " << id << ENDL
- << "has wrong prev_id: " << bl.prev_id << ENDL
- << "expected: " << get_tail_id());
- return false;
- }
-
- if(!check_block_timestamp_main(bl))
- {
- LOG_PRINT_L1("Block with id: " << id << ENDL
- << "has invalid timestamp: " << bl.timestamp);
- //add_block_as_invalid(bl, id);//do not add blocks to invalid storage befor proof of work check was passed
- bvc.m_verifivation_failed = true;
- return false;
- }
-
- //check proof of work
- TIME_MEASURE_START(target_calculating_time);
- difficulty_type current_diffic = get_difficulty_for_next_block();
- CHECK_AND_ASSERT_MES(current_diffic, false, "!!!!!!!!! difficulty overhead !!!!!!!!!");
- TIME_MEASURE_FINISH(target_calculating_time);
- TIME_MEASURE_START(longhash_calculating_time);
- crypto::hash proof_of_work = null_hash;
-
- // Formerly the code below contained an if loop with the following condition
- // !m_checkpoints.is_in_checkpoint_zone(get_current_blockchain_height())
- // however, this caused the daemon to not bother checking PoW for blocks
- // before checkpoints, which is very dangerous behaviour. We moved the PoW
- // validation out of the next chunk of code to make sure that we correctly
- // check PoW now.
- proof_of_work = get_block_longhash(bl, m_blocks.size());
-
- if(!check_hash(proof_of_work, current_diffic))
- {
- LOG_PRINT_L1("Block with id: " << id << ENDL
- << "does not have enough proof of work: " << proof_of_work << ENDL
- << "unexpected difficulty: " << current_diffic );
- bvc.m_verifivation_failed = true;
- return false;
- }
-
- // If we're at a checkpoint, ensure that our hardcoded checkpoint hash
- // is correct.
- if(m_checkpoints.is_in_checkpoint_zone(get_current_blockchain_height()))
- {
- if(!m_checkpoints.check_block(get_current_blockchain_height(), id))
- {
- LOG_ERROR("CHECKPOINT VALIDATION FAILED");
- bvc.m_verifivation_failed = true;
- return false;
- }
- }
-
- TIME_MEASURE_FINISH(longhash_calculating_time);
-
- if(!prevalidate_miner_transaction(bl, m_blocks.size()))
- {
- LOG_PRINT_L1("Block with id: " << id
- << " failed to pass prevalidation");
- bvc.m_verifivation_failed = true;
- return false;
- }
- crypto::hash coinbase_hash = null_hash;
- size_t coinbase_blob_size = 0;
- get_transaction_hash(bl.miner_tx, coinbase_hash, coinbase_blob_size);
- size_t cumulative_block_size = coinbase_blob_size;
- //process transactions
- if(!add_transaction_from_block(bl.miner_tx, coinbase_hash, id, get_current_blockchain_height(), coinbase_blob_size))
- {
- LOG_PRINT_L1("Block with id: " << id << " failed to add transaction to blockchain storage");
- bvc.m_verifivation_failed = true;
- return false;
- }
- size_t tx_processed_count = 0;
- uint64_t fee_summary = 0;
- BOOST_FOREACH(const crypto::hash& tx_id, bl.tx_hashes)
- {
- transaction tx;
- size_t blob_size = 0;
- uint64_t fee = 0;
- bool relayed = false;
- if(!m_tx_pool->take_tx(tx_id, tx, blob_size, fee, relayed))
- {
- LOG_PRINT_L1("Block with id: " << id << "has at least one unknown transaction with id: " << tx_id);
- purge_block_data_from_blockchain(bl, tx_processed_count);
- //add_block_as_invalid(bl, id);
- bvc.m_verifivation_failed = true;
- return false;
- }
- if(!check_tx_inputs(tx))
- {
- LOG_PRINT_L1("Block with id: " << id << "has at least one transaction (id: " << tx_id << ") with wrong inputs.");
- cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
- bool add_res = m_tx_pool->add_tx(tx, tvc, true, relayed, 1);
- CHECK_AND_ASSERT_MES2(add_res, "handle_block_to_main_chain: failed to add transaction back to transaction pool");
- purge_block_data_from_blockchain(bl, tx_processed_count);
- add_block_as_invalid(bl, id);
- LOG_PRINT_L1("Block with id " << id << " added as invalid becouse of wrong inputs in transactions");
- bvc.m_verifivation_failed = true;
- return false;
- }
-
- if(!add_transaction_from_block(tx, tx_id, id, get_current_blockchain_height(), blob_size))
- {
- LOG_PRINT_L1("Block with id: " << id << " failed to add transaction to blockchain storage");
- cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
- bool add_res = m_tx_pool->add_tx(tx, tvc, true, relayed, 1);
- CHECK_AND_ASSERT_MES2(add_res, "handle_block_to_main_chain: failed to add transaction back to transaction pool");
- purge_block_data_from_blockchain(bl, tx_processed_count);
- bvc.m_verifivation_failed = true;
- return false;
- }
- fee_summary += fee;
- cumulative_block_size += blob_size;
- ++tx_processed_count;
- }
- uint64_t base_reward = 0;
- uint64_t already_generated_coins = m_blocks.size() ? m_blocks.back().already_generated_coins:0;
- if(!validate_miner_transaction(bl, cumulative_block_size, fee_summary, base_reward, already_generated_coins))
- {
- LOG_PRINT_L1("Block with id: " << id
- << " has incorrect miner transaction");
- purge_block_data_from_blockchain(bl, tx_processed_count);
- bvc.m_verifivation_failed = true;
- return false;
- }
-
-
- block_extended_info bei = boost::value_initialized<block_extended_info>();
- bei.bl = bl;
- bei.block_cumulative_size = cumulative_block_size;
- bei.cumulative_difficulty = current_diffic;
-
- // In the "tail" state when the minimum subsidy (implemented in get_block_reward) is in effect, the number of
- // coins will eventually exceed MONEY_SUPPLY and overflow a uint64. To prevent overflow, cap already_generated_coins
- // at MONEY_SUPPLY. already_generated_coins is only used to compute the block subsidy and MONEY_SUPPLY yields a
- // subsidy of 0 under the base formula and therefore the minimum subsidy >0 in the tail state.
-
- bei.already_generated_coins = base_reward < (MONEY_SUPPLY-already_generated_coins) ? already_generated_coins + base_reward : MONEY_SUPPLY;
-
- if(m_blocks.size())
- bei.cumulative_difficulty += m_blocks.back().cumulative_difficulty;
-
- bei.height = m_blocks.size();
-
- auto ind_res = m_blocks_index.insert(std::pair<crypto::hash, size_t>(id, bei.height));
- if(!ind_res.second)
- {
- LOG_PRINT_L1("block with id: " << id << " already in block indexes");
- purge_block_data_from_blockchain(bl, tx_processed_count);
- bvc.m_verifivation_failed = true;
- return false;
- }
-
- m_blocks.push_back(bei);
- update_next_comulative_size_limit();
- TIME_MEASURE_FINISH(block_processing_time);
- LOG_PRINT_L1("+++++ BLOCK SUCCESSFULLY ADDED" << ENDL << "id:\t" << id
- << ENDL << "PoW:\t" << proof_of_work
- << ENDL << "HEIGHT " << bei.height << ", difficulty:\t" << current_diffic
- << ENDL << "block reward: " << print_money(fee_summary + base_reward) << "(" << print_money(base_reward) << " + " << print_money(fee_summary)
- << "), coinbase_blob_size: " << coinbase_blob_size << ", cumulative size: " << cumulative_block_size
- << ", " << block_processing_time << "("<< target_calculating_time << "/" << longhash_calculating_time << ")ms");
-
- epee::net_utils::data_logger::get_instance().add_data("blockchain_processing_time", block_processing_time);
-
- bvc.m_added_to_main_chain = true;
- /*if(!m_orphanes_reorganize_in_work)
- review_orphaned_blocks_with_new_block_id(id, true);*/
-
- m_tx_pool->on_blockchain_inc(bei.height, id);
- //LOG_PRINT_L0("BLOCK: " << ENDL << "" << dump_obj_as_json(bei.bl));
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::update_next_comulative_size_limit()
-{
- std::vector<size_t> sz;
- get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
-
- uint64_t median = epee::misc_utils::median(sz);
- if(median <= CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1)
- median = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1;
-
- m_current_block_cumul_sz_limit = median*2;
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::add_new_block(const block& bl_, block_verification_context& bvc)
-{
- //copy block here to let modify block.target
- block bl = bl_;
- crypto::hash id = get_block_hash(bl);
- CRITICAL_REGION_LOCAL(*m_tx_pool);//to avoid deadlock lets lock tx_pool for whole add/reorganize process
- CRITICAL_REGION_LOCAL1(m_blockchain_lock);
- if(have_block(id))
- {
- LOG_PRINT_L3("block with id = " << id << " already exists");
- bvc.m_already_exists = true;
- return false;
- }
-
- //check that block refers to chain tail
- if(!(bl.prev_id == get_tail_id()))
- {
- //chain switching or wrong block
- bvc.m_added_to_main_chain = false;
- return handle_alternative_block(bl, id, bvc);
- //never relay alternative blocks
- }
-
- return handle_block_to_main_chain(bl, id, bvc);
-}
-//------------------------------------------------------------------
-void blockchain_storage::check_against_checkpoints(const checkpoints& points, bool enforce)
-{
- const auto& pts = points.get_points();
-
- for (const auto& pt : pts)
- {
- // if the checkpoint is for a block we don't have yet, move on
- if (pt.first >= m_blocks.size())
- {
- continue;
- }
-
- if (!points.check_block(pt.first, get_block_hash(m_blocks[pt.first].bl)))
- {
- // if asked to enforce checkpoints, roll back to a couple of blocks before the checkpoint
- if (enforce)
- {
- LOG_ERROR("Local blockchain failed to pass a checkpoint, rolling back!");
- std::list<block> empty;
- rollback_blockchain_switching(empty, pt.first - 2);
- }
- else
- {
- LOG_ERROR("WARNING: local blockchain failed to pass a MoneroPulse checkpoint, and you could be on a fork. You should either sync up from scratch, OR download a fresh blockchain bootstrap, OR enable checkpoint enforcing with the --enforce-dns-checkpointing command-line option");
- }
- }
- }
-}
-//------------------------------------------------------------------
-// returns false if any of the checkpoints loading returns false.
-// That should happen only if a checkpoint is added that conflicts
-// with an existing checkpoint.
-bool blockchain_storage::update_checkpoints(const std::string& file_path, bool check_dns)
-{
- if (!m_checkpoints.load_checkpoints_from_json(file_path))
- {
- return false;
- }
-
- // if we're checking both dns and json, load checkpoints from dns.
- // if we're not hard-enforcing dns checkpoints, handle accordingly
- if (m_enforce_dns_checkpoints && check_dns)
- {
- if (!m_checkpoints.load_checkpoints_from_dns())
- {
- return false;
- }
- }
- else if (check_dns)
- {
- checkpoints dns_points;
- dns_points.load_checkpoints_from_dns(m_testnet);
- if (m_checkpoints.check_for_conflicts(dns_points))
- {
- check_against_checkpoints(dns_points, false);
- }
- else
- {
- LOG_PRINT_L0("One or more checkpoints fetched from DNS conflicted with existing checkpoints!");
- }
- }
-
- check_against_checkpoints(m_checkpoints, true);
-
- return true;
-}
-//------------------------------------------------------------------
-void blockchain_storage::set_enforce_dns_checkpoints(bool enforce_checkpoints)
-{
- m_enforce_dns_checkpoints = enforce_checkpoints;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::flush_txes_from_pool(const std::list<crypto::hash> &txids)
-{
- bool res = true;
- for (const auto &txid: txids)
- {
- cryptonote::transaction tx;
- size_t blob_size;
- uint64_t fee;
- bool relayed;
- LOG_PRINT_L1("Removing txid " << txid << " from the pool");
- if(!m_tx_pool->take_tx(txid, tx, blob_size, fee, relayed))
- {
- LOG_PRINT_L0("Failed to remove txid " << txid << " from the pool");
- res = false;
- }
- }
- return res;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::for_all_key_images(std::function<bool(const crypto::key_image&)> f) const
-{
- for (key_images_container::const_iterator i = m_spent_keys.begin(); i != m_spent_keys.end(); ++i) {
- if (!f(*i))
- return false;
- }
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::for_all_blocks(std::function<bool(uint64_t, const crypto::hash&, const block&)> f) const
-{
- for (blocks_container::const_iterator i = m_blocks.begin(); i != m_blocks.end(); ++i) {
- crypto::hash hash;
- get_block_hash (i->bl, hash);
- if (!f(i->height, hash, i->bl))
- return false;
- }
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::for_all_transactions(std::function<bool(const crypto::hash&, const transaction&)> f) const
-{
- for (transactions_container::const_iterator i = m_transactions.begin(); i != m_transactions.end(); ++i) {
- if (!f(i->first, i->second.tx))
- return false;
- }
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::for_all_outputs(std::function<bool(uint64_t, const crypto::hash&, size_t)> f) const
-{
- for (outputs_container::const_iterator i = m_outputs.begin(); i != m_outputs.end(); ++i) {
- for (size_t n = 0; n < i->second.size(); ++n) {
- if (!f(i->first, i->second[n].first, i->second[n].second))
- return false;
- }
- }
- return true;
-}
diff --git a/src/cryptonote_core/blockchain_storage.h b/src/cryptonote_core/blockchain_storage.h
deleted file mode 100644
index 878202cf1..000000000
--- a/src/cryptonote_core/blockchain_storage.h
+++ /dev/null
@@ -1,377 +0,0 @@
-// Copyright (c) 2014-2016, The Monero Project
-//
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without modification, are
-// permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this list of
-// conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice, this list
-// of conditions and the following disclaimer in the documentation and/or other
-// materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its contributors may be
-// used to endorse or promote products derived from this software without specific
-// prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
-// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
-// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
-// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
-
-#pragma once
-#include <boost/serialization/serialization.hpp>
-#include <boost/serialization/version.hpp>
-#include <boost/serialization/list.hpp>
-#include <boost/multi_index_container.hpp>
-#include <boost/multi_index/global_fun.hpp>
-#include <boost/multi_index/hashed_index.hpp>
-#include <boost/multi_index/member.hpp>
-#include <boost/foreach.hpp>
-#include <atomic>
-
-#include "syncobj.h"
-#include "string_tools.h"
-#include "tx_pool.h"
-#include "cryptonote_basic.h"
-#include "common/util.h"
-#include "cryptonote_protocol/cryptonote_protocol_defs.h"
-#include "rpc/core_rpc_server_commands_defs.h"
-#include "difficulty.h"
-#include "cryptonote_core/cryptonote_format_utils.h"
-#include "verification_context.h"
-#include "crypto/hash.h"
-#include "checkpoints.h"
-
-namespace cryptonote
-{
-
- /************************************************************************/
- /* */
- /************************************************************************/
- class blockchain_storage
- {
- public:
- struct transaction_chain_entry
- {
- transaction tx;
- uint64_t m_keeper_block_height;
- size_t m_blob_size;
- std::vector<uint64_t> m_global_output_indexes;
- };
-
- struct block_extended_info
- {
- block bl;
- uint64_t height;
- size_t block_cumulative_size;
- difficulty_type cumulative_difficulty;
- uint64_t already_generated_coins;
- };
-
- blockchain_storage(tx_memory_pool* tx_pool):m_tx_pool(tx_pool), m_current_block_cumul_sz_limit(0), m_is_in_checkpoint_zone(false), m_is_blockchain_storing(false), m_enforce_dns_checkpoints(false)
- {};
-
- bool init() { return init(tools::get_default_data_dir(), true); }
- bool init(const std::string& config_folder, bool testnet = false);
- bool deinit();
-
- void set_checkpoints(checkpoints&& chk_pts) { m_checkpoints = chk_pts; }
-
- //bool push_new_block();
- bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs) const;
- bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const;
- bool get_alternative_blocks(std::list<block>& blocks) const;
- size_t get_alternative_blocks_count() const;
- crypto::hash get_block_id_by_height(uint64_t height) const;
- bool get_block_by_hash(const crypto::hash &h, block &blk) const;
- void get_all_known_block_ids(std::list<crypto::hash> &main, std::list<crypto::hash> &alt, std::list<crypto::hash> &invalid) const;
-
- template<class archive_t>
- void serialize(archive_t & ar, const unsigned int version);
-
- bool have_tx(const crypto::hash &id) const;
- bool have_tx_keyimges_as_spent(const transaction &tx) const;
- bool have_tx_keyimg_as_spent(const crypto::key_image &key_im) const;
- const transaction *get_tx(const crypto::hash &id) const;
-
- uint64_t get_current_blockchain_height() const;
- crypto::hash get_tail_id() const;
- crypto::hash get_tail_id(uint64_t& height) const;
- difficulty_type get_difficulty_for_next_block() const;
- bool add_new_block(const block& bl_, block_verification_context& bvc);
- bool reset_and_set_genesis_block(const block& b);
- bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, const blobdata& ex_nonce) const;
- bool have_block(const crypto::hash& id) const;
- size_t get_total_transactions() const;
- bool get_outs(uint64_t amount, std::list<crypto::public_key>& pkeys) const;
- bool get_short_chain_history(std::list<crypto::hash>& ids) const;
- bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const;
- bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, uint64_t& starter_offset) const;
- bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const;
- bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp);
- bool handle_get_objects(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res);
- bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const;
- bool get_backward_blocks_sizes(size_t from_height, std::vector<size_t>& sz, size_t count) const;
- bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs) const;
- bool store_blockchain();
-
- bool check_tx_inputs(const transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id) const;
- uint64_t get_current_cumulative_blocksize_limit() const;
- bool is_storing_blockchain()const{return m_is_blockchain_storing;}
- uint64_t block_difficulty(size_t i) const;
- double get_avg_block_size( size_t count) const;
- bool flush_txes_from_pool(const std::list<crypto::hash> &txids);
-
- template<class t_ids_container, class t_blocks_container, class t_missed_container>
- bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) const
- {
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
-
- BOOST_FOREACH(const auto& bl_id, block_ids)
- {
- auto it = m_blocks_index.find(bl_id);
- if(it == m_blocks_index.end())
- missed_bs.push_back(bl_id);
- else
- {
- CHECK_AND_ASSERT_MES(it->second < m_blocks.size(), false, "Internal error: bl_id=" << epee::string_tools::pod_to_hex(bl_id)
- << " have index record with offset="<<it->second<< ", bigger then m_blocks.size()=" << m_blocks.size());
- blocks.push_back(m_blocks[it->second].bl);
- }
- }
- return true;
- }
-
- template<class t_ids_container, class t_tx_container, class t_missed_container>
- bool get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const
- {
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
-
- BOOST_FOREACH(const auto& tx_id, txs_ids)
- {
- auto it = m_transactions.find(tx_id);
- if(it == m_transactions.end())
- {
- transaction tx;
- if(!m_tx_pool->get_transaction(tx_id, tx))
- missed_txs.push_back(tx_id);
- else
- txs.push_back(tx);
- }
- else
- txs.push_back(it->second.tx);
- }
- return true;
- }
- //debug functions
- void print_blockchain(uint64_t start_index, uint64_t end_index) const;
- void print_blockchain_index() const;
- void print_blockchain_outs(const std::string& file) const;
- void check_against_checkpoints(const checkpoints& points, bool enforce);
- bool update_checkpoints(const std::string& file_path, bool check_dns);
- void set_enforce_dns_checkpoints(bool enforce_checkpoints);
-
- block get_block(uint64_t height) const { return m_blocks[height].bl; }
- size_t get_block_size(uint64_t height) const { return m_blocks[height].block_cumulative_size; }
- difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return m_blocks[height].cumulative_difficulty; }
- uint64_t get_block_coins_generated(uint64_t height) const { return m_blocks[height].already_generated_coins; }
-
- bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const;
- bool for_all_blocks(std::function<bool(uint64_t height, const crypto::hash&, const block&)>) const;
- bool for_all_transactions(std::function<bool(const crypto::hash&, const transaction&)>) const;
- bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, size_t tx_idx)>) const;
-
- // use for testing only
- bool debug_pop_block_from_blockchain() { return pop_block_from_blockchain(); }
-
- private:
- typedef std::unordered_map<crypto::hash, size_t> blocks_by_id_index;
- typedef std::unordered_map<crypto::hash, transaction_chain_entry> transactions_container;
- typedef std::unordered_set<crypto::key_image> key_images_container;
- typedef std::vector<block_extended_info> blocks_container;
- typedef std::unordered_map<crypto::hash, block_extended_info> blocks_ext_by_hash;
- typedef std::unordered_map<crypto::hash, block> blocks_by_hash;
- typedef std::map<uint64_t, std::vector<std::pair<crypto::hash, size_t>>> outputs_container; //crypto::hash - tx hash, size_t - index of out in transaction
-
- tx_memory_pool* m_tx_pool;
- mutable epee::critical_section m_blockchain_lock; // TODO: add here reader/writer lock
-
- // main chain
- blocks_container m_blocks; // height -> block_extended_info
- blocks_by_id_index m_blocks_index; // crypto::hash -> height
- transactions_container m_transactions;
- key_images_container m_spent_keys;
- size_t m_current_block_cumul_sz_limit;
-
-
- // all alternative chains
- blocks_ext_by_hash m_alternative_chains; // crypto::hash -> block_extended_info
-
- // some invalid blocks
- blocks_ext_by_hash m_invalid_blocks; // crypto::hash -> block_extended_info
- outputs_container m_outputs;
-
-
- std::string m_config_folder;
- checkpoints m_checkpoints;
- std::atomic<bool> m_is_in_checkpoint_zone;
- std::atomic<bool> m_is_blockchain_storing;
-
- bool m_enforce_dns_checkpoints;
- bool m_testnet;
-
- // made private for consistency with blockchain.h
- template<class visitor_t>
- bool scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height = NULL) const;
- bool check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t* pmax_related_block_height = NULL) const;
- bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height = NULL) const;
- bool check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height = NULL) const;
-
- bool switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::iterator>& alt_chain, bool discard_disconnected_chain);
- bool pop_block_from_blockchain();
- bool purge_block_data_from_blockchain(const block& b, size_t processed_tx_count);
- bool purge_transaction_from_blockchain(const crypto::hash& tx_id);
- bool purge_transaction_keyimages_from_blockchain(const transaction& tx, bool strict_check);
-
- bool handle_block_to_main_chain(const block& bl, block_verification_context& bvc);
- bool handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc);
- bool handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc);
- difficulty_type get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::iterator>& alt_chain, block_extended_info& bei) const;
- bool prevalidate_miner_transaction(const block& b, uint64_t height) const;
- bool validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins) const;
- bool validate_transaction(const block& b, uint64_t height, const transaction& tx) const;
- bool rollback_blockchain_switching(std::list<block>& original_chain, size_t rollback_height);
- bool add_transaction_from_block(const transaction& tx, const crypto::hash& tx_id, const crypto::hash& bl_id, uint64_t bl_height, size_t blob_size);
- bool push_transaction_to_global_outs_index(const transaction& tx, const crypto::hash& tx_id, std::vector<uint64_t>& global_indexes);
- bool pop_transaction_from_global_index(const transaction& tx, const crypto::hash& tx_id);
- bool get_last_n_blocks_sizes(std::vector<size_t>& sz, size_t count) const;
- bool add_out_to_get_random_outs(const std::vector<std::pair<crypto::hash, size_t> >& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) const;
- bool is_tx_spendtime_unlocked(uint64_t unlock_time) const;
- bool add_block_as_invalid(const block& bl, const crypto::hash& h);
- bool add_block_as_invalid(const block_extended_info& bei, const crypto::hash& h);
- size_t find_end_of_allowed_index(const std::vector<std::pair<crypto::hash, size_t> >& amount_outs) const;
- bool check_block_timestamp_main(const block& b) const;
- bool check_block_timestamp(std::vector<uint64_t> timestamps, const block& b) const;
- uint64_t get_adjusted_time() const;
- bool complete_timestamps_vector(uint64_t start_height, std::vector<uint64_t>& timestamps) const;
- bool update_next_comulative_size_limit();
- bool store_genesis_block(bool testnet, bool check_added = false);
- };
-
-
- /************************************************************************/
- /* */
- /************************************************************************/
-
- #define CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER 12
-
- template<class archive_t>
- void blockchain_storage::serialize(archive_t & ar, const unsigned int version)
- {
- if(version < 11)
- return;
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- ar & m_blocks;
- ar & m_blocks_index;
- ar & m_transactions;
- ar & m_spent_keys;
- ar & m_alternative_chains;
- ar & m_outputs;
- ar & m_invalid_blocks;
- ar & m_current_block_cumul_sz_limit;
- /*serialization bug workaround*/
- if(version > 11)
- {
- uint64_t total_check_count = m_blocks.size() + m_blocks_index.size() + m_transactions.size() + m_spent_keys.size() + m_alternative_chains.size() + m_outputs.size() + m_invalid_blocks.size() + m_current_block_cumul_sz_limit;
- if(archive_t::is_saving::value)
- {
- ar & total_check_count;
- }else
- {
- uint64_t total_check_count_loaded = 0;
- ar & total_check_count_loaded;
- if(total_check_count != total_check_count_loaded)
- {
- LOG_ERROR("Blockchain storage data corruption detected. total_count loaded from file = " << total_check_count_loaded << ", expected = " << total_check_count);
-
- LOG_PRINT_L0("Blockchain storage:" << ENDL <<
- "m_blocks: " << m_blocks.size() << ENDL <<
- "m_blocks_index: " << m_blocks_index.size() << ENDL <<
- "m_transactions: " << m_transactions.size() << ENDL <<
- "m_spent_keys: " << m_spent_keys.size() << ENDL <<
- "m_alternative_chains: " << m_alternative_chains.size() << ENDL <<
- "m_outputs: " << m_outputs.size() << ENDL <<
- "m_invalid_blocks: " << m_invalid_blocks.size() << ENDL <<
- "m_current_block_cumul_sz_limit: " << m_current_block_cumul_sz_limit);
-
- throw std::runtime_error("Blockchain data corruption");
- }
- }
- }
-
-
- LOG_PRINT_L2("Blockchain storage:" << ENDL <<
- "m_blocks: " << m_blocks.size() << ENDL <<
- "m_blocks_index: " << m_blocks_index.size() << ENDL <<
- "m_transactions: " << m_transactions.size() << ENDL <<
- "m_spent_keys: " << m_spent_keys.size() << ENDL <<
- "m_alternative_chains: " << m_alternative_chains.size() << ENDL <<
- "m_outputs: " << m_outputs.size() << ENDL <<
- "m_invalid_blocks: " << m_invalid_blocks.size() << ENDL <<
- "m_current_block_cumul_sz_limit: " << m_current_block_cumul_sz_limit);
- }
-
- //------------------------------------------------------------------
- template<class visitor_t>
- bool blockchain_storage::scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height) const
- {
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- auto it = m_outputs.find(tx_in_to_key.amount);
- if(it == m_outputs.end() || !tx_in_to_key.key_offsets.size())
- return false;
-
- std::vector<uint64_t> absolute_offsets = relative_output_offsets_to_absolute(tx_in_to_key.key_offsets);
-
-
- const std::vector<std::pair<crypto::hash, size_t> >& amount_outs_vec = it->second;
- size_t count = 0;
- BOOST_FOREACH(uint64_t i, absolute_offsets)
- {
- if(i >= amount_outs_vec.size() )
- {
- LOG_PRINT_L0("Wrong index in transaction inputs: " << i << ", expected maximum " << amount_outs_vec.size() - 1);
- return false;
- }
- transactions_container::const_iterator tx_it = m_transactions.find(amount_outs_vec[i].first);
- CHECK_AND_ASSERT_MES(tx_it != m_transactions.end(), false, "Wrong transaction id in output indexes: " << epee::string_tools::pod_to_hex(amount_outs_vec[i].first));
- CHECK_AND_ASSERT_MES(amount_outs_vec[i].second < tx_it->second.tx.vout.size(), false,
- "Wrong index in transaction outputs: " << amount_outs_vec[i].second << ", expected less then " << tx_it->second.tx.vout.size());
- if(!vis.handle_output(tx_it->second.tx, tx_it->second.tx.vout[amount_outs_vec[i].second]))
- {
- LOG_PRINT_L0("Failed to handle_output for output no = " << count << ", with absolute offset " << i);
- return false;
- }
- if(count++ == absolute_offsets.size()-1 && pmax_related_block_height)
- {
- if(*pmax_related_block_height < tx_it->second.m_keeper_block_height)
- *pmax_related_block_height = tx_it->second.m_keeper_block_height;
- }
- }
-
- return true;
- }
-}
-
-
-
-BOOST_CLASS_VERSION(cryptonote::blockchain_storage, CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER)
diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_core/cryptonote_basic.h
index ed62e26f4..f54b8c2b3 100644
--- a/src/cryptonote_core/cryptonote_basic.h
+++ b/src/cryptonote_core/cryptonote_basic.h
@@ -49,9 +49,7 @@
#include "crypto/hash.h"
#include "misc_language.h"
#include "tx_extra.h"
-
-#define DB_MEMORY 1
-#define DB_LMDB 2
+#include "ringct/rctTypes.h"
namespace cryptonote
{
@@ -175,14 +173,14 @@ namespace cryptonote
BEGIN_SERIALIZE()
VARINT_FIELD(version)
- if(CURRENT_TRANSACTION_VERSION < version) return false;
+ if(version == 0 || CURRENT_TRANSACTION_VERSION < version) return false;
VARINT_FIELD(unlock_time)
FIELD(vin)
FIELD(vout)
FIELD(extra)
END_SERIALIZE()
- protected:
+ public:
transaction_prefix(){}
};
@@ -190,6 +188,7 @@ namespace cryptonote
{
public:
std::vector<std::vector<crypto::signature> > signatures; //count signatures always the same as inputs count
+ rct::rctSig rct_signatures;
transaction();
virtual ~transaction();
@@ -198,34 +197,59 @@ namespace cryptonote
BEGIN_SERIALIZE_OBJECT()
FIELDS(*static_cast<transaction_prefix *>(this))
- ar.tag("signatures");
- ar.begin_array();
- PREPARE_CUSTOM_VECTOR_SERIALIZATION(vin.size(), signatures);
- bool signatures_not_expected = signatures.empty();
- if (!signatures_not_expected && vin.size() != signatures.size())
- return false;
-
- for (size_t i = 0; i < vin.size(); ++i)
+ if (version == 1)
{
- size_t signature_size = get_signature_size(vin[i]);
- if (signatures_not_expected)
+ ar.tag("signatures");
+ ar.begin_array();
+ PREPARE_CUSTOM_VECTOR_SERIALIZATION(vin.size(), signatures);
+ bool signatures_not_expected = signatures.empty();
+ if (!signatures_not_expected && vin.size() != signatures.size())
+ return false;
+
+ for (size_t i = 0; i < vin.size(); ++i)
{
- if (0 == signature_size)
- continue;
- else
+ size_t signature_size = get_signature_size(vin[i]);
+ if (signatures_not_expected)
+ {
+ if (0 == signature_size)
+ continue;
+ else
+ return false;
+ }
+
+ PREPARE_CUSTOM_VECTOR_SERIALIZATION(signature_size, signatures[i]);
+ if (signature_size != signatures[i].size())
return false;
- }
-
- PREPARE_CUSTOM_VECTOR_SERIALIZATION(signature_size, signatures[i]);
- if (signature_size != signatures[i].size())
- return false;
- FIELDS(signatures[i]);
+ FIELDS(signatures[i]);
- if (vin.size() - i > 1)
- ar.delimit_array();
+ if (vin.size() - i > 1)
+ ar.delimit_array();
+ }
+ ar.end_array();
+ }
+ else
+ {
+ FIELD(rct_signatures)
+ switch (rct_signatures.type)
+ {
+ case rct::RCTTypeNull:
+ break;
+ case rct::RCTTypeSimple:
+ if (rct_signatures.mixRing.size() && rct_signatures.mixRing.size() != vin.size())
+ return false;
+ break;
+ case rct::RCTTypeFull:
+ for (size_t i = 0; i < rct_signatures.mixRing.size(); ++i)
+ {
+ if (rct_signatures.mixRing[i].size() != vin.size())
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
}
- ar.end_array();
END_SERIALIZE()
private:
@@ -248,12 +272,13 @@ namespace cryptonote
inline
void transaction::set_null()
{
- version = 0;
+ version = 1;
unlock_time = 0;
vin.clear();
vout.clear();
extra.clear();
signatures.clear();
+ rct_signatures.type = rct::RCTTypeNull;
}
inline
diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h
index 79ceec5bc..c91f78c58 100644
--- a/src/cryptonote_core/cryptonote_boost_serialization.h
+++ b/src/cryptonote_core/cryptonote_boost_serialization.h
@@ -37,9 +37,13 @@
#include <boost/serialization/map.hpp>
#include <boost/foreach.hpp>
#include <boost/serialization/is_bitwise_serializable.hpp>
+#include <boost/archive/binary_iarchive.hpp>
+#include <boost/archive/binary_oarchive.hpp>
#include "cryptonote_basic.h"
#include "common/unordered_containers_boost_serialization.h"
#include "crypto/crypto.h"
+#include "ringct/rctTypes.h"
+#include "ringct/rctOps.h"
//namespace cryptonote {
namespace boost
@@ -140,6 +144,16 @@ namespace boost
template <class Archive>
+ inline void serialize(Archive &a, cryptonote::transaction_prefix &x, const boost::serialization::version_type ver)
+ {
+ a & x.version;
+ a & x.unlock_time;
+ a & x.vin;
+ a & x.vout;
+ a & x.extra;
+ }
+
+ template <class Archive>
inline void serialize(Archive &a, cryptonote::transaction &x, const boost::serialization::version_type ver)
{
a & x.version;
@@ -147,7 +161,10 @@ namespace boost
a & x.vin;
a & x.vout;
a & x.extra;
- a & x.signatures;
+ if (x.version == 1)
+ a & x.signatures;
+ else
+ a & x.rct_signatures;
}
@@ -163,6 +180,107 @@ namespace boost
a & b.miner_tx;
a & b.tx_hashes;
}
+
+ template <class Archive>
+ inline void serialize(Archive &a, rct::key &x, const boost::serialization::version_type ver)
+ {
+ a & reinterpret_cast<char (&)[sizeof(rct::key)]>(x);
+ }
+
+ template <class Archive>
+ inline void serialize(Archive &a, rct::ctkey &x, const boost::serialization::version_type ver)
+ {
+ a & x.dest;
+ a & x.mask;
+ }
+
+ template <class Archive>
+ inline void serialize(Archive &a, rct::rangeSig &x, const boost::serialization::version_type ver)
+ {
+ a & x.asig;
+ a & x.Ci;
+ }
+
+ template <class Archive>
+ inline void serialize(Archive &a, rct::asnlSig &x, const boost::serialization::version_type ver)
+ {
+ a & x.L1;
+ a & x.s2;
+ a & x.s;
+ }
+
+ template <class Archive>
+ inline void serialize(Archive &a, rct::mgSig &x, const boost::serialization::version_type ver)
+ {
+ a & x.ss;
+ a & x.cc;
+ // a & x.II; // not serialized, we can recover it from the tx vin
+ }
+
+ template <class Archive>
+ inline void serialize(Archive &a, rct::ecdhTuple &x, const boost::serialization::version_type ver)
+ {
+ a & x.mask;
+ a & x.amount;
+ // a & x.senderPk; // not serialized, as we do not use it in monero currently
+ }
+
+ inline void serializeOutPk(boost::archive::binary_iarchive &a, rct::ctkeyV &outPk_, const boost::serialization::version_type ver)
+ {
+ rct::keyV outPk;
+ a & outPk;
+ outPk_.resize(outPk.size());
+ for (size_t n = 0; n < outPk_.size(); ++n)
+ {
+ outPk_[n].dest = rct::identity();
+ outPk_[n].mask = outPk[n];
+ }
+ }
+
+ inline void serializeOutPk(boost::archive::binary_oarchive &a, rct::ctkeyV &outPk_, const boost::serialization::version_type ver)
+ {
+ rct::keyV outPk(outPk_.size());
+ for (size_t n = 0; n < outPk_.size(); ++n)
+ outPk[n] = outPk_[n].mask;
+ a & outPk;
+ }
+
+ template <class Archive>
+ inline void serialize(Archive &a, rct::rctSigBase &x, const boost::serialization::version_type ver)
+ {
+ a & x.type;
+ if (x.type == rct::RCTTypeNull)
+ return;
+ if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple)
+ throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type");
+ // a & x.message; message is not serialized, as it can be reconstructed from the tx data
+ // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets
+ if (x.type == rct::RCTTypeSimple)
+ a & x.pseudoOuts;
+ a & x.ecdhInfo;
+ serializeOutPk(a, x.outPk, ver);
+ a & x.txnFee;
+ }
+
+ template <class Archive>
+ inline void serialize(Archive &a, rct::rctSig &x, const boost::serialization::version_type ver)
+ {
+ a & x.type;
+ if (x.type == rct::RCTTypeNull)
+ return;
+ if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple)
+ throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type");
+ // a & x.message; message is not serialized, as it can be reconstructed from the tx data
+ // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets
+ if (x.type == rct::RCTTypeSimple)
+ a & x.pseudoOuts;
+ a & x.ecdhInfo;
+ serializeOutPk(a, x.outPk, ver);
+ a & x.txnFee;
+ //--------------
+ a & x.p.rangeSigs;
+ a & x.p.MGs;
+ }
}
}
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 657291a75..73edde1b7 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -43,6 +43,7 @@ using namespace epee;
#include "misc_language.h"
#include <csignal>
#include "cryptonote_core/checkpoints.h"
+#include "ringct/rctTypes.h"
#include "blockchain_db/blockchain_db.h"
#include "blockchain_db/lmdb/db_lmdb.h"
#if defined(BERKELEY_DB)
@@ -57,11 +58,7 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
core::core(i_cryptonote_protocol* pprotocol):
m_mempool(m_blockchain_storage),
-#if BLOCKCHAIN_DB == DB_LMDB
m_blockchain_storage(m_mempool),
-#else
- m_blockchain_storage(&m_mempool),
-#endif
m_miner(this),
m_miner_address(boost::value_initialized<account_public_address>()),
m_starter_message_showed(false),
@@ -257,7 +254,6 @@ namespace cryptonote
r = m_mempool.init(m_fakechain ? std::string() : m_config_folder);
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool");
-#if BLOCKCHAIN_DB == DB_LMDB
std::string db_type = command_line::get_arg(vm, command_line::arg_db_type);
std::string db_sync_mode = command_line::get_arg(vm, command_line::arg_db_sync_mode);
bool fast_sync = command_line::get_arg(vm, command_line::arg_fast_block_sync) != 0;
@@ -406,9 +402,6 @@ namespace cryptonote
bool show_time_stats = command_line::get_arg(vm, command_line::arg_show_time_stats) != 0;
m_blockchain_storage.set_show_time_stats(show_time_stats);
-#else
- r = m_blockchain_storage.init(m_config_folder, m_testnet);
-#endif
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage");
// load json & DNS checkpoints, and verify them
@@ -506,6 +499,15 @@ namespace cryptonote
}
//std::cout << "!"<< tx.vin.size() << std::endl;
+ uint8_t version = m_blockchain_storage.get_current_hard_fork_version();
+ const size_t max_tx_version = version == 1 ? 1 : 2;
+ if (tx.version == 0 || tx.version > max_tx_version)
+ {
+ // v2 is the latest one we know
+ tvc.m_verifivation_failed = true;
+ return false;
+ }
+
if(!check_tx_syntax(tx))
{
LOG_PRINT_L1("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " syntax, rejected");
@@ -561,6 +563,14 @@ namespace cryptonote
LOG_PRINT_RED_L1("tx with invalid outputs, rejected for tx id= " << get_transaction_hash(tx));
return false;
}
+ if (tx.version > 1)
+ {
+ if (tx.rct_signatures.outPk.size() != tx.vout.size())
+ {
+ LOG_PRINT_RED_L1("tx with mismatched vout/outPk count, rejected for tx id= " << get_transaction_hash(tx));
+ return false;
+ }
+ }
if(!check_money_overflow(tx))
{
@@ -568,15 +578,19 @@ namespace cryptonote
return false;
}
- uint64_t amount_in = 0;
- get_inputs_money_amount(tx, amount_in);
- uint64_t amount_out = get_outs_money_amount(tx);
-
- if(amount_in <= amount_out)
+ if (tx.version == 1)
{
- LOG_PRINT_RED_L1("tx with wrong amounts: ins " << amount_in << ", outs " << amount_out << ", rejected for tx id= " << get_transaction_hash(tx));
- return false;
+ uint64_t amount_in = 0;
+ get_inputs_money_amount(tx, amount_in);
+ uint64_t amount_out = get_outs_money_amount(tx);
+
+ if(amount_in <= amount_out)
+ {
+ LOG_PRINT_RED_L1("tx with wrong amounts: ins " << amount_in << ", outs " << amount_out << ", rejected for tx id= " << get_transaction_hash(tx));
+ return false;
+ }
}
+ // for version > 1, ringct signatures check verifies amounts match
if(!keeped_by_block && get_object_blobsize(tx) >= m_blockchain_storage.get_current_cumulative_blocksize_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE)
{
@@ -715,6 +729,11 @@ namespace cryptonote
return m_blockchain_storage.get_outs(req, res);
}
//-----------------------------------------------------------------------------------------------
+ bool core::get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const
+ {
+ return m_blockchain_storage.get_random_rct_outs(req, res);
+ }
+ //-----------------------------------------------------------------------------------------------
bool core::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs) const
{
return m_blockchain_storage.get_tx_outputs_gindexs(tx_id, indexs);
@@ -781,18 +800,14 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
bool core::prepare_handle_incoming_blocks(const std::list<block_complete_entry> &blocks)
{
-#if BLOCKCHAIN_DB == DB_LMDB
m_blockchain_storage.prepare_handle_incoming_blocks(blocks);
-#endif
return true;
}
//-----------------------------------------------------------------------------------------------
bool core::cleanup_handle_incoming_blocks(bool force_sync)
{
-#if BLOCKCHAIN_DB == DB_LMDB
m_blockchain_storage.cleanup_handle_incoming_blocks(force_sync);
-#endif
return true;
}
@@ -919,11 +934,6 @@ namespace cryptonote
m_starter_message_showed = true;
}
-#if BLOCKCHAIN_DB == DB_LMDB
- // m_store_blockchain_interval.do_call(boost::bind(&Blockchain::store_blockchain, &m_blockchain_storage));
-#else
- m_store_blockchain_interval.do_call(boost::bind(&blockchain_storage::store_blockchain, &m_blockchain_storage));
-#endif
m_fork_moaner.do_call(boost::bind(&core::check_fork_time, this));
m_txpool_auto_relayer.do_call(boost::bind(&core::relay_txpool_transactions, this));
m_miner.on_idle();
@@ -933,7 +943,6 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
bool core::check_fork_time()
{
-#if BLOCKCHAIN_DB == DB_LMDB
HardFork::State state = m_blockchain_storage.get_hard_fork_state();
switch (state) {
case HardFork::LikelyForked:
@@ -952,7 +961,6 @@ namespace cryptonote
default:
break;
}
-#endif
return true;
}
//-----------------------------------------------------------------------------------------------
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 63969bc23..d16bd6553 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -40,11 +40,7 @@
#include "cryptonote_protocol/cryptonote_protocol_handler_common.h"
#include "storages/portable_storage_template_helper.h"
#include "tx_pool.h"
-#if BLOCKCHAIN_DB == DB_LMDB
#include "blockchain.h"
-#else
-#include "blockchain_storage.h"
-#endif
#include "miner.h"
#include "connection_context.h"
#include "cryptonote_core/cryptonote_stat_info.h"
@@ -483,6 +479,14 @@ namespace cryptonote
*/
bool get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res) const;
+ /**
+ *
+ * @copydoc Blockchain::get_random_rct_outs
+ *
+ * @note see Blockchain::get_random_rct_outs
+ */
+ bool get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const;
+
/**
* @copydoc miner::pause
@@ -498,7 +502,6 @@ namespace cryptonote
*/
void resume_mine();
-#if BLOCKCHAIN_DB == DB_LMDB
/**
* @brief gets the Blockchain instance
*
@@ -512,10 +515,6 @@ namespace cryptonote
* @return a const reference to the Blockchain instance
*/
const Blockchain& get_blockchain_storage()const{return m_blockchain_storage;}
-#else
- blockchain_storage& get_blockchain_storage(){return m_blockchain_storage;}
- const blockchain_storage& get_blockchain_storage()const{return m_blockchain_storage;}
-#endif
/**
* @copydoc Blockchain::print_blockchain
@@ -765,11 +764,7 @@ namespace cryptonote
uint64_t m_test_drop_download_height = 0; //!< height under which to drop incoming blocks, if doing so
tx_memory_pool m_mempool; //!< transaction pool instance
-#if BLOCKCHAIN_DB == DB_LMDB
Blockchain m_blockchain_storage; //!< Blockchain instance
-#else
- blockchain_storage m_blockchain_storage;
-#endif
i_cryptonote_protocol* m_pprotocol; //!< cryptonote protocol instance
diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp
index ff752ae47..32b96f1fc 100644
--- a/src/cryptonote_core/cryptonote_format_utils.cpp
+++ b/src/cryptonote_core/cryptonote_format_utils.cpp
@@ -37,6 +37,7 @@ using namespace epee;
#include "miner.h"
#include "crypto/crypto.h"
#include "crypto/hash.h"
+#include "ringct/rctSigs.h"
#define ENCRYPTED_PAYMENT_ID_TAIL 0x8d
@@ -100,7 +101,7 @@ namespace cryptonote
CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob");
//TODO: validate tx
- crypto::cn_fast_hash(tx_blob.data(), tx_blob.size(), tx_hash);
+ get_transaction_hash(tx, tx_hash);
get_transaction_prefix_hash(tx, tx_prefix_hash);
return true;
}
@@ -135,7 +136,10 @@ namespace cryptonote
// 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
- if (hard_fork_version >= 2) {
+ // 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;
}
@@ -145,12 +149,16 @@ namespace cryptonote
[&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)
+ 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[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.resize(out_amounts.size() - 1);
}
}
@@ -181,7 +189,11 @@ namespace cryptonote
CHECK_AND_ASSERT_MES(summary_amounts == block_reward, false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal block_reward = " << block_reward);
- tx.version = CURRENT_TRANSACTION_VERSION;
+ 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);
@@ -252,6 +264,11 @@ namespace cryptonote
//---------------------------------------------------------------
bool get_tx_fee(const transaction& tx, uint64_t & fee)
{
+ if (tx.version > 1)
+ {
+ fee = tx.rct_signatures.txnFee;
+ return true;
+ }
uint64_t amount_in = 0;
uint64_t amount_out = 0;
BOOST_FOREACH(auto& in, tx.vin)
@@ -315,6 +332,11 @@ namespace cryptonote
return pub_key_field.pub_key;
}
//---------------------------------------------------------------
+ crypto::public_key get_tx_pub_key_from_extra(const transaction_prefix& tx_prefix)
+ {
+ return get_tx_pub_key_from_extra(tx_prefix.extra);
+ }
+ //---------------------------------------------------------------
crypto::public_key get_tx_pub_key_from_extra(const transaction& tx)
{
return get_tx_pub_key_from_extra(tx.extra);
@@ -447,13 +469,16 @@ namespace cryptonote
return encrypt_payment_id(payment_id, public_key, secret_key);
}
//---------------------------------------------------------------
- bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key)
+ bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct)
{
+ std::vector<crypto::secret_key> amount_keys;
tx.vin.clear();
tx.vout.clear();
tx.signatures.clear();
+ tx.rct_signatures = rct::rctSig();
+ amount_keys.clear();
- tx.version = CURRENT_TRANSACTION_VERSION;
+ tx.version = rct ? 2 : 1;
tx.unlock_time = unlock_time;
tx.extra = extra;
@@ -509,7 +534,6 @@ namespace cryptonote
};
std::vector<input_generation_context_data> in_contexts;
-
uint64_t summary_inputs_money = 0;
//fill inputs
BOOST_FOREACH(const tx_source_entry& src_entr, sources)
@@ -529,7 +553,7 @@ namespace cryptonote
return false;
//check that derivated key is equal with real output key
- if( !(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second) )
+ if( !(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second.dest) )
{
LOG_ERROR("derived public key missmatch with output public key! "<< ENDL << "derived_key:"
<< string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:"
@@ -559,12 +583,18 @@ namespace cryptonote
size_t output_index = 0;
BOOST_FOREACH(const tx_destination_entry& dst_entr, shuffled_dsts)
{
- CHECK_AND_ASSERT_MES(dst_entr.amount > 0, false, "Destination with wrong amount: " << dst_entr.amount);
+ CHECK_AND_ASSERT_MES(dst_entr.amount > 0 || tx.version > 1, false, "Destination with wrong amount: " << dst_entr.amount);
crypto::key_derivation derivation;
crypto::public_key out_eph_public_key;
bool r = crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, txkey.sec, derivation);
CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_view_public_key << ", " << txkey.sec << ")");
+ if (tx.version > 1)
+ {
+ crypto::secret_key scalar1;
+ crypto::derivation_to_scalar(derivation, output_index, scalar1);
+ amount_keys.push_back(scalar1);
+ }
r = crypto::derive_public_key(derivation, output_index, dst_entr.addr.m_spend_public_key, out_eph_public_key);
CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to derive_public_key(" << derivation << ", " << output_index << ", "<< dst_entr.addr.m_spend_public_key << ")");
@@ -586,33 +616,148 @@ namespace cryptonote
}
- //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;
- BOOST_FOREACH(const tx_source_entry& src_entr, sources)
+ if (tx.version == 1)
{
- ss_ring_s << "pub_keys:" << ENDL;
- std::vector<const crypto::public_key*> keys_ptrs;
- BOOST_FOREACH(const tx_source_entry::output_entry& o, src_entr.outputs)
+ //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;
+ BOOST_FOREACH(const tx_source_entry& src_entr, sources)
{
- keys_ptrs.push_back(&o.second);
- ss_ring_s << o.second << ENDL;
+ 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;
+ BOOST_FOREACH(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());
+ 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;
+ i++;
}
- tx.signatures.push_back(std::vector<crypto::signature>());
- std::vector<crypto::signature>& sigs = tx.signatures.back();
- sigs.resize(src_entr.outputs.size());
- 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;
- i++;
+ LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str() , LOG_LEVEL_3);
}
+ else
+ {
+ bool all_rct_inputs = true;
+ size_t n_total_outs = sources[0].outputs.size(); // only for non-simple rct
+ BOOST_FOREACH(const tx_source_entry& src_entr, sources)
+ all_rct_inputs &= !(src_entr.mask == rct::identity());
- LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str() , LOG_LEVEL_3);
+ // 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;
+
+ if (!use_simple_rct)
+ {
+ // non simple ringct requires all real inputs to be at the same index for all inputs
+ BOOST_FOREACH(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 mixin");
+ return false;
+ }
+ }
+ }
+
+ uint64_t amount_in = 0, amount_out = 0;
+ rct::ctkeyV inSk;
+ // 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;
+ 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);
+ // inPk: (public key, commitment)
+ // will be done when filling in mixRing
+ }
+ 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);
+ 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, (const rct::keyV&)amount_keys, index, outSk);
+ else
+ tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, (const rct::keyV&)amount_keys, sources[0].real_output, outSk); // same index assumption
+
+ CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout");
+
+ LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL, LOG_LEVEL_3);
+ }
return true;
}
@@ -661,7 +806,10 @@ namespace cryptonote
<< out.target.type().name() << ", expected " << typeid(txout_to_key).name()
<< ", in transaction id=" << get_transaction_hash(tx));
- CHECK_AND_NO_ASSERT_MES(0 < out.amount, false, "zero amount ouput in transaction id=" << get_transaction_hash(tx));
+ if (tx.version == 1)
+ {
+ CHECK_AND_NO_ASSERT_MES(0 < out.amount, false, "zero amount output in transaction id=" << get_transaction_hash(tx));
+ }
if(!check_key(boost::get<txout_to_key>(out.target).key))
return false;
@@ -776,20 +924,49 @@ namespace cryptonote
crypto::hash get_transaction_hash(const transaction& t)
{
crypto::hash h = null_hash;
- size_t blob_size = 0;
- get_object_hash(t, h, blob_size);
+ get_transaction_hash(t, h, NULL);
return h;
}
//---------------------------------------------------------------
bool get_transaction_hash(const transaction& t, crypto::hash& res)
{
- size_t blob_size = 0;
- return get_object_hash(t, res, blob_size);
+ return get_transaction_hash(t, res, NULL);
+ }
+ //---------------------------------------------------------------
+ bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size)
+ {
+ // v1 transactions hash the entire blob
+ if (t.version == 1)
+ {
+ size_t ignored_blob_size, &blob_size_ref = blob_size ? *blob_size : ignored_blob_size;
+ return get_object_hash(t, res, blob_size_ref);
+ }
+
+ // v2 transactions hash different parts together, than hash the set of those hashes
+ crypto::hash hashes[3];
+
+ // prefix
+ get_transaction_prefix_hash(t, hashes[0]);
+
+ // base rct data
+ get_blob_hash(t_serializable_object_to_blob((const rct::rctSigBase&)t.rct_signatures), hashes[1]);
+
+ // prunable rct data
+ get_blob_hash(t_serializable_object_to_blob(t.rct_signatures.p), hashes[2]);
+
+ // the tx hash is the hash of the 3 hashes
+ res = cn_fast_hash(hashes, sizeof(hashes));
+
+ // we still need the size
+ if (blob_size)
+ *blob_size = get_object_blobsize(t);
+
+ return true;
}
//---------------------------------------------------------------
bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size)
{
- return get_object_hash(t, res, blob_size);
+ return get_transaction_hash(t, res, &blob_size);
}
//---------------------------------------------------------------
blobdata get_block_hashing_blob(const block& b)
diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h
index 056940e98..e6a3bfba4 100644
--- a/src/cryptonote_core/cryptonote_format_utils.h
+++ b/src/cryptonote_core/cryptonote_format_utils.h
@@ -35,6 +35,7 @@
#include "include_base_utils.h"
#include "crypto/crypto.h"
#include "crypto/hash.h"
+#include "ringct/rctOps.h"
namespace cryptonote
@@ -50,13 +51,17 @@ namespace cryptonote
struct tx_source_entry
{
- typedef std::pair<uint64_t, crypto::public_key> output_entry;
+ typedef std::pair<uint64_t, rct::ctkey> output_entry;
- std::vector<output_entry> outputs; //index + key
+ std::vector<output_entry> outputs; //index + key + optional ringct commitment
size_t real_output; //index in outputs vector of real output_entry
crypto::public_key real_out_tx_key; //incoming real tx public key
size_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
+
+ 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)}))); }
};
struct tx_destination_entry
@@ -70,7 +75,7 @@ namespace cryptonote
//---------------------------------------------------------------
bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time);
- bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &txkey);
+ bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct = false);
template<typename T>
bool find_tx_extra_field_by_type(const std::vector<tx_extra_field>& tx_extra_fields, T& field)
@@ -85,6 +90,7 @@ namespace cryptonote
bool parse_tx_extra(const std::vector<uint8_t>& tx_extra, std::vector<tx_extra_field>& tx_extra_fields);
crypto::public_key get_tx_pub_key_from_extra(const std::vector<uint8_t>& tx_extra);
+ crypto::public_key get_tx_pub_key_from_extra(const transaction_prefix& tx);
crypto::public_key get_tx_pub_key_from_extra(const transaction& tx);
bool add_tx_pub_key_to_extra(transaction& tx, const crypto::public_key& tx_pub_key);
bool add_extra_nonce_to_tx_extra(std::vector<uint8_t>& tx_extra, const blobdata& extra_nonce);
@@ -106,6 +112,7 @@ namespace cryptonote
crypto::hash get_transaction_hash(const transaction& t);
bool get_transaction_hash(const transaction& t, crypto::hash& res);
bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size);
+ bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size);
blobdata get_block_hashing_blob(const block& b);
bool get_block_hash(const block& b, crypto::hash& res);
crypto::hash get_block_hash(const block& b);
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index 3d5ab86e1..46fab4dcf 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -37,11 +37,7 @@
#include "cryptonote_format_utils.h"
#include "cryptonote_boost_serialization.h"
#include "cryptonote_config.h"
-#if BLOCKCHAIN_DB == DB_LMDB
#include "blockchain.h"
-#else
-#include "blockchain_storage.h"
-#endif
#include "common/boost_serialization_helper.h"
#include "common/int-util.h"
#include "misc_language.h"
@@ -74,21 +70,22 @@ namespace cryptonote
}
}
//---------------------------------------------------------------------------------
-#if BLOCKCHAIN_DB == DB_LMDB
//---------------------------------------------------------------------------------
tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs)
{
}
-#else
- tx_memory_pool::tx_memory_pool(blockchain_storage& bchs): m_blockchain(bchs)
- {
-
- }
-#endif
//---------------------------------------------------------------------------------
bool tx_memory_pool::add_tx(const transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool kept_by_block, bool relayed, uint8_t version)
{
+ if (tx.version == 0)
+ {
+ // v0 never accepted
+ LOG_PRINT_L1("transaction version 0 is invalid");
+ tvc.m_verifivation_failed = true;
+ return false;
+ }
+
// we do not accept transactions that timed out before, unless they're
// kept_by_block
if (!kept_by_block && m_timed_out_transactions.find(id) != m_timed_out_transactions.end())
@@ -106,25 +103,34 @@ namespace cryptonote
return false;
}
- uint64_t inputs_amount = 0;
- if(!get_inputs_money_amount(tx, inputs_amount))
+ // fee per kilobyte, size rounded up.
+ uint64_t fee;
+
+ if (tx.version == 1)
{
- tvc.m_verifivation_failed = true;
- return false;
- }
+ uint64_t inputs_amount = 0;
+ if(!get_inputs_money_amount(tx, inputs_amount))
+ {
+ tvc.m_verifivation_failed = true;
+ return false;
+ }
- uint64_t outputs_amount = get_outs_money_amount(tx);
+ uint64_t outputs_amount = get_outs_money_amount(tx);
+ if(outputs_amount >= inputs_amount)
+ {
+ LOG_PRINT_L1("transaction use more money then it has: use " << print_money(outputs_amount) << ", have " << print_money(inputs_amount));
+ tvc.m_verifivation_failed = true;
+ tvc.m_overspend = true;
+ return false;
+ }
- if(outputs_amount >= inputs_amount)
+ fee = inputs_amount - outputs_amount;
+ }
+ else
{
- LOG_PRINT_L1("transaction use more money then it has: use " << print_money(outputs_amount) << ", have " << print_money(inputs_amount));
- tvc.m_verifivation_failed = true;
- tvc.m_overspend = true;
- return false;
+ fee = tx.rct_signatures.txnFee;
}
- // fee per kilobyte, size rounded up.
- uint64_t fee = inputs_amount - outputs_amount;
uint64_t needed_fee = blob_size / 1024;
needed_fee += (blob_size % 1024) ? 1 : 0;
needed_fee *= FEE_PER_KB;
@@ -161,7 +167,7 @@ namespace cryptonote
if (!m_blockchain.check_tx_outputs(tx, tvc))
{
- LOG_PRINT_L1("Transaction with id= "<< id << " has at least one invalid outout");
+ LOG_PRINT_L1("Transaction with id= "<< id << " has at least one invalid output");
tvc.m_verifivation_failed = true;
tvc.m_invalid_output = true;
return false;
@@ -169,11 +175,9 @@ namespace cryptonote
crypto::hash max_used_block_id = null_hash;
uint64_t max_used_block_height = 0;
-#if BLOCKCHAIN_DB == DB_LMDB
- bool ch_inp_res = m_blockchain.check_tx_inputs(tx, max_used_block_height, max_used_block_id, tvc, kept_by_block);
-#else
- bool ch_inp_res = m_blockchain.check_tx_inputs(tx, max_used_block_height, max_used_block_id);
-#endif
+ tx_details txd;
+ txd.tx = tx;
+ bool ch_inp_res = m_blockchain.check_tx_inputs(txd.tx, max_used_block_height, max_used_block_id, tvc, kept_by_block);
CRITICAL_REGION_LOCAL(m_transactions_lock);
if(!ch_inp_res)
{
@@ -181,11 +185,10 @@ namespace cryptonote
// may become valid again, so ignore the failed inputs check.
if(kept_by_block)
{
- auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details()));
+ auto txd_p = m_transactions.insert(transactions_container::value_type(id, txd));
CHECK_AND_ASSERT_MES(txd_p.second, false, "transaction already exists at inserting in memory pool");
txd_p.first->second.blob_size = blob_size;
- txd_p.first->second.tx = tx;
- txd_p.first->second.fee = inputs_amount - outputs_amount;
+ txd_p.first->second.fee = fee;
txd_p.first->second.max_used_block_id = null_hash;
txd_p.first->second.max_used_block_height = 0;
txd_p.first->second.kept_by_block = kept_by_block;
@@ -203,12 +206,11 @@ namespace cryptonote
}else
{
//update transactions container
- auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details()));
+ auto txd_p = m_transactions.insert(transactions_container::value_type(id, txd));
CHECK_AND_ASSERT_MES(txd_p.second, false, "intrnal error: transaction already exists at inserting in memorypool");
txd_p.first->second.blob_size = blob_size;
- txd_p.first->second.tx = tx;
txd_p.first->second.kept_by_block = kept_by_block;
- txd_p.first->second.fee = inputs_amount - outputs_amount;
+ txd_p.first->second.fee = fee;
txd_p.first->second.max_used_block_id = max_used_block_id;
txd_p.first->second.max_used_block_height = max_used_block_height;
txd_p.first->second.last_failed_height = 0;
@@ -518,12 +520,8 @@ namespace cryptonote
if(txd.last_failed_id == m_blockchain.get_block_id_by_height(txd.last_failed_height))
return false;
//check ring signature again, it is possible (with very small chance) that this transaction become again valid
-#if BLOCKCHAIN_DB == DB_LMDB
tx_verification_context tvc;
if(!m_blockchain.check_tx_inputs(txd.tx, txd.max_used_block_height, txd.max_used_block_id, tvc))
-#else
- if(!m_blockchain.check_tx_inputs(txd.tx, txd.max_used_block_height, txd.max_used_block_id))
-#endif
{
txd.last_failed_height = m_blockchain.get_current_blockchain_height()-1;
txd.last_failed_id = m_blockchain.get_block_id_by_height(txd.last_failed_height);
@@ -649,6 +647,7 @@ namespace cryptonote
//---------------------------------------------------------------------------------
size_t tx_memory_pool::validate(uint8_t version)
{
+ CRITICAL_REGION_LOCAL(m_transactions_lock);
size_t n_removed = 0;
size_t tx_size_limit = (version < 2 ? TRANSACTION_SIZE_LIMIT_V1 : TRANSACTION_SIZE_LIMIT_V2);
for (auto it = m_transactions.begin(); it != m_transactions.end(); ) {
diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h
index c7aab7f08..0e280872d 100644
--- a/src/cryptonote_core/tx_pool.h
+++ b/src/cryptonote_core/tx_pool.h
@@ -48,11 +48,7 @@
namespace cryptonote
{
-#if BLOCKCHAIN_DB == DB_LMDB
class Blockchain;
-#else
- class blockchain_storage;
-#endif
/************************************************************************/
/* */
/************************************************************************/
@@ -93,16 +89,12 @@ namespace cryptonote
class tx_memory_pool: boost::noncopyable
{
public:
-#if BLOCKCHAIN_DB == DB_LMDB
/**
* @brief Constructor
*
* @param bchs a Blockchain class instance, for getting chain info
*/
tx_memory_pool(Blockchain& bchs);
-#else
- tx_memory_pool(blockchain_storage& bchs);
-#endif
/**
@@ -492,18 +484,7 @@ namespace cryptonote
std::unordered_set<crypto::hash> m_timed_out_transactions;
std::string m_config_folder; //!< the folder to save state to
-#if BLOCKCHAIN_DB == DB_LMDB
Blockchain& m_blockchain; //!< reference to the Blockchain object
-#else
- blockchain_storage& m_blockchain;
-#endif
-
-#if BLOCKCHAIN_DB == DB_LMDB
-#else
-#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
- friend class blockchain_storage;
-#endif
-#endif
};
}
diff --git a/src/cryptonote_core/verification_context.h b/src/cryptonote_core/verification_context.h
index e58291ea9..0bb635e84 100644
--- a/src/cryptonote_core/verification_context.h
+++ b/src/cryptonote_core/verification_context.h
@@ -47,6 +47,7 @@ namespace cryptonote
bool m_too_big;
bool m_overspend;
bool m_fee_too_low;
+ bool m_not_rct;
};
struct block_verification_context