diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 7c9bd9163..674e4a529 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -647,7 +647,7 @@ block Blockchain::pop_block_from_blockchain() // that might not be always true. Unlikely though, and always relaying // these again might cause a spike of traffic as many nodes re-relay // all the transactions in a popped block when a reorg happens. - bool r = m_tx_pool.add_tx(tx, tvc, relay_method::block, true, version); + bool r = m_tx_pool.add_tx(tx, tvc, relay_method::block, true, version, true); if (!r) { LOG_ERROR("Error returning transaction to tx_pool"); @@ -3937,6 +3937,47 @@ bool Blockchain::is_tx_spendtime_unlocked(uint64_t unlock_time, uint8_t hf_versi } return false; } + +bool Blockchain::get_maximum_block_id_height(transaction& tx, uint8_t version, uint64_t* pmax_used_block_height, crypto::hash* pmax_used_block_id) +{ + + crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx); + std::vector> pubkeys(tx.vin.size()); + size_t sig_index = 0; + + for (const auto& txin : tx.vin) + { + CHECK_AND_ASSERT_MES(txin.type() == typeid(txin_to_key), false, "wrong type id in tx input at Blockchain::check_tx_inputs"); + const txin_to_key& in_to_key = boost::get(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 (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); + } + + // make sure that output being spent matches up correctly with the + // signature spending it. + if (!check_tx_input(tx.version, in_to_key, tx_prefix_hash, tx.version == 1 ? tx.signatures[sig_index] : std::vector(), tx.rct_signatures, pubkeys[sig_index], pmax_used_block_height, version)) + { + // MERROR_VER("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() + // { + // MERROR_VER(" *pmax_used_block_height: " << *pmax_used_block_height); + // } + + return false; + } + + sig_index++; + } + + *pmax_used_block_id = m_db->get_block_hash_from_height(*pmax_used_block_height); + return true; +} + //------------------------------------------------------------------ // This function locates all outputs associated with a given input (mixins) // and validates that they exist and are usable. It also checks the ring diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index be2848d09..79ca0ec26 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -31,6 +31,7 @@ #pragma once #include #include +#include #if BOOST_VERSION >= 107400 #include #endif @@ -205,6 +206,16 @@ namespace cryptonote */ size_t get_alternative_blocks_count() const; + /** + * @brief get maximum used block height and id of transaction + * + * @param tx the transaction to get maximum input height + * @param version version of the hardfork + * @max_used_block_id return-by-reference block hash of most recent input + * @max_used_block_height return-by-reference return-by-reference block hash of most recent input + */ + bool get_maximum_block_id_height(transaction& tx, uint8_t version, uint64_t* pmax_used_block_height, crypto::hash* pmax_used_block_id); + /** * @brief gets a block's hash given a height * @@ -617,6 +628,7 @@ namespace cryptonote */ 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) const; + /** * @brief get fee quantization mask * diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index d86a9f5f9..111309185 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include @@ -140,7 +141,7 @@ namespace cryptonote // corresponding lists. } //--------------------------------------------------------------------------------- - bool tx_memory_pool::add_tx(transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, relay_method tx_relay, bool relayed, uint8_t version) + bool tx_memory_pool::add_tx(transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, relay_method tx_relay, bool relayed, uint8_t version, bool already_verified) { const bool kept_by_block = (tx_relay == relay_method::block); @@ -265,8 +266,13 @@ namespace cryptonote crypto::hash max_used_block_id = null_hash; uint64_t max_used_block_height = 0; + + if(already_verified) { + m_blockchain.get_maximum_block_id_height(tx, version, &max_used_block_height, &max_used_block_id); + } + cryptonote::txpool_tx_meta_t meta{}; - bool ch_inp_res = check_tx_inputs([&tx]()->cryptonote::transaction&{ return tx; }, id, max_used_block_height, max_used_block_id, tvc, kept_by_block); + bool ch_inp_res = already_verified ? true : check_tx_inputs([&tx]()->cryptonote::transaction&{ return tx; }, id, max_used_block_height, max_used_block_id, tvc, kept_by_block); if(!ch_inp_res) { // if the transaction was valid before (kept_by_block), then it @@ -396,14 +402,14 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::add_tx(transaction &tx, tx_verification_context& tvc, relay_method tx_relay, bool relayed, uint8_t version) + bool tx_memory_pool::add_tx(transaction &tx, tx_verification_context& tvc, relay_method tx_relay, bool relayed, uint8_t version, bool already_verified) { crypto::hash h = null_hash; cryptonote::blobdata bl; t_serializable_object_to_blob(tx, bl); if (bl.size() == 0 || !get_transaction_hash(tx, h)) return false; - return add_tx(tx, h, bl, get_transaction_weight(tx, bl.size()), tvc, tx_relay, relayed, version); + return add_tx(tx, h, bl, get_transaction_weight(tx, bl.size()), tvc, tx_relay, relayed, version, already_verified); } //--------------------------------------------------------------------------------- size_t tx_memory_pool::get_txpool_weight() const diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 3bb96d3a8..00f8bac13 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -104,7 +104,7 @@ namespace cryptonote * @tx_relay how the transaction was received * @param tx_weight the transaction's weight */ - bool add_tx(transaction &tx, const crypto::hash &id, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, relay_method tx_relay, bool relayed, uint8_t version); + bool add_tx(transaction &tx, const crypto::hash &id, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, relay_method tx_relay, bool relayed, uint8_t version, bool already_verified = false); /** * @brief add a transaction to the transaction pool @@ -122,7 +122,7 @@ namespace cryptonote * * @return true if the transaction passes validations, otherwise false */ - bool add_tx(transaction &tx, tx_verification_context& tvc, relay_method tx_relay, bool relayed, uint8_t version); + bool add_tx(transaction &tx, tx_verification_context& tvc, relay_method tx_relay, bool relayed, uint8_t version, bool already_verified = false); /** * @brief takes a transaction with the given hash from the pool