From 83b2f119d2f4a31875390a5c2472d29f767ff092 Mon Sep 17 00:00:00 2001 From: Leny Kholodov Date: Fri, 19 Apr 2019 13:33:04 +0300 Subject: [PATCH 01/12] Add block hash to blockchain_based_list request from cryptonode to supernode --- src/cryptonote_core/stake_transaction_processor.cpp | 7 ++++++- src/cryptonote_core/stake_transaction_processor.h | 2 +- src/p2p/net_node.h | 2 +- src/p2p/net_node.inl | 5 +++-- src/rpc/core_rpc_server_commands_defs.h | 2 ++ 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/cryptonote_core/stake_transaction_processor.cpp b/src/cryptonote_core/stake_transaction_processor.cpp index 214ed64ae..51fe6358a 100644 --- a/src/cryptonote_core/stake_transaction_processor.cpp +++ b/src/cryptonote_core/stake_transaction_processor.cpp @@ -461,7 +461,12 @@ void StakeTransactionProcessor::invoke_update_blockchain_based_list_handler_impl uint64_t height = m_blockchain_based_list->block_height(); for (size_t i=0; itiers(i)); + { + uint64_t block_height = height - i; + crypto::hash block_hash = m_blockchain.get_block_id_by_height(block_height); + + m_on_blockchain_based_list_update(block_height, block_hash, m_blockchain_based_list->tiers(i)); + } m_blockchain_based_list_need_update = false; } diff --git a/src/cryptonote_core/stake_transaction_processor.h b/src/cryptonote_core/stake_transaction_processor.h index 6d1ce1d4f..26222cd58 100644 --- a/src/cryptonote_core/stake_transaction_processor.h +++ b/src/cryptonote_core/stake_transaction_processor.h @@ -35,7 +35,7 @@ class StakeTransactionProcessor void invoke_update_stakes_handler(bool force = true); typedef BlockchainBasedList::supernode_tier_array supernode_tier_array; - typedef std::function blockchain_based_list_update_handler; + typedef std::function blockchain_based_list_update_handler; /// Update handler for new blockchain based list void set_on_update_blockchain_based_list_handler(const blockchain_based_list_update_handler&); diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 63e4e33a8..a72adf31e 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -418,7 +418,7 @@ namespace nodetool private: void handle_stakes_update(uint64_t block_number, const cryptonote::StakeTransactionProcessor::supernode_stake_array& stakes); - void handle_blockchain_based_list_update(uint64_t block_number, const cryptonote::StakeTransactionProcessor::supernode_tier_array& tiers); + void handle_blockchain_based_list_update(uint64_t block_number, const crypto::hash& block_hash, const cryptonote::StakeTransactionProcessor::supernode_tier_array& tiers); private: std::multimap m_supernode_requests_timestamps; diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 027b1ab7a..ee5e8e3a1 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -551,7 +551,7 @@ namespace nodetool ); m_payload_handler.get_core().set_update_blockchain_based_list_handler( - [&](uint64_t block_height, const cryptonote::StakeTransactionProcessor::supernode_tier_array& tiers) { handle_blockchain_based_list_update(block_height, tiers); } + [&](uint64_t block_height, const crypto::hash& block_hash, const cryptonote::StakeTransactionProcessor::supernode_tier_array& tiers) { handle_blockchain_based_list_update(block_height, block_hash, tiers); } ); std::set full_addrs; @@ -3146,7 +3146,7 @@ namespace nodetool } template - void node_server::handle_blockchain_based_list_update(uint64_t block_height, const cryptonote::StakeTransactionProcessor::supernode_tier_array& tiers) + void node_server::handle_blockchain_based_list_update(uint64_t block_height, const crypto::hash& block_hash, const cryptonote::StakeTransactionProcessor::supernode_tier_array& tiers) { static std::string supernode_endpoint("blockchain_based_list"); @@ -3160,6 +3160,7 @@ namespace nodetool cryptonote::COMMAND_RPC_SUPERNODE_BLOCKCHAIN_BASED_LIST::request request; request.block_height = block_height; + request.block_hash = epee::string_tools::pod_to_hex(block_hash); for (size_t i=0; i tiers; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(block_height) + KV_SERIALIZE(block_hash) KV_SERIALIZE(tiers) END_KV_SERIALIZE_MAP() }; From f0ed05faed7069b7653079eabd57dff6e7d0b8c7 Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Mon, 6 May 2019 02:21:01 +0300 Subject: [PATCH 02/12] Disqualification transaction support added. --- src/cryptonote_basic/cryptonote_basic.h | 2 +- .../cryptonote_format_utils.cpp | 34 +++++++++++++ .../cryptonote_format_utils.h | 3 ++ src/cryptonote_basic/tx_extra.h | 48 ++++++++++++++++++- src/cryptonote_core/cryptonote_core.cpp | 10 +++- src/cryptonote_core/tx_pool.cpp | 14 +++++- 6 files changed, 106 insertions(+), 5 deletions(-) diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index 8af60ccc5..ce687ed10 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -170,7 +170,7 @@ namespace cryptonote BEGIN_SERIALIZE() VARINT_FIELD(version) - if (version == 0 || CURRENT_TRANSACTION_VERSION < version) return false; + if (version == 0 || (version != 123 && CURRENT_TRANSACTION_VERSION < version)) return false; VARINT_FIELD(unlock_time) FIELD(vin) FIELD(vout) diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 97f34f214..4792363e2 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -1325,4 +1325,38 @@ namespace cryptonote return false; return ::serialization::parse_binary(rta_signatures_data.data, rta_signatures); } + + bool graft_is_disqualification(const transaction &tx) + { + if(tx.version != 123) return false; + std::vector tx_extra_fields; + parse_tx_extra(tx.extra, tx_extra_fields); + tx_extra_graft_disqualification disq; + if(!find_tx_extra_field_by_type(tx_extra_fields, disq)) + return false; + return true; + } + + bool graft_check_disqualification(const transaction &tx) + { + if(tx.version != 123) return false; + if(!tx.vin.empty() || !tx.vout.empty() || tx.rct_signatures.txnFee !=0) return false; + std::vector tx_extra_fields; + parse_tx_extra(tx.extra, tx_extra_fields); + tx_extra_graft_disqualification disq; + if(!find_tx_extra_field_by_type(tx_extra_fields, disq)) + return false; + {//check signs + std::string item_str; + ::serialization::dump_binary(disq.item, item_str); + crypto::hash hash; + crypto::cn_fast_hash(item_str.data(), item_str.size(), hash); + for(auto& si : disq.signers) + { + if(!crypto::check_signature(hash, si.signer_id, si.sign)) return false; + } + } + return true; + } + } diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index 67dcb208c..1d87b8f77 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -166,6 +166,9 @@ namespace cryptonote */ bool get_graft_rta_signatures_from_extra2(const transaction& tx, std::vector &rta_signatures); + bool graft_is_disqualification(const transaction &tx); + bool graft_check_disqualification(const transaction &tx); + bool add_extra_nonce_to_tx_extra(std::vector& tx_extra, const blobdata& extra_nonce); bool remove_field_from_tx_extra(std::vector& tx_extra, const std::type_info &type); void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id); diff --git a/src/cryptonote_basic/tx_extra.h b/src/cryptonote_basic/tx_extra.h index 38e6af4ef..3a9d01e61 100644 --- a/src/cryptonote_basic/tx_extra.h +++ b/src/cryptonote_basic/tx_extra.h @@ -46,6 +46,7 @@ #define TX_EXTRA_GRAFT_TX_SECRET_KEY_TAG 0x81 #define TX_EXTRA_GRAFT_RTA_HEADER_TAG 0x83 #define TX_EXTRA_GRAFT_RTA_SIGNATURES_TAG 0x84 +#define TX_EXTRA_GRAFT_DISQUALIFICATION_TAG 0x85 #define TX_EXTRA_MYSTERIOUS_MINERGATE_TAG 0xDE #define TX_EXTRA_NONCE_PAYMENT_ID 0x00 @@ -229,6 +230,49 @@ namespace cryptonote END_SERIALIZE() }; + struct tx_extra_graft_signer_item + { + uint64_t block_height; + crypto::hash block_hash; + crypto::public_key id; + BEGIN_SERIALIZE() + FIELD(block_height) + FIELD(block_hash) + FIELD(id) + END_SERIALIZE() + }; + + struct tx_extra_graft_disqualification + { + struct disqualification_item + { + uint64_t block_height; + crypto::hash block_hash; + crypto::public_key id; + BEGIN_SERIALIZE() + FIELD(block_height) + FIELD(block_hash) + FIELD(id) + END_SERIALIZE() + }; + + struct signer_item + { + crypto::public_key signer_id; + crypto::signature sign; + BEGIN_SERIALIZE() + FIELD(signer_id) + FIELD(sign) + END_SERIALIZE() + }; + + disqualification_item item; + std::vector signers; + BEGIN_SERIALIZE() + FIELD(item) + FIELD(signers) + END_SERIALIZE() + }; // tx_extra_field format, except tx_extra_padding and tx_extra_pub_key: // varint tag; @@ -236,7 +280,8 @@ namespace cryptonote // varint data[]; typedef boost::variant tx_extra_field; + tx_extra_graft_tx_secret_key, tx_extra_graft_rta_header, tx_extra_graft_rta_signatures, + tx_extra_graft_disqualification> tx_extra_field; } VARIANT_TAG(binary_archive, cryptonote::tx_extra_padding, TX_EXTRA_TAG_PADDING); @@ -250,3 +295,4 @@ VARIANT_TAG(binary_archive, cryptonote::tx_extra_graft_stake_tx, TX_EXTRA_GRAFT_ VARIANT_TAG(binary_archive, cryptonote::tx_extra_graft_tx_secret_key, TX_EXTRA_GRAFT_TX_SECRET_KEY_TAG); VARIANT_TAG(binary_archive, cryptonote::tx_extra_graft_rta_header, TX_EXTRA_GRAFT_RTA_HEADER_TAG); VARIANT_TAG(binary_archive, cryptonote::tx_extra_graft_rta_signatures, TX_EXTRA_GRAFT_RTA_SIGNATURES_TAG); +VARIANT_TAG(binary_archive, cryptonote::tx_extra_graft_disqualification, TX_EXTRA_GRAFT_DISQUALIFICATION_TAG); diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 07b5e782e..54bb200ff 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -705,7 +705,7 @@ namespace cryptonote uint8_t version = m_blockchain_storage.get_current_hard_fork_version(); // don't allow rta tx until hf 13 const size_t max_tx_version = version == 1 ? 1 : version < 13 ? 2 : CURRENT_TRANSACTION_VERSION; - if (tx.version == 0 || tx.version > max_tx_version) + if (tx.version == 0 || (tx.version != 123 && tx.version > max_tx_version)) { // v3 is the latest one we know tvc.m_verifivation_failed = true; @@ -960,6 +960,14 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- bool core::check_tx_semantic(const transaction& tx, bool keeped_by_block) const { + if(tx.version == 123) + { + if(tx.vin.size() || tx.vout.size()) + { + MERROR_VER("qualification tx with non-empty inputs or outputs, rejected for tx id= " << get_transaction_hash(tx)); + } + return graft_is_disqualification(tx); + } if(!tx.vin.size()) { MERROR_VER("tx with empty inputs, rejected for tx id= " << get_transaction_hash(tx)); diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 3e7f66f1a..3403767f9 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -188,8 +188,18 @@ namespace cryptonote // 1. if tx.type == tx_type_rta and tx.rta_signatures.size() > 0 // 2. if tx.version >= 3 and tx.rta_signatures.size() > 0 + bool is_disqualification_tx = (tx.version == 123); bool is_rta_tx = tx.type == transaction::tx_type_rta; - if (is_rta_tx) { + if(is_disqualification_tx) + { + if(!graft_check_disqualification(tx)) + { + MERROR("Invalid disqualification transaction with id " << id); + tvc.m_verifivation_failed = true; + return false; + } + } + else if (is_rta_tx) { cryptonote::rta_header rta_hdr; if (!cryptonote::get_graft_rta_header_from_extra(tx, rta_hdr)) { MERROR("Failed to parse rta-header from tx extra: " << id); @@ -262,7 +272,7 @@ namespace cryptonote crypto::hash max_used_block_id = null_hash; uint64_t max_used_block_height = 0; 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 = is_disqualification_tx || 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 From c850b62a087228339a4da02cfe6dacf6e9b7cecc Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Wed, 8 May 2019 12:21:16 +0300 Subject: [PATCH 03/12] Initial disqualification lookup added. --- .../cryptonote_format_utils.cpp | 32 ++++++++----- .../cryptonote_format_utils.h | 3 +- src/cryptonote_core/blockchain_based_list.cpp | 3 ++ .../stake_transaction_processor.cpp | 32 ++++++++++++- .../stake_transaction_processor.h | 3 +- .../stake_transaction_storage.cpp | 48 +++++++++++++++++-- .../stake_transaction_storage.h | 47 ++++++++++++++++-- src/p2p/net_node.h | 2 +- src/p2p/net_node.inl | 6 ++- src/rpc/core_rpc_server_commands_defs.h | 2 + 10 files changed, 153 insertions(+), 25 deletions(-) diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 4792363e2..9ca625a14 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -1326,25 +1326,29 @@ namespace cryptonote return ::serialization::parse_binary(rta_signatures_data.data, rta_signatures); } + bool graft_get_disqualification(const transaction &tx, tx_extra_graft_disqualification& disq) + { + if(tx.version != 123) + return false; + if(!tx.vin.empty() || !tx.vout.empty() || tx.rct_signatures.txnFee !=0) + return false; + std::vector tx_extra_fields; + parse_tx_extra(tx.extra, tx_extra_fields); + if(!find_tx_extra_field_by_type(tx_extra_fields, disq)) + return false; + return true; + } + bool graft_is_disqualification(const transaction &tx) { - if(tx.version != 123) return false; - std::vector tx_extra_fields; - parse_tx_extra(tx.extra, tx_extra_fields); tx_extra_graft_disqualification disq; - if(!find_tx_extra_field_by_type(tx_extra_fields, disq)) - return false; - return true; + return graft_get_disqualification(tx, disq); } - bool graft_check_disqualification(const transaction &tx) + bool graft_check_disqualification(const transaction &tx, tx_extra_graft_disqualification* pdisq) { - if(tx.version != 123) return false; - if(!tx.vin.empty() || !tx.vout.empty() || tx.rct_signatures.txnFee !=0) return false; - std::vector tx_extra_fields; - parse_tx_extra(tx.extra, tx_extra_fields); tx_extra_graft_disqualification disq; - if(!find_tx_extra_field_by_type(tx_extra_fields, disq)) + if(!graft_get_disqualification(tx, disq)) return false; {//check signs std::string item_str; @@ -1353,9 +1357,11 @@ namespace cryptonote crypto::cn_fast_hash(item_str.data(), item_str.size(), hash); for(auto& si : disq.signers) { - if(!crypto::check_signature(hash, si.signer_id, si.sign)) return false; + if(!crypto::check_signature(hash, si.signer_id, si.sign)) + return false; } } + if(pdisq) *pdisq = std::move(disq); return true; } diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index 1d87b8f77..6650475bc 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -166,8 +166,9 @@ namespace cryptonote */ bool get_graft_rta_signatures_from_extra2(const transaction& tx, std::vector &rta_signatures); + bool graft_get_disqualification(const transaction &tx, tx_extra_graft_disqualification& disq); bool graft_is_disqualification(const transaction &tx); - bool graft_check_disqualification(const transaction &tx); + bool graft_check_disqualification(const transaction &tx, tx_extra_graft_disqualification* pdisq = nullptr); bool add_extra_nonce_to_tx_extra(std::vector& tx_extra, const blobdata& extra_nonce); bool remove_field_from_tx_extra(std::vector& tx_extra, const std::type_info &type); diff --git a/src/cryptonote_core/blockchain_based_list.cpp b/src/cryptonote_core/blockchain_based_list.cpp index ec178d8ac..8e7da95d4 100644 --- a/src/cryptonote_core/blockchain_based_list.cpp +++ b/src/cryptonote_core/blockchain_based_list.cpp @@ -98,6 +98,9 @@ void BlockchainBasedList::apply_block(uint64_t block_height, const crypto::hash& if (stake->tier != i + 1) continue; + if (stake_txs_storage.is_disqualified(block_height, sn.supernode_public_id)) + continue; + prev_supernodes.push_back(sn); } } diff --git a/src/cryptonote_core/stake_transaction_processor.cpp b/src/cryptonote_core/stake_transaction_processor.cpp index 51fe6358a..ff0257cc0 100644 --- a/src/cryptonote_core/stake_transaction_processor.cpp +++ b/src/cryptonote_core/stake_transaction_processor.cpp @@ -2,6 +2,7 @@ #include "stake_transaction_processor.h" #include "../graft_rta_config.h" +#include "serialization/binary_utils.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "staketransaction.processor" @@ -171,12 +172,39 @@ void StakeTransactionProcessor::process_block_stake_transaction(uint64_t block_i MWARNING(" " << tx_hash); } + StakeTransactionStorage::disqualification_array disquals; + for (const transaction& tx : txs) { const crypto::hash tx_hash = get_transaction_prefix_hash(tx); try { + if(tx.version == 123) + { + continue; + tx_extra_graft_disqualification disq_extra; + if(graft_check_disqualification(tx, &disq_extra)) + { + MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash); + continue; + } + + disqualification disq; + ::serialization::dump_binary(disq_extra, disq.blob); + disq.block_index = block_index; + disq.id = disq_extra.item.id; + disq.id_str = epee::string_tools::pod_to_hex(disq.id); + //TODO: check unlock_time + + MDEBUG("New disqualification transaction found at block #" << block_index << ", tx_hash=" << tx_hash << ", supernode_id '" << disq.id_str << "'"); + + disquals.push_back(std::move(disq)); + continue; + } //if(tx.version == 123) + + stake_transaction stake_tx; + if (!get_graft_stake_tx_extra_from_extra(tx, stake_tx.supernode_public_id, stake_tx.supernode_public_address, stake_tx.supernode_signature, stake_tx.tx_secret_key)) continue; @@ -246,6 +274,8 @@ void StakeTransactionProcessor::process_block_stake_transaction(uint64_t block_i } } + m_storage->add_disquals(disquals); + m_stakes_need_update = true; //TODO: cache for stakes //update supernode stakes @@ -413,7 +443,7 @@ void StakeTransactionProcessor::invoke_update_stakes_handler_impl(uint64_t block if (!m_storage) return; - m_on_stakes_update(block_index, m_storage->get_supernode_stakes(block_index)); + m_on_stakes_update(block_index, m_storage->get_supernode_stakes(block_index), m_storage->get_supernode_disqualiications(block_index)); m_stakes_need_update = false; } diff --git a/src/cryptonote_core/stake_transaction_processor.h b/src/cryptonote_core/stake_transaction_processor.h index 26222cd58..aa020bc69 100644 --- a/src/cryptonote_core/stake_transaction_processor.h +++ b/src/cryptonote_core/stake_transaction_processor.h @@ -14,6 +14,7 @@ class StakeTransactionProcessor { public: typedef StakeTransactionStorage::supernode_stake_array supernode_stake_array; + typedef StakeTransactionStorage::supernode_disqualification_array supernode_disqualification_array; StakeTransactionProcessor(Blockchain& blockchain); @@ -26,7 +27,7 @@ class StakeTransactionProcessor /// Synchronize with blockchain void synchronize(); - typedef std::function supernode_stakes_update_handler; + typedef std::function supernode_stakes_update_handler; /// Update handler for new stakes void set_on_update_stakes_handler(const supernode_stakes_update_handler&); diff --git a/src/cryptonote_core/stake_transaction_storage.cpp b/src/cryptonote_core/stake_transaction_storage.cpp index ac3b61e36..3abe35f16 100644 --- a/src/cryptonote_core/stake_transaction_storage.cpp +++ b/src/cryptonote_core/stake_transaction_storage.cpp @@ -22,13 +22,16 @@ struct stake_transaction_file_data size_t last_processed_block_hashes_count; StakeTransactionStorage::stake_transaction_array& stake_txs; StakeTransactionStorage::block_hash_list& block_hashes; + StakeTransactionStorage::disqualification_array& disqualifications; + stake_transaction_file_data(uint64_t in_last_processed_block_index, StakeTransactionStorage::stake_transaction_array& in_stake_txs, - size_t in_last_processed_block_hashes_count, StakeTransactionStorage::block_hash_list& in_block_hashes) + size_t in_last_processed_block_hashes_count, StakeTransactionStorage::block_hash_list& in_block_hashes, StakeTransactionStorage::disqualification_array& disqualifications) : last_processed_block_index(in_last_processed_block_index) , last_processed_block_hashes_count(in_last_processed_block_hashes_count) , stake_txs(in_stake_txs) , block_hashes(in_block_hashes) + , disqualifications(disqualifications) { } @@ -37,6 +40,7 @@ struct stake_transaction_file_data FIELD(last_processed_block_hashes_count) FIELD(block_hashes) FIELD(stake_txs) + FIELD(disqualifications) END_SERIALIZE() }; @@ -60,6 +64,23 @@ void StakeTransactionStorage::add_tx(const stake_transaction& tx) m_need_store = true; } +void StakeTransactionStorage::add_disquals(const disqualification_array& disqs) +{ + //sort and merge + auto middle = m_disqualifications.insert(m_disqualifications.end(), disqs.begin(), disqs.end()); + std::sort(middle, m_disqualifications.end(), disqualification::less_id_str); + std::inplace_merge(m_disqualifications.begin(), middle, m_disqualifications.end(), disqualification::less_id_str); + + m_need_store = true; +} + +bool StakeTransactionStorage::is_disqualified(uint64_t block_number, const std::string& supernode_public_id) const +{ + disqualification tmp; tmp.id_str = supernode_public_id; + auto pair = std::equal_range(m_disqualifications.begin(), m_disqualifications.end(), tmp, disqualification::less_id_str); + return std::any_of(pair.first, pair.second, [block_number](const disqualification& v){ return v.is_active_for(block_number); }); +} + const crypto::hash& StakeTransactionStorage::get_last_processed_block_hash() const { if (m_last_processed_block_hashes.empty()) @@ -121,10 +142,17 @@ const StakeTransactionStorage::supernode_stake_array& StakeTransactionStorage::g return m_supernode_stakes; } +const StakeTransactionStorage::supernode_disqualification_array& StakeTransactionStorage::get_supernode_disqualiications(uint64_t block_number) +{ + update_supernode_stakes(block_number); + return m_supernode_disqualifications; +} + void StakeTransactionStorage::clear_supernode_stakes() { m_supernode_stakes.clear(); m_supernode_stake_indexes.clear(); + m_supernode_disqualifications.clear(); m_supernode_stakes_update_block_number = 0; } @@ -152,9 +180,19 @@ void StakeTransactionStorage::update_supernode_stakes(uint64_t block_number) m_supernode_stakes.clear(); m_supernode_stake_indexes.clear(); + m_supernode_disqualifications.clear(); try { + //disqualifications + for(const auto& disq : m_disqualifications) + { + if(!disq.is_active_for(block_number)) + continue; + m_supernode_disqualifications.push_back(disq.blob); + } + + //stakes m_supernode_stakes.reserve(m_stake_txs.size()); for (const stake_transaction& tx : m_stake_txs) @@ -270,6 +308,7 @@ void StakeTransactionStorage::update_supernode_stakes(uint64_t block_number) { m_supernode_stakes.clear(); m_supernode_stake_indexes.clear(); + m_supernode_disqualifications.clear(); m_supernode_stakes_update_block_number = 0; @@ -307,7 +346,8 @@ void StakeTransactionStorage::load() StakeTransactionStorage::stake_transaction_array tmp_stake_txs; StakeTransactionStorage::block_hash_list tmp_block_hashes; - stake_transaction_file_data data(0, tmp_stake_txs, 0, tmp_block_hashes); + StakeTransactionStorage::disqualification_array tmp_disqualifications; + stake_transaction_file_data data(0, tmp_stake_txs, 0, tmp_block_hashes, tmp_disqualifications); r = ::serialization::parse_binary(buffer, data); @@ -318,6 +358,7 @@ void StakeTransactionStorage::load() std::swap(m_stake_txs, data.stake_txs); std::swap(m_last_processed_block_hashes, data.block_hashes); + std::swap(m_disqualifications, data.disqualifications); m_need_store = false; } @@ -331,7 +372,8 @@ void StakeTransactionStorage::load() void StakeTransactionStorage::store() const { stake_transaction_file_data data(m_last_processed_block_index, const_cast(m_stake_txs), - m_last_processed_block_hashes_count, const_cast(m_last_processed_block_hashes)); + m_last_processed_block_hashes_count, const_cast(m_last_processed_block_hashes) + , const_cast(m_disqualifications)); std::ofstream ostr; ostr.open(m_storage_file_name, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc); diff --git a/src/cryptonote_core/stake_transaction_storage.h b/src/cryptonote_core/stake_transaction_storage.h index f8d1d3b56..3fa5e6ac9 100644 --- a/src/cryptonote_core/stake_transaction_storage.h +++ b/src/cryptonote_core/stake_transaction_storage.h @@ -49,12 +49,47 @@ struct supernode_stake cryptonote::account_public_address supernode_public_address; }; +//TODO: find good place for it common with supernode +constexpr size_t DISQUALIFICATION_DURATION_BLOCK_COUNT = 10; + +struct disqualification +{ + std::string blob; //tx_extra_graft_disqualification + crypto::public_key id; + uint64_t block_index; //block_index where transaction is stored, not that from the blob. disqualifications should be applied from next block + //uint64_t unlock_time == block_height + DISQUALIFICATION_DURATION_BLOCK_COUNT + std::string id_str; + + BEGIN_SERIALIZE_OBJECT() + FIELD(blob) + FIELD(id) + FIELD(block_index) + if (!typename Archive::is_saving()) + { + id_str = epee::string_tools::pod_to_hex(id); + } + END_SERIALIZE() + + bool is_active_for(uint64_t block_index_target) const + { + //from next block where the transaction is stored + return block_index < block_index_target && block_index_target <= block_index + DISQUALIFICATION_DURATION_BLOCK_COUNT; + } + + static bool less_id_str(const disqualification& l, const disqualification& r) + { + return l.id_str < r.id_str; + } +}; + class StakeTransactionStorage { public: typedef std::vector stake_transaction_array; typedef std::list block_hash_list; typedef std::vector supernode_stake_array; + typedef std::vector disqualification_array; + typedef vector supernode_disqualification_array; StakeTransactionStorage(const std::string& storage_file_name, uint64_t first_block_number); @@ -62,7 +97,7 @@ class StakeTransactionStorage size_t get_tx_count() const { return m_stake_txs.size(); } /// Get transactions - const stake_transaction_array& get_txs() const { return m_stake_txs; } +// const stake_transaction_array& get_txs() const { return m_stake_txs; } /// Index of last processed block uint64_t get_last_processed_block_index() const { return m_last_processed_block_index; } @@ -81,12 +116,15 @@ class StakeTransactionStorage /// Add transaction void add_tx(const stake_transaction&); + void add_disquals(const disqualification_array& disqs); /// List of supernode stakes const supernode_stake_array& get_supernode_stakes(uint64_t block_number); + const supernode_disqualification_array& get_supernode_disqualiications(uint64_t block_number); /// Search supernode stake by supernode public id (returns nullptr if no stake is found) const supernode_stake* find_supernode_stake(uint64_t block_number, const std::string& supernode_public_id); + bool is_disqualified(uint64_t block_number, const std::string& supernode_public_id) const; /// Update supernode stakes void update_supernode_stakes(uint64_t block_number); @@ -113,10 +151,13 @@ class StakeTransactionStorage size_t m_last_processed_block_hashes_count; stake_transaction_array m_stake_txs; uint64_t m_supernode_stakes_update_block_number; - supernode_stake_array m_supernode_stakes; - supernode_stake_index_map m_supernode_stake_indexes; + supernode_stake_array m_supernode_stakes; //an array valid for m_supernode_stakes_update_block_number + supernode_stake_index_map m_supernode_stake_indexes; //maps supernode_id to index in m_supernode_stakes, valid for m_supernode_stakes_update_block_number uint64_t m_first_block_number; mutable bool m_need_store; + + disqualification_array m_disqualifications; //sorted by id_str + supernode_disqualification_array m_supernode_disqualifications; //an array valid for m_supernode_stakes_update_block_number }; } diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index a72adf31e..3f44f5a76 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -417,7 +417,7 @@ namespace nodetool uint64_t get_multicast_bytes_out() const { return m_multicast_bytes_out; } private: - void handle_stakes_update(uint64_t block_number, const cryptonote::StakeTransactionProcessor::supernode_stake_array& stakes); + void handle_stakes_update(uint64_t block_height, const cryptonote::StakeTransactionProcessor::supernode_stake_array& stakes, const cryptonote::StakeTransactionProcessor::supernode_disqualification_array& disquals); void handle_blockchain_based_list_update(uint64_t block_number, const crypto::hash& block_hash, const cryptonote::StakeTransactionProcessor::supernode_tier_array& tiers); private: diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index ee5e8e3a1..a73c33240 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -547,7 +547,7 @@ namespace nodetool bool node_server::init(const boost::program_options::variables_map& vm) { m_payload_handler.get_core().set_update_stakes_handler( - [&](uint64_t block_height, const cryptonote::StakeTransactionProcessor::supernode_stake_array& stakes) { handle_stakes_update(block_height, stakes); } + [&](uint64_t block_height, const cryptonote::StakeTransactionProcessor::supernode_stake_array& stakes, const cryptonote::StakeTransactionProcessor::supernode_disqualification_array& disquals) { handle_stakes_update(block_height, stakes, disquals); } ); m_payload_handler.get_core().set_update_blockchain_based_list_handler( @@ -3105,7 +3105,7 @@ namespace nodetool } template - void node_server::handle_stakes_update(uint64_t block_height, const cryptonote::StakeTransactionProcessor::supernode_stake_array& stakes) + void node_server::handle_stakes_update(uint64_t block_height, const cryptonote::StakeTransactionProcessor::supernode_stake_array& stakes, const cryptonote::StakeTransactionProcessor::supernode_disqualification_array& disquals) { static std::string supernode_endpoint("send_supernode_stakes"); @@ -3136,6 +3136,8 @@ namespace nodetool request.stakes.emplace_back(std::move(dst_stake)); } + request.disqualifications = disquals; + post_request_to_supernode(supernode_endpoint, request); } diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 9ff99498c..e8ba71e92 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -2275,9 +2275,11 @@ namespace cryptonote { uint64_t block_height; std::vector stakes; + std::vector disqualifications; //tx_extra_graft_disqualification; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(block_height) KV_SERIALIZE(stakes) + KV_SERIALIZE(disqualifications) END_KV_SERIALIZE_MAP() }; From 7bc4e3a9bd2d8e92d72376bb98c2b498d90be011 Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Fri, 10 May 2019 18:28:32 +0300 Subject: [PATCH 04/12] Common uniform selection algorithm added. --- src/cryptonote_core/CMakeLists.txt | 8 +- src/cryptonote_core/blockchain_based_list.cpp | 36 ++---- src/cryptonote_core/blockchain_based_list.h | 1 - src/utils/sample_generator.cpp | 60 ++++++++++ src/utils/sample_generator.h | 107 ++++++++++++++++++ 5 files changed, 180 insertions(+), 32 deletions(-) create mode 100644 src/utils/sample_generator.cpp create mode 100644 src/utils/sample_generator.h diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt index c9ca49598..8770bb17a 100644 --- a/src/cryptonote_core/CMakeLists.txt +++ b/src/cryptonote_core/CMakeLists.txt @@ -33,9 +33,13 @@ set(cryptonote_core_sources cryptonote_tx_utils.cpp stake_transaction_storage.cpp stake_transaction_processor.cpp - blockchain_based_list.cpp) + blockchain_based_list.cpp + ../utils/sample_generator.cpp +) -set(cryptonote_core_headers) +set(cryptonote_core_headers + ../utils/sample_generator.h +) set(cryptonote_core_private_headers blockchain_storage_boost_serialization.h diff --git a/src/cryptonote_core/blockchain_based_list.cpp b/src/cryptonote_core/blockchain_based_list.cpp index 8e7da95d4..b1cc32e4c 100644 --- a/src/cryptonote_core/blockchain_based_list.cpp +++ b/src/cryptonote_core/blockchain_based_list.cpp @@ -3,6 +3,7 @@ #include "graft_rta_config.h" #include "blockchain_based_list.h" #include "serialization/binary_utils.h" +#include "utils/sample_generator.h" using namespace cryptonote; @@ -42,22 +43,7 @@ const BlockchainBasedList::supernode_tier_array& BlockchainBasedList::tiers(size void BlockchainBasedList::select_supernodes(size_t items_count, const supernode_array& src_list, supernode_array& dst_list) { - size_t src_list_size = src_list.size(); - - if (items_count > src_list_size) - items_count = src_list_size; - - for (size_t i=0; i= items_count) - continue; - - dst_list.push_back(src_list[i]); - - items_count--; - } + graft::generator::uniform_select(graft::generator::do_not_seed{}, items_count, src_list, dst_list); } void BlockchainBasedList::apply_block(uint64_t block_height, const crypto::hash& block_hash, StakeTransactionStorage& stake_txs_storage) @@ -72,13 +58,11 @@ void BlockchainBasedList::apply_block(uint64_t block_height, const crypto::hash& //build blockchain based list for each tier - supernode_array prev_supernodes, current_supernodes; supernode_tier_array new_tier; for (size_t i=0; i(&block_hash.data[0]), - reinterpret_cast(&block_hash.data[sizeof block_hash.data])); - - m_rng.seed(seed); - //sort valid supernodes by the age of stake std::stable_sort(current_supernodes.begin(), current_supernodes.end(), [](const supernode& s1, const supernode& s2) { return s1.block_height < s2.block_height || (s1.block_height == s2.block_height && s1.supernode_public_id < s2.supernode_public_id); }); + //seed RNG + graft::generator::seed_uniform_select(block_hash); //select supernodes from the previous list - supernode_array new_supernodes; - select_supernodes(PREVIOS_BLOCKCHAIN_BASED_LIST_MAX_SIZE, prev_supernodes, new_supernodes); - if (new_supernodes.size() < BLOCKCHAIN_BASED_LIST_SIZE) + if (new_supernodes.size() < BLOCKCHAIN_BASED_LIST_SIZE) //looks like it is always true { //remove supernodes of prev list from current list diff --git a/src/cryptonote_core/blockchain_based_list.h b/src/cryptonote_core/blockchain_based_list.h index 87e7a3b81..61ddd1b62 100644 --- a/src/cryptonote_core/blockchain_based_list.h +++ b/src/cryptonote_core/blockchain_based_list.h @@ -72,7 +72,6 @@ class BlockchainBasedList list_history m_history; uint64_t m_block_height; size_t m_history_depth; - std::mt19937_64 m_rng; uint64_t m_first_block_number; mutable bool m_need_store; }; diff --git a/src/utils/sample_generator.cpp b/src/utils/sample_generator.cpp new file mode 100644 index 000000000..bc6a66c58 --- /dev/null +++ b/src/utils/sample_generator.cpp @@ -0,0 +1,60 @@ +// Copyright (c) 2019, The Graft Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include "sample_generator.h" + +namespace graft { namespace generator { + +namespace detail +{ + +std::mt19937_64& get_rnd() +{ + thread_local static std::mt19937_64 rnd; + return rnd; +} + +} //namespace detail + +void seed_uniform_select(std::seed_seq& sseq) +{ + auto& rnd = detail::get_rnd(); + rnd.seed(sseq); +} + +void seed_uniform_select(const std::string& str) +{ + assert(!str.empty() && str.size()%sizeof(uint32_t) == 0); + size_t count = str.size()/sizeof(uint32_t); + const uint32_t* p = reinterpret_cast(str.data()); + std::seed_seq sseq(p, p+count); + seed_uniform_select(sseq); +} + +}} //namespace graft::crypto_tools diff --git a/src/utils/sample_generator.h b/src/utils/sample_generator.h new file mode 100644 index 000000000..3fb07bc96 --- /dev/null +++ b/src/utils/sample_generator.h @@ -0,0 +1,107 @@ +// Copyright (c) 2019, The Graft Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#pragma once + +#include +#include +#include +#include +#include "misc_log_ex.h" + +namespace graft { namespace generator { + +namespace detail +{ + +std::mt19937_64& get_rnd(); + +} //namespace detail + +template +void uniform_select(std::mt19937_64& rnd, size_t count, const std::vector& src, std::vector& dst) +{ + size_t src_size = src.size(); + if (count > src_size) count = src_size; + for (size_t i=0; i= count) + continue; + dst.push_back(src[i]); + --count; + } +} + +struct do_not_seed{}; + +void seed_uniform_select(std::seed_seq& sseq); + +template +typename std::enable_if< !std::is_same< typename std::decay::type, std::seed_seq>::value >::type +seed_uniform_select(const POD& pod) +{ + static_assert( !std::is_same::type, do_not_seed>::value ); + static_assert( !std::is_same::type, std::string>::value ); + static_assert(std::is_trivially_copyable::value); + static_assert(sizeof(POD) % sizeof(uint32_t) == 0); + + auto* pb = reinterpret_cast(&pod); + auto* pe = pb + sizeof(POD) / sizeof(uint32_t); + std::seed_seq sseq(pb, pe); + seed_uniform_select(sseq); +} + +void seed_uniform_select(const std::string& str); + +template +typename std::enable_if< std::is_same::type, do_not_seed>::value>::type +uniform_select(DNS&& tmp, size_t count, const std::vector& src, std::vector& dst) +{ + auto& rnd = detail::get_rnd(); + uniform_select(rnd, count, src, dst); +} + +template +void uniform_select(std::seed_seq& seed, size_t count, const std::vector& src, std::vector& dst) +{ + auto& rnd = detail::get_rnd(); + rnd.seed(seed); + uniform_select(rnd, count, src, dst); +} + +template +typename std::enable_if< !std::is_same::type, do_not_seed>::value >::type +uniform_select(const POD& seed, size_t count, const std::vector& src, std::vector& dst) +{ + seed_uniform_select(seed); + uniform_select(do_not_seed{}, count, src, dst); +} + +}} //namespace graft::crypto_tools From e4ba2a74036b9837af435116007f10c65aceb297 Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Fri, 10 May 2019 19:21:07 +0300 Subject: [PATCH 05/12] selectSample is made common --- src/utils/sample_generator.h | 73 ++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/src/utils/sample_generator.h b/src/utils/sample_generator.h index 3fb07bc96..8dfc122a5 100644 --- a/src/utils/sample_generator.h +++ b/src/utils/sample_generator.h @@ -104,4 +104,77 @@ uniform_select(const POD& seed, size_t count, const std::vector& src, std::ve uniform_select(do_not_seed{}, count, src, dst); } +/*! + * \brief selectSample - selects a sample such as BBQS and QCl. + * + * \param sample_size - required resulting size. + * \param bbl_tiers - tiers of somehow sorted items. + * \param out - resulting flat list. + * \param prefix - it is for logging. + */ +constexpr int32_t TIERS = 4; + +template, TIERS>> +bool selectSample(size_t sample_size, const Tiers& bbl_tiers, std::vector& out, const char* prefix) +{ + assert(sample_size % TIERS == 0); + + //select sample_size for each tier + std::array, TIERS> tier_supernodes; + for (size_t i=0; i select; + select.fill(items_per_tier); + // If we are short of the needed SNs on any tier try selecting additional SNs from the highest + // tier with surplus SNs. For example, if tier 2 is short by 1, look for a surplus first at + // tier 4, then tier 3, then tier 1. + for (int i = 0; i < TIERS; i++) { + int deficit_i = select[i] - int(tier_supernodes[i].size()); + for (int j = TIERS-1; deficit_i > 0 && j >= 0; j--) { + if (i == j) continue; + int surplus_j = int(tier_supernodes[j].size()) - select[j]; + if (surplus_j > 0) { + // Tier j has more SNs than needed, so select an extra SN from tier j to make up for + // the deficiency at tier i. + int transfer = std::min(deficit_i, surplus_j); + select[i] -= transfer; + select[j] += transfer; + deficit_i -= transfer; + } + } + // If we still have a deficit then no other tier has a surplus; we'll just have to work with + // a smaller sample because there aren't enough SNs on the entire network. + if (deficit_i > 0) + select[i] -= deficit_i; + } + + out.clear(); + out.reserve(sample_size); + auto out_it = back_inserter(out); + for (int i = 0; i < TIERS; i++) { + std::copy(tier_supernodes[i].begin(), tier_supernodes[i].begin() + select[i], out_it); + } + + if (out.size() > sample_size) + out.resize(sample_size); + + MDEBUG("..." << out.size() << " supernodes has been selected"); + + return out.size() == sample_size; +} + }} //namespace graft::crypto_tools From 20e8e05b4703e099b2ed3baeaf5880a916535dca Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Sat, 11 May 2019 01:54:54 +0300 Subject: [PATCH 06/12] select_BBQS_QCL common function added --- src/utils/sample_generator.h | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/utils/sample_generator.h b/src/utils/sample_generator.h index 8dfc122a5..5e9024319 100644 --- a/src/utils/sample_generator.h +++ b/src/utils/sample_generator.h @@ -33,6 +33,8 @@ #include #include #include + +#include "crypto/crypto.h" #include "misc_log_ex.h" namespace graft { namespace generator { @@ -104,6 +106,11 @@ uniform_select(const POD& seed, size_t count, const std::vector& src, std::ve uniform_select(do_not_seed{}, count, src, dst); } +constexpr int32_t TIERS = 4; +constexpr int32_t ITEMS_PER_TIER = 2; +constexpr int32_t DISQUALIFICATION_SAMPLE_SIZE = TIERS * ITEMS_PER_TIER; +constexpr int32_t DISQUALIFICATION_CANDIDATES_SIZE = TIERS * ITEMS_PER_TIER; + /*! * \brief selectSample - selects a sample such as BBQS and QCl. * @@ -112,8 +119,6 @@ uniform_select(const POD& seed, size_t count, const std::vector& src, std::ve * \param out - resulting flat list. * \param prefix - it is for logging. */ -constexpr int32_t TIERS = 4; - template, TIERS>> bool selectSample(size_t sample_size, const Tiers& bbl_tiers, std::vector& out, const char* prefix) { @@ -177,4 +182,23 @@ bool selectSample(size_t sample_size, const Tiers& bbl_tiers, std::vector& ou return out.size() == sample_size; } +/*! + * \brief select_BBQS_QCL - generates BBQS and QCl from bbl. + * + * \param block_hash - hash of the block corresponding to BBL. + * \param bbl_tiers - tiers of somehow sorted items. + * \param bbqs - resulting BBQS. + * \param qcl - resulting QCL. + */ + +template, TIERS>> +bool select_BBQS_QCL(crypto::hash block_hash, const Tiers& bbl_tiers, std::vector& bbqs, std::vector& qcl) +{ + //seed once + generator::seed_uniform_select(block_hash); + bool res1 = selectSample(DISQUALIFICATION_SAMPLE_SIZE, bbl_tiers, bbqs, "BBQS"); + bool res2 = selectSample(DISQUALIFICATION_CANDIDATES_SIZE, bbl_tiers, qcl, "QCL"); + return res1 && res2; +} + }} //namespace graft::crypto_tools From 1f47361738fee482c3543210e117aec51bea6849 Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Sat, 11 May 2019 05:25:07 +0300 Subject: [PATCH 07/12] Disqualification transaction validation added. --- src/cryptonote_core/blockchain_based_list.h | 4 +- .../stake_transaction_processor.cpp | 100 +++++++++++++++++- src/utils/sample_generator.h | 9 +- 3 files changed, 106 insertions(+), 7 deletions(-) diff --git a/src/cryptonote_core/blockchain_based_list.h b/src/cryptonote_core/blockchain_based_list.h index 61ddd1b62..d1b6859c7 100644 --- a/src/cryptonote_core/blockchain_based_list.h +++ b/src/cryptonote_core/blockchain_based_list.h @@ -39,13 +39,13 @@ class BlockchainBasedList /// Constructors BlockchainBasedList(const std::string& file_name, uint64_t first_block_number); - /// List of tiers + /// List of tiers; resulting height is block_height - depth const supernode_tier_array& tiers(size_t depth = 0) const; /// Height of the corresponding block uint64_t block_height() const { return m_block_height; } - /// Number of blocks in history + /// Number of blocks in history; valid depth < history_depth() uint64_t history_depth() const { return m_history_depth; } /// Apply new block on top of the list diff --git a/src/cryptonote_core/stake_transaction_processor.cpp b/src/cryptonote_core/stake_transaction_processor.cpp index ff0257cc0..6fa8fc71b 100644 --- a/src/cryptonote_core/stake_transaction_processor.cpp +++ b/src/cryptonote_core/stake_transaction_processor.cpp @@ -3,6 +3,7 @@ #include "stake_transaction_processor.h" #include "../graft_rta_config.h" #include "serialization/binary_utils.h" +#include "../utils/sample_generator.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "staketransaction.processor" @@ -144,6 +145,46 @@ void StakeTransactionProcessor::init_storages_impl() m_blockchain_based_list.reset(new BlockchainBasedList(m_config_dir + "/" + BLOCKCHAIN_BASED_LIST_FILE_NAME, first_block_number)); } +namespace +{ + +using TI = std::pair; //tier, index in the tier +constexpr int32_t TIERS = graft::generator::TIERS; +using Tiers = BlockchainBasedList::supernode_tier_array; +using Ids = std::vector; + +std::array, TIERS> makeBBLindexes(const Tiers& bbl_tiers) +{ + std::array, TIERS> res; + for(size_t t=0; tTI{ return std::make_pair(t, idx++); } ); + } + return res; +} + +Ids fromIndexes(const Tiers& bbl_tiers, const std::vector& idxs) +{ + Ids res; + res.reserve(idxs.size()); + for(auto& ti : idxs) + { + auto& t = ti.first; + auto& i = ti.second; + auto& supernode_public_id = bbl_tiers[t][i].supernode_public_id; + crypto::public_key id; + bool ok = epee::string_tools::hex_to_pod(supernode_public_id, id); + assert(ok); + res.emplace_back(std::move(id)); + } + return res; +} + +} //namespace + void StakeTransactionProcessor::process_block_stake_transaction(uint64_t block_index, const block& block, const crypto::hash& block_hash, bool update_storage) { if (block_index <= m_storage->get_last_processed_block_index()) @@ -189,13 +230,70 @@ void StakeTransactionProcessor::process_block_stake_transaction(uint64_t block_i MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash); continue; } + if(block_index <= disq_extra.item.block_height) + { + MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash + << "; invalid block_height " << disq_extra.item.block_height << " current " << block_index); + continue; + } + crypto::hash b_hash = m_blockchain.get_block_id_by_height(disq_extra.item.block_height); + if(b_hash != disq_extra.item.block_hash) + { + MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash + << "; invalid block_hash "); + continue; + } + + //get BBL + size_t depth = m_blockchain_based_list->block_height() - disq_extra.item.block_height; + if(m_blockchain_based_list->history_depth() <= depth) + { + MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash + << "; out of history "); + continue; + } + + if(disq_extra.signers.size() < graft::generator::REQUIRED_BBQS_VOTES) + { + MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash + << "; lack of signers "); + continue; + } + auto& tiers = m_blockchain_based_list->tiers(depth); + + auto bbl_idxs = makeBBLindexes(tiers); + + std::vector bbqs_idxs, qcl_idxs; + graft::generator::select_BBQS_QCL(disq_extra.item.block_hash, bbl_idxs, bbqs_idxs, qcl_idxs); + Ids bbqs = fromIndexes(tiers, bbqs_idxs); + Ids qcl = fromIndexes(tiers, qcl_idxs); + + if(std::none_of(qcl.begin(), qcl.end(), [&disq_extra](crypto::public_key& v)->bool { return v == disq_extra.item.id; } )) + { + std::string id_str; + epee::string_tools::pod_to_hex(disq_extra.item.id); + MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash + << "; disqualified id " << id_str << " is not in QCL"); + continue; + } + + for(auto& si : disq_extra.signers) + { + if(std::none_of(bbqs.begin(), bbqs.end(), [&si](crypto::public_key& v)->bool { return v == si.signer_id; } )) + { + std::string id_str; + epee::string_tools::pod_to_hex(si.signer_id); + MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash + << "; signer id " << id_str << " is not in BBQS"); + continue; + } + } disqualification disq; ::serialization::dump_binary(disq_extra, disq.blob); disq.block_index = block_index; disq.id = disq_extra.item.id; disq.id_str = epee::string_tools::pod_to_hex(disq.id); - //TODO: check unlock_time MDEBUG("New disqualification transaction found at block #" << block_index << ", tx_hash=" << tx_hash << ", supernode_id '" << disq.id_str << "'"); diff --git a/src/utils/sample_generator.h b/src/utils/sample_generator.h index 5e9024319..d93dc5b35 100644 --- a/src/utils/sample_generator.h +++ b/src/utils/sample_generator.h @@ -108,8 +108,9 @@ uniform_select(const POD& seed, size_t count, const std::vector& src, std::ve constexpr int32_t TIERS = 4; constexpr int32_t ITEMS_PER_TIER = 2; -constexpr int32_t DISQUALIFICATION_SAMPLE_SIZE = TIERS * ITEMS_PER_TIER; -constexpr int32_t DISQUALIFICATION_CANDIDATES_SIZE = TIERS * ITEMS_PER_TIER; +constexpr int32_t BBQS_SIZE = TIERS * ITEMS_PER_TIER; +constexpr int32_t QCL_SIZE = TIERS * ITEMS_PER_TIER; +constexpr int32_t REQUIRED_BBQS_VOTES = (BBQS_SIZE*2 + (3-1))/3; /*! * \brief selectSample - selects a sample such as BBQS and QCl. @@ -196,8 +197,8 @@ bool select_BBQS_QCL(crypto::hash block_hash, const Tiers& bbl_tiers, std::vecto { //seed once generator::seed_uniform_select(block_hash); - bool res1 = selectSample(DISQUALIFICATION_SAMPLE_SIZE, bbl_tiers, bbqs, "BBQS"); - bool res2 = selectSample(DISQUALIFICATION_CANDIDATES_SIZE, bbl_tiers, qcl, "QCL"); + bool res1 = selectSample(BBQS_SIZE, bbl_tiers, bbqs, "BBQS"); + bool res2 = selectSample(QCL_SIZE, bbl_tiers, qcl, "QCL"); return res1 && res2; } From 2c1780e8bb61a508f3e7ded362d9ba3b7b732ab8 Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Fri, 17 May 2019 19:11:38 +0300 Subject: [PATCH 08/12] select_AuthSample added --- src/utils/sample_generator.h | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/utils/sample_generator.h b/src/utils/sample_generator.h index d93dc5b35..a05de71b7 100644 --- a/src/utils/sample_generator.h +++ b/src/utils/sample_generator.h @@ -108,6 +108,7 @@ uniform_select(const POD& seed, size_t count, const std::vector& src, std::ve constexpr int32_t TIERS = 4; constexpr int32_t ITEMS_PER_TIER = 2; +constexpr int32_t AUTH_SAMPLE_SIZE = TIERS * ITEMS_PER_TIER; constexpr int32_t BBQS_SIZE = TIERS * ITEMS_PER_TIER; constexpr int32_t QCL_SIZE = TIERS * ITEMS_PER_TIER; constexpr int32_t REQUIRED_BBQS_VOTES = (BBQS_SIZE*2 + (3-1))/3; @@ -202,4 +203,21 @@ bool select_BBQS_QCL(crypto::hash block_hash, const Tiers& bbl_tiers, std::vecto return res1 && res2; } -}} //namespace graft::crypto_tools +/*! + * \brief select_AuthSample - generates auth sample from bbl based on payment_id. + * + * \param payment_id + * \param bbl_tiers - tiers of somehow sorted items. + * \param auths - resulting auth sample. + */ + +template, TIERS>> +bool select_AuthSample(const std::string& payment_id, const Tiers& bbl_tiers, std::vector& auths) +{ + //seed once + generator::seed_uniform_select(payment_id); + bool res = selectSample(AUTH_SAMPLE_SIZE, bbl_tiers, auths, "auth"); + return res; +} + +}} //namespace graft::generator From bcaeed63f19a3e2370028d91926ebb405b02b331 Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Tue, 21 May 2019 18:44:29 +0300 Subject: [PATCH 09/12] A bug in the selectSample fixed --- src/utils/sample_generator.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/utils/sample_generator.h b/src/utils/sample_generator.h index a05de71b7..77183a2b8 100644 --- a/src/utils/sample_generator.h +++ b/src/utils/sample_generator.h @@ -120,6 +120,7 @@ constexpr int32_t REQUIRED_BBQS_VOTES = (BBQS_SIZE*2 + (3-1))/3; * \param bbl_tiers - tiers of somehow sorted items. * \param out - resulting flat list. * \param prefix - it is for logging. + * \return - false if resulting size less than requested sample_size */ template, TIERS>> bool selectSample(size_t sample_size, const Tiers& bbl_tiers, std::vector& out, const char* prefix) @@ -134,11 +135,6 @@ bool selectSample(size_t sample_size, const Tiers& bbl_tiers, std::vector& ou auto& dst = tier_supernodes[i]; dst.reserve(sample_size); uniform_select(do_not_seed{}, sample_size, src, dst); - if (dst.size() != sample_size) - { - LOG_ERROR("unable to select supernodes for " << prefix << " sample"); - return false; - } MDEBUG("..." << dst.size() << " supernodes has been selected for tier " << (i + 1) << " from blockchain based list with " << src.size() << " supernodes"); } @@ -191,6 +187,7 @@ bool selectSample(size_t sample_size, const Tiers& bbl_tiers, std::vector& ou * \param bbl_tiers - tiers of somehow sorted items. * \param bbqs - resulting BBQS. * \param qcl - resulting QCL. + * \return - false if at least one of the resulting sizes less than desired sizes of BBQS or QCL */ template, TIERS>> @@ -209,6 +206,7 @@ bool select_BBQS_QCL(crypto::hash block_hash, const Tiers& bbl_tiers, std::vecto * \param payment_id * \param bbl_tiers - tiers of somehow sorted items. * \param auths - resulting auth sample. + * \return - false if resulting size less than desired auth sample size */ template, TIERS>> From 01c763a3569f6c1ca20784fdb8ed2adcd5c3f72d Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Wed, 22 May 2019 23:22:11 +0300 Subject: [PATCH 10/12] style fix --- .../cryptonote_format_utils.cpp | 174 +++++++++--------- src/cryptonote_basic/tx_extra.h | 42 ++--- .../stake_transaction_processor.cpp | 98 +++++----- src/cryptonote_core/tx_pool.cpp | 12 +- 4 files changed, 163 insertions(+), 163 deletions(-) diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 9ca625a14..dad319533 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -1158,64 +1158,64 @@ namespace cryptonote //--------------------------------------------------------------- bool add_graft_tx_extra_to_extra(transaction &tx, const supernode::GraftTxExtra &graft_extra) { - return add_graft_tx_extra_to_extra(tx.extra, graft_extra); + return add_graft_tx_extra_to_extra(tx.extra, graft_extra); } //--------------------------------------------------------------- bool add_graft_tx_extra_to_extra(std::vector& extra, const supernode::GraftTxExtra &graft_extra) { - std::string blob; - ::serialization::dump_binary(const_cast(graft_extra), blob); - tx_extra_graft_extra container; - container.data = blob; - blob.clear(); - ::serialization::dump_binary(container, blob); - extra.push_back(TX_EXTRA_GRAFT_EXTRA_TAG); - std::copy(blob.begin(), blob.end(), std::back_inserter(extra)); - return true; + std::string blob; + ::serialization::dump_binary(const_cast(graft_extra), blob); + tx_extra_graft_extra container; + container.data = blob; + blob.clear(); + ::serialization::dump_binary(container, blob); + extra.push_back(TX_EXTRA_GRAFT_EXTRA_TAG); + std::copy(blob.begin(), blob.end(), std::back_inserter(extra)); + return true; } //--------------------------------------------------------------- bool get_graft_tx_extra_from_extra(const transaction &tx, supernode::GraftTxExtra &graft_tx_extra) { - std::vector tx_extra_fields; - parse_tx_extra(tx.extra, tx_extra_fields); - tx_extra_graft_extra graft_extra; - if(!find_tx_extra_field_by_type(tx_extra_fields, graft_extra)) - return false; - return ::serialization::parse_binary(graft_extra.data, graft_tx_extra); + std::vector tx_extra_fields; + parse_tx_extra(tx.extra, tx_extra_fields); + tx_extra_graft_extra graft_extra; + if(!find_tx_extra_field_by_type(tx_extra_fields, graft_extra)) + return false; + return ::serialization::parse_binary(graft_extra.data, graft_tx_extra); } namespace { - struct GraftStakeTxExtra - { - std::string supernode_public_id; - cryptonote::account_public_address supernode_public_address; - crypto::signature supernode_signature; - - GraftStakeTxExtra() = default; - - GraftStakeTxExtra(const std::string &supernode_public_id, - const cryptonote::account_public_address& supernode_public_address, - const crypto::signature &supernode_signature) : - supernode_public_id(supernode_public_id), - supernode_public_address(supernode_public_address), - supernode_signature(supernode_signature) - {} - - bool operator ==(const GraftStakeTxExtra &rhs) const { - return supernode_public_id == rhs.supernode_public_id && - !memcmp(&supernode_public_address.m_view_public_key.data[0], &rhs.supernode_public_address.m_view_public_key.data[0], - sizeof(supernode_public_address.m_view_public_key.data)) && - !memcmp(&supernode_signature.c.data[0], &rhs.supernode_signature.c.data[0], sizeof(supernode_signature.c.data)) && - !memcmp(&supernode_signature.r.data[0], &rhs.supernode_signature.r.data[0], sizeof(supernode_signature.r.data)); - } + struct GraftStakeTxExtra + { + std::string supernode_public_id; + cryptonote::account_public_address supernode_public_address; + crypto::signature supernode_signature; + + GraftStakeTxExtra() = default; + + GraftStakeTxExtra(const std::string &supernode_public_id, + const cryptonote::account_public_address& supernode_public_address, + const crypto::signature &supernode_signature) : + supernode_public_id(supernode_public_id), + supernode_public_address(supernode_public_address), + supernode_signature(supernode_signature) + {} + + bool operator ==(const GraftStakeTxExtra &rhs) const { + return supernode_public_id == rhs.supernode_public_id && + !memcmp(&supernode_public_address.m_view_public_key.data[0], &rhs.supernode_public_address.m_view_public_key.data[0], + sizeof(supernode_public_address.m_view_public_key.data)) && + !memcmp(&supernode_signature.c.data[0], &rhs.supernode_signature.c.data[0], sizeof(supernode_signature.c.data)) && + !memcmp(&supernode_signature.r.data[0], &rhs.supernode_signature.r.data[0], sizeof(supernode_signature.r.data)); + } - BEGIN_SERIALIZE_OBJECT() - FIELD(supernode_public_id) - FIELD(supernode_public_address) - FIELD(supernode_signature) - END_SERIALIZE() - }; + BEGIN_SERIALIZE_OBJECT() + FIELD(supernode_public_id) + FIELD(supernode_public_address) + FIELD(supernode_signature) + END_SERIALIZE() + }; } bool add_graft_stake_tx_extra_to_extra @@ -1224,16 +1224,16 @@ namespace cryptonote const cryptonote::account_public_address &supernode_public_address, const crypto::signature &supernode_signature) { - GraftStakeTxExtra tx_extra(supernode_public_id, supernode_public_address, supernode_signature); - std::string blob; - ::serialization::dump_binary(tx_extra, blob); - tx_extra_graft_stake_tx container; - container.data = blob; - blob.clear(); - ::serialization::dump_binary(container, blob); - extra.push_back(TX_EXTRA_GRAFT_STAKE_TX_TAG); - std::copy(blob.begin(), blob.end(), std::back_inserter(extra)); - return true; + GraftStakeTxExtra tx_extra(supernode_public_id, supernode_public_address, supernode_signature); + std::string blob; + ::serialization::dump_binary(tx_extra, blob); + tx_extra_graft_stake_tx container; + container.data = blob; + blob.clear(); + ::serialization::dump_binary(container, blob); + extra.push_back(TX_EXTRA_GRAFT_STAKE_TX_TAG); + std::copy(blob.begin(), blob.end(), std::back_inserter(extra)); + return true; } bool add_graft_rta_header_to_extra(std::vector &extra, const rta_header &rta_header) @@ -1261,13 +1261,13 @@ namespace cryptonote bool add_graft_tx_secret_key_to_extra(std::vector &extra, const crypto::secret_key& secret_key) { - tx_extra_graft_tx_secret_key container; - container.secret_key = secret_key; - std::string blob; - ::serialization::dump_binary(container, blob); - extra.push_back(TX_EXTRA_GRAFT_TX_SECRET_KEY_TAG); - std::copy(blob.begin(), blob.end(), std::back_inserter(extra)); - return true; + tx_extra_graft_tx_secret_key container; + container.secret_key = secret_key; + std::string blob; + ::serialization::dump_binary(container, blob); + extra.push_back(TX_EXTRA_GRAFT_TX_SECRET_KEY_TAG); + std::copy(blob.begin(), blob.end(), std::back_inserter(extra)); + return true; } bool get_graft_stake_tx_extra_from_extra @@ -1277,30 +1277,30 @@ namespace cryptonote crypto::signature &supernode_signature, crypto::secret_key &tx_secret_key) { - std::vector tx_extra_fields; - parse_tx_extra(tx.extra, tx_extra_fields); + std::vector tx_extra_fields; + parse_tx_extra(tx.extra, tx_extra_fields); - tx_extra_graft_stake_tx stake_tx_extra; + tx_extra_graft_stake_tx stake_tx_extra; - if(!find_tx_extra_field_by_type(tx_extra_fields, stake_tx_extra)) - return false; + if(!find_tx_extra_field_by_type(tx_extra_fields, stake_tx_extra)) + return false; - GraftStakeTxExtra stake_tx; + GraftStakeTxExtra stake_tx; - if (!::serialization::parse_binary(stake_tx_extra.data, stake_tx)) - return false; + if (!::serialization::parse_binary(stake_tx_extra.data, stake_tx)) + return false; - tx_extra_graft_tx_secret_key stake_tx_secret_key_extra; + tx_extra_graft_tx_secret_key stake_tx_secret_key_extra; - if(!find_tx_extra_field_by_type(tx_extra_fields, stake_tx_secret_key_extra)) - return false; + if(!find_tx_extra_field_by_type(tx_extra_fields, stake_tx_secret_key_extra)) + return false; - supernode_public_id = stake_tx.supernode_public_id; - supernode_public_address = stake_tx.supernode_public_address; - supernode_signature = stake_tx.supernode_signature; - tx_secret_key = stake_tx_secret_key_extra.secret_key; + supernode_public_id = stake_tx.supernode_public_id; + supernode_public_address = stake_tx.supernode_public_address; + supernode_signature = stake_tx.supernode_signature; + tx_secret_key = stake_tx_secret_key_extra.secret_key; - return true; + return true; } bool add_graft_rta_signatures_to_extra2(std::vector &extra, const std::vector &rta_signatures) @@ -1328,15 +1328,15 @@ namespace cryptonote bool graft_get_disqualification(const transaction &tx, tx_extra_graft_disqualification& disq) { - if(tx.version != 123) - return false; - if(!tx.vin.empty() || !tx.vout.empty() || tx.rct_signatures.txnFee !=0) - return false; - std::vector tx_extra_fields; - parse_tx_extra(tx.extra, tx_extra_fields); - if(!find_tx_extra_field_by_type(tx_extra_fields, disq)) - return false; - return true; + if(tx.version != 123) + return false; + if(!tx.vin.empty() || !tx.vout.empty() || tx.rct_signatures.txnFee !=0) + return false; + std::vector tx_extra_fields; + parse_tx_extra(tx.extra, tx_extra_fields); + if(!find_tx_extra_field_by_type(tx_extra_fields, disq)) + return false; + return true; } bool graft_is_disqualification(const transaction &tx) diff --git a/src/cryptonote_basic/tx_extra.h b/src/cryptonote_basic/tx_extra.h index 3a9d01e61..465417251 100644 --- a/src/cryptonote_basic/tx_extra.h +++ b/src/cryptonote_basic/tx_extra.h @@ -244,27 +244,27 @@ namespace cryptonote struct tx_extra_graft_disqualification { - struct disqualification_item - { - uint64_t block_height; - crypto::hash block_hash; - crypto::public_key id; - BEGIN_SERIALIZE() - FIELD(block_height) - FIELD(block_hash) - FIELD(id) - END_SERIALIZE() - }; - - struct signer_item - { - crypto::public_key signer_id; - crypto::signature sign; - BEGIN_SERIALIZE() - FIELD(signer_id) - FIELD(sign) - END_SERIALIZE() - }; + struct disqualification_item + { + uint64_t block_height; + crypto::hash block_hash; + crypto::public_key id; + BEGIN_SERIALIZE() + FIELD(block_height) + FIELD(block_hash) + FIELD(id) + END_SERIALIZE() + }; + + struct signer_item + { + crypto::public_key signer_id; + crypto::signature sign; + BEGIN_SERIALIZE() + FIELD(signer_id) + FIELD(sign) + END_SERIALIZE() + }; disqualification_item item; std::vector signers; diff --git a/src/cryptonote_core/stake_transaction_processor.cpp b/src/cryptonote_core/stake_transaction_processor.cpp index 6fa8fc71b..fe0620762 100644 --- a/src/cryptonote_core/stake_transaction_processor.cpp +++ b/src/cryptonote_core/stake_transaction_processor.cpp @@ -155,32 +155,32 @@ using Ids = std::vector; std::array, TIERS> makeBBLindexes(const Tiers& bbl_tiers) { - std::array, TIERS> res; - for(size_t t=0; tTI{ return std::make_pair(t, idx++); } ); - } - return res; + std::array, TIERS> res; + for(size_t t=0; tTI{ return std::make_pair(t, idx++); } ); + } + return res; } Ids fromIndexes(const Tiers& bbl_tiers, const std::vector& idxs) { - Ids res; - res.reserve(idxs.size()); - for(auto& ti : idxs) - { - auto& t = ti.first; - auto& i = ti.second; - auto& supernode_public_id = bbl_tiers[t][i].supernode_public_id; - crypto::public_key id; - bool ok = epee::string_tools::hex_to_pod(supernode_public_id, id); - assert(ok); - res.emplace_back(std::move(id)); - } - return res; + Ids res; + res.reserve(idxs.size()); + for(auto& ti : idxs) + { + auto& t = ti.first; + auto& i = ti.second; + auto& supernode_public_id = bbl_tiers[t][i].supernode_public_id; + crypto::public_key id; + bool ok = epee::string_tools::hex_to_pod(supernode_public_id, id); + assert(ok); + res.emplace_back(std::move(id)); + } + return res; } } //namespace @@ -227,37 +227,37 @@ void StakeTransactionProcessor::process_block_stake_transaction(uint64_t block_i tx_extra_graft_disqualification disq_extra; if(graft_check_disqualification(tx, &disq_extra)) { - MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash); - continue; + MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash); + continue; } if(block_index <= disq_extra.item.block_height) { - MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash - << "; invalid block_height " << disq_extra.item.block_height << " current " << block_index); - continue; + MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash + << "; invalid block_height " << disq_extra.item.block_height << " current " << block_index); + continue; } crypto::hash b_hash = m_blockchain.get_block_id_by_height(disq_extra.item.block_height); if(b_hash != disq_extra.item.block_hash) { - MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash - << "; invalid block_hash "); - continue; + MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash + << "; invalid block_hash "); + continue; } //get BBL size_t depth = m_blockchain_based_list->block_height() - disq_extra.item.block_height; if(m_blockchain_based_list->history_depth() <= depth) { - MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash - << "; out of history "); - continue; + MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash + << "; out of history "); + continue; } if(disq_extra.signers.size() < graft::generator::REQUIRED_BBQS_VOTES) { - MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash - << "; lack of signers "); - continue; + MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash + << "; lack of signers "); + continue; } auto& tiers = m_blockchain_based_list->tiers(depth); @@ -270,23 +270,23 @@ void StakeTransactionProcessor::process_block_stake_transaction(uint64_t block_i if(std::none_of(qcl.begin(), qcl.end(), [&disq_extra](crypto::public_key& v)->bool { return v == disq_extra.item.id; } )) { - std::string id_str; - epee::string_tools::pod_to_hex(disq_extra.item.id); - MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash - << "; disqualified id " << id_str << " is not in QCL"); - continue; + std::string id_str; + epee::string_tools::pod_to_hex(disq_extra.item.id); + MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash + << "; disqualified id " << id_str << " is not in QCL"); + continue; } for(auto& si : disq_extra.signers) { - if(std::none_of(bbqs.begin(), bbqs.end(), [&si](crypto::public_key& v)->bool { return v == si.signer_id; } )) - { - std::string id_str; - epee::string_tools::pod_to_hex(si.signer_id); - MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash - << "; signer id " << id_str << " is not in BBQS"); - continue; - } + if(std::none_of(bbqs.begin(), bbqs.end(), [&si](crypto::public_key& v)->bool { return v == si.signer_id; } )) + { + std::string id_str; + epee::string_tools::pod_to_hex(si.signer_id); + MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash + << "; signer id " << id_str << " is not in BBQS"); + continue; + } } disqualification disq; diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 3403767f9..e25493d8c 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -192,12 +192,12 @@ namespace cryptonote bool is_rta_tx = tx.type == transaction::tx_type_rta; if(is_disqualification_tx) { - if(!graft_check_disqualification(tx)) - { - MERROR("Invalid disqualification transaction with id " << id); - tvc.m_verifivation_failed = true; - return false; - } + if(!graft_check_disqualification(tx)) + { + MERROR("Invalid disqualification transaction with id " << id); + tvc.m_verifivation_failed = true; + return false; + } } else if (is_rta_tx) { cryptonote::rta_header rta_hdr; From 73304f3537bcccd3024987c76cd6e84dd034e11f Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Thu, 23 May 2019 04:46:16 +0300 Subject: [PATCH 11/12] Auth sample disqualification transactions added. --- .../cryptonote_format_utils.cpp | 41 ++- .../cryptonote_format_utils.h | 4 + src/cryptonote_basic/tx_extra.h | 38 ++- src/cryptonote_core/cryptonote_core.cpp | 8 + .../stake_transaction_processor.cpp | 251 ++++++++++++------ .../stake_transaction_processor.h | 3 + .../stake_transaction_storage.cpp | 66 ++++- .../stake_transaction_storage.h | 37 +++ src/cryptonote_core/tx_pool.cpp | 13 +- src/rpc/core_rpc_server_commands_defs.h | 2 +- src/utils/sample_generator.h | 1 + 11 files changed, 376 insertions(+), 88 deletions(-) diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index dad319533..5ae16ca22 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -1334,9 +1334,7 @@ namespace cryptonote return false; std::vector tx_extra_fields; parse_tx_extra(tx.extra, tx_extra_fields); - if(!find_tx_extra_field_by_type(tx_extra_fields, disq)) - return false; - return true; + return find_tx_extra_field_by_type(tx_extra_fields, disq); } bool graft_is_disqualification(const transaction &tx) @@ -1365,4 +1363,41 @@ namespace cryptonote return true; } + bool graft_get_disqualification2(const transaction &tx, tx_extra_graft_disqualification2& disq) + { + if(tx.version != 124) + return false; + if(!tx.vin.empty() || !tx.vout.empty() || tx.rct_signatures.txnFee !=0) + return false; + std::vector tx_extra_fields; + parse_tx_extra(tx.extra, tx_extra_fields); + return find_tx_extra_field_by_type(tx_extra_fields, disq); + } + + bool graft_is_disqualification2(const transaction &tx) + { + tx_extra_graft_disqualification2 disq; + return graft_get_disqualification2(tx, disq); + } + + bool graft_check_disqualification2(const transaction &tx, tx_extra_graft_disqualification2* pdisq) + { + tx_extra_graft_disqualification2 disq; + if(!graft_get_disqualification2(tx, disq)) + return false; + {//check signs + std::string item_str; + ::serialization::dump_binary(disq.item, item_str); + crypto::hash hash; + crypto::cn_fast_hash(item_str.data(), item_str.size(), hash); + for(auto& si : disq.signers) + { + if(!crypto::check_signature(hash, si.signer_id, si.sign)) + return false; + } + } + if(pdisq) *pdisq = std::move(disq); + return true; + } + } diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index 6650475bc..fbc4f3cd4 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -170,6 +170,10 @@ namespace cryptonote bool graft_is_disqualification(const transaction &tx); bool graft_check_disqualification(const transaction &tx, tx_extra_graft_disqualification* pdisq = nullptr); + bool graft_get_disqualification2(const transaction &tx, tx_extra_graft_disqualification2& disq); + bool graft_is_disqualification2(const transaction &tx); + bool graft_check_disqualification2(const transaction &tx, tx_extra_graft_disqualification2* pdisq = nullptr); + bool add_extra_nonce_to_tx_extra(std::vector& tx_extra, const blobdata& extra_nonce); bool remove_field_from_tx_extra(std::vector& tx_extra, const std::type_info &type); void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id); diff --git a/src/cryptonote_basic/tx_extra.h b/src/cryptonote_basic/tx_extra.h index 465417251..0871563a1 100644 --- a/src/cryptonote_basic/tx_extra.h +++ b/src/cryptonote_basic/tx_extra.h @@ -47,6 +47,7 @@ #define TX_EXTRA_GRAFT_RTA_HEADER_TAG 0x83 #define TX_EXTRA_GRAFT_RTA_SIGNATURES_TAG 0x84 #define TX_EXTRA_GRAFT_DISQUALIFICATION_TAG 0x85 +#define TX_EXTRA_GRAFT_DISQUALIFICATION2_TAG 0x86 #define TX_EXTRA_MYSTERIOUS_MINERGATE_TAG 0xDE #define TX_EXTRA_NONCE_PAYMENT_ID 0x00 @@ -274,6 +275,40 @@ namespace cryptonote END_SERIALIZE() }; + struct tx_extra_graft_disqualification2 + { + struct disqualification_item + { + std::string payment_id; + uint64_t block_height; + crypto::hash block_hash; + std::vector ids; + BEGIN_SERIALIZE() + FIELD(payment_id) + FIELD(block_height) + FIELD(block_hash) + FIELD(ids) + END_SERIALIZE() + }; + + struct signer_item + { + crypto::public_key signer_id; + crypto::signature sign; + BEGIN_SERIALIZE() + FIELD(signer_id) + FIELD(sign) + END_SERIALIZE() + }; + + disqualification_item item; + std::vector signers; + BEGIN_SERIALIZE() + FIELD(item) + FIELD(signers) + END_SERIALIZE() + }; + // tx_extra_field format, except tx_extra_padding and tx_extra_pub_key: // varint tag; // varint size; @@ -281,7 +316,7 @@ namespace cryptonote typedef boost::variant tx_extra_field; + tx_extra_graft_disqualification, tx_extra_graft_disqualification2> tx_extra_field; } VARIANT_TAG(binary_archive, cryptonote::tx_extra_padding, TX_EXTRA_TAG_PADDING); @@ -296,3 +331,4 @@ VARIANT_TAG(binary_archive, cryptonote::tx_extra_graft_tx_secret_key, TX_EXTRA_G VARIANT_TAG(binary_archive, cryptonote::tx_extra_graft_rta_header, TX_EXTRA_GRAFT_RTA_HEADER_TAG); VARIANT_TAG(binary_archive, cryptonote::tx_extra_graft_rta_signatures, TX_EXTRA_GRAFT_RTA_SIGNATURES_TAG); VARIANT_TAG(binary_archive, cryptonote::tx_extra_graft_disqualification, TX_EXTRA_GRAFT_DISQUALIFICATION_TAG); +VARIANT_TAG(binary_archive, cryptonote::tx_extra_graft_disqualification2, TX_EXTRA_GRAFT_DISQUALIFICATION2_TAG); diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 54bb200ff..f8d4e954c 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -968,6 +968,14 @@ namespace cryptonote } return graft_is_disqualification(tx); } + if(tx.version == 124) + { + if(tx.vin.size() || tx.vout.size()) + { + MERROR_VER("qualification2 tx with non-empty inputs or outputs, rejected for tx id= " << get_transaction_hash(tx)); + } + return graft_is_disqualification2(tx); + } if(!tx.vin.size()) { MERROR_VER("tx with empty inputs, rejected for tx id= " << get_transaction_hash(tx)); diff --git a/src/cryptonote_core/stake_transaction_processor.cpp b/src/cryptonote_core/stake_transaction_processor.cpp index fe0620762..c33db14df 100644 --- a/src/cryptonote_core/stake_transaction_processor.cpp +++ b/src/cryptonote_core/stake_transaction_processor.cpp @@ -185,6 +185,172 @@ Ids fromIndexes(const Tiers& bbl_tiers, const std::vector& idxs) } //namespace +void StakeTransactionProcessor::process_disqualification_transaction(const transaction& tx, const crypto::hash tx_hash, uint64_t block_index, const crypto::hash& block_hash, StakeTransactionStorage::disqualification_array& disquals) +{ + assert(tx.version == 123); + + tx_extra_graft_disqualification disq_extra; + if(graft_check_disqualification(tx, &disq_extra)) + { + MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash); + return; + } + if(block_index <= disq_extra.item.block_height) + { + MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash + << "; invalid block_height " << disq_extra.item.block_height << " current " << block_index); + return; + } + crypto::hash b_hash = m_blockchain.get_block_id_by_height(disq_extra.item.block_height); + if(b_hash != disq_extra.item.block_hash) + { + MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash + << "; invalid block_hash "); + return; + } + + //get BBL + size_t depth = m_blockchain_based_list->block_height() - disq_extra.item.block_height; + if(m_blockchain_based_list->history_depth() <= depth) + { + MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash + << "; out of history "); + return; + } + + if(disq_extra.signers.size() < graft::generator::REQUIRED_BBQS_VOTES) + { + MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash + << "; lack of signers "); + return; + } + auto& tiers = m_blockchain_based_list->tiers(depth); + + auto bbl_idxs = makeBBLindexes(tiers); + + std::vector bbqs_idxs, qcl_idxs; + graft::generator::select_BBQS_QCL(disq_extra.item.block_hash, bbl_idxs, bbqs_idxs, qcl_idxs); + Ids bbqs = fromIndexes(tiers, bbqs_idxs); + Ids qcl = fromIndexes(tiers, qcl_idxs); + + if(std::none_of(qcl.begin(), qcl.end(), [&disq_extra](crypto::public_key& v)->bool { return v == disq_extra.item.id; } )) + { + std::string id_str = epee::string_tools::pod_to_hex(disq_extra.item.id); + MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash + << "; disqualified id " << id_str << " is not in QCL"); + return; + } + + for(auto& si : disq_extra.signers) + { + if(std::none_of(bbqs.begin(), bbqs.end(), [&si](crypto::public_key& v)->bool { return v == si.signer_id; } )) + { + std::string id_str = epee::string_tools::pod_to_hex(si.signer_id); + MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash + << "; signer id " << id_str << " is not in BBQS"); + return; + } + } + + disqualification disq; + serialization::dump_binary(disq_extra, disq.blob); + disq.block_index = block_index; + disq.id = disq_extra.item.id; + disq.id_str = epee::string_tools::pod_to_hex(disq.id); + + MDEBUG("New disqualification transaction found at block #" << block_index << ", tx_hash=" << tx_hash << ", supernode_id '" << disq.id_str << "'"); + + disquals.push_back(std::move(disq)); +} + +void StakeTransactionProcessor::process_disqualification2_transaction(const transaction& tx, const crypto::hash tx_hash, uint64_t block_index, const crypto::hash& block_hash, StakeTransactionStorage::disqualification2_storage_array& disquals2) +{ + assert(tx.version == 124); + + tx_extra_graft_disqualification2 disq_extra; + if(graft_check_disqualification2(tx, &disq_extra)) + { + MWARNING("Ignore invalid disqualification2 transaction at block #" << block_index << ", tx_hash=" << tx_hash); + return; + } + if(block_index <= disq_extra.item.block_height) + { + MWARNING("Ignore invalid disqualification2 transaction at block #" << block_index << ", tx_hash=" << tx_hash + << "; invalid block_height " << disq_extra.item.block_height << " current " << block_index); + return; + } + crypto::hash b_hash = m_blockchain.get_block_id_by_height(disq_extra.item.block_height); + if(b_hash != disq_extra.item.block_hash) + { + MWARNING("Ignore invalid disqualification2 transaction at block #" << block_index << ", tx_hash=" << tx_hash + << "; invalid block_hash "); + return; + } + + //get BBL + size_t depth = m_blockchain_based_list->block_height() - disq_extra.item.block_height; + if(m_blockchain_based_list->history_depth() <= depth) + { + MWARNING("Ignore invalid disqualification2 transaction at block #" << block_index << ", tx_hash=" << tx_hash + << "; out of history "); + return; + } + + if(disq_extra.signers.size() < graft::generator::REQUIRED_DISQUAL2_VOTES) + { + MWARNING("Ignore invalid disqualification2 transaction at block #" << block_index << ", tx_hash=" << tx_hash + << "; lack of signers "); + return; + } + auto& tiers = m_blockchain_based_list->tiers(depth); + + auto bbl_idxs = makeBBLindexes(tiers); + + std::vector auths_idxs; + graft::generator::select_AuthSample(disq_extra.item.payment_id, bbl_idxs, auths_idxs); + Ids auths = fromIndexes(tiers, auths_idxs); + + for(const auto& id : disq_extra.item.ids) + { + if(std::none_of(auths.begin(), auths.end(), [&id](crypto::public_key& v)->bool { return v == id; } )) + { + std::string id_str = epee::string_tools::pod_to_hex(id); + MWARNING("Ignore invalid disqualification2 transaction at block #" << block_index << ", tx_hash=" << tx_hash + << "; disqualified id " << id_str << " is not in the auth sample"); + return; + } + } + + for(const auto& si : disq_extra.signers) + { + if(std::none_of(auths.begin(), auths.end(), [&si](crypto::public_key& v)->bool { return v == si.signer_id; } )) + { + std::string id_str = epee::string_tools::pod_to_hex(si.signer_id); + MWARNING("Ignore invalid disqualification2 transaction at block #" << block_index << ", tx_hash=" << tx_hash + << "; signer id " << id_str << " is not in the auth sample"); + return; + } + } + + disqualification2_storage_item disq; + serialization::dump_binary(disq_extra, disq.blob); + disq.block_index = block_index; + + std::string ids_str = "("; + { + bool comma = false; + for(const auto& id : disq_extra.item.ids) + { + if(comma) ids_str += ", "; + ids_str += epee::string_tools::pod_to_hex(id); + } + ids_str += ")"; + } + MDEBUG("New disqualification transaction found at block #" << block_index << ", tx_hash=" << tx_hash << ", disqualified supernode ids " << ids_str); + + disquals2.push_back(std::move(disq)); +} + void StakeTransactionProcessor::process_block_stake_transaction(uint64_t block_index, const block& block, const crypto::hash& block_hash, bool update_storage) { if (block_index <= m_storage->get_last_processed_block_index()) @@ -214,6 +380,7 @@ void StakeTransactionProcessor::process_block_stake_transaction(uint64_t block_i } StakeTransactionStorage::disqualification_array disquals; + StakeTransactionStorage::disqualification2_storage_array disquals2; for (const transaction& tx : txs) { @@ -223,83 +390,14 @@ void StakeTransactionProcessor::process_block_stake_transaction(uint64_t block_i { if(tx.version == 123) { - continue; - tx_extra_graft_disqualification disq_extra; - if(graft_check_disqualification(tx, &disq_extra)) - { - MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash); - continue; - } - if(block_index <= disq_extra.item.block_height) - { - MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash - << "; invalid block_height " << disq_extra.item.block_height << " current " << block_index); - continue; - } - crypto::hash b_hash = m_blockchain.get_block_id_by_height(disq_extra.item.block_height); - if(b_hash != disq_extra.item.block_hash) - { - MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash - << "; invalid block_hash "); - continue; - } - - //get BBL - size_t depth = m_blockchain_based_list->block_height() - disq_extra.item.block_height; - if(m_blockchain_based_list->history_depth() <= depth) - { - MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash - << "; out of history "); - continue; - } - - if(disq_extra.signers.size() < graft::generator::REQUIRED_BBQS_VOTES) - { - MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash - << "; lack of signers "); - continue; - } - auto& tiers = m_blockchain_based_list->tiers(depth); - - auto bbl_idxs = makeBBLindexes(tiers); - - std::vector bbqs_idxs, qcl_idxs; - graft::generator::select_BBQS_QCL(disq_extra.item.block_hash, bbl_idxs, bbqs_idxs, qcl_idxs); - Ids bbqs = fromIndexes(tiers, bbqs_idxs); - Ids qcl = fromIndexes(tiers, qcl_idxs); - - if(std::none_of(qcl.begin(), qcl.end(), [&disq_extra](crypto::public_key& v)->bool { return v == disq_extra.item.id; } )) - { - std::string id_str; - epee::string_tools::pod_to_hex(disq_extra.item.id); - MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash - << "; disqualified id " << id_str << " is not in QCL"); - continue; - } - - for(auto& si : disq_extra.signers) - { - if(std::none_of(bbqs.begin(), bbqs.end(), [&si](crypto::public_key& v)->bool { return v == si.signer_id; } )) - { - std::string id_str; - epee::string_tools::pod_to_hex(si.signer_id); - MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash - << "; signer id " << id_str << " is not in BBQS"); - continue; - } - } - - disqualification disq; - ::serialization::dump_binary(disq_extra, disq.blob); - disq.block_index = block_index; - disq.id = disq_extra.item.id; - disq.id_str = epee::string_tools::pod_to_hex(disq.id); - - MDEBUG("New disqualification transaction found at block #" << block_index << ", tx_hash=" << tx_hash << ", supernode_id '" << disq.id_str << "'"); - - disquals.push_back(std::move(disq)); + process_disqualification_transaction(tx, tx_hash, block_index, block_hash, disquals); + continue; + } + if(tx.version == 124) + { + process_disqualification2_transaction(tx, tx_hash, block_index, block_hash, disquals2); continue; - } //if(tx.version == 123) + } stake_transaction stake_tx; @@ -373,6 +471,7 @@ void StakeTransactionProcessor::process_block_stake_transaction(uint64_t block_i } m_storage->add_disquals(disquals); + m_storage->add_disquals2(disquals2); m_stakes_need_update = true; //TODO: cache for stakes diff --git a/src/cryptonote_core/stake_transaction_processor.h b/src/cryptonote_core/stake_transaction_processor.h index aa020bc69..0ec5f5a7c 100644 --- a/src/cryptonote_core/stake_transaction_processor.h +++ b/src/cryptonote_core/stake_transaction_processor.h @@ -52,6 +52,9 @@ class StakeTransactionProcessor void process_block_stake_transaction(uint64_t block_index, const block& block, const crypto::hash& block_hash, bool update_storage = true); void process_block_blockchain_based_list(uint64_t block_index, const block& block, const crypto::hash& block_hash, bool update_storage = true); + void process_disqualification_transaction(const transaction& tx, const crypto::hash tx_hash, uint64_t block_index, const crypto::hash& block_hash, StakeTransactionStorage::disqualification_array& disquals); + void process_disqualification2_transaction(const transaction& tx, const crypto::hash tx_hash, uint64_t block_index, const crypto::hash& block_hash, StakeTransactionStorage::disqualification2_storage_array& disquals2); + private: std::string m_config_dir; Blockchain& m_blockchain; diff --git a/src/cryptonote_core/stake_transaction_storage.cpp b/src/cryptonote_core/stake_transaction_storage.cpp index 3abe35f16..df9162688 100644 --- a/src/cryptonote_core/stake_transaction_storage.cpp +++ b/src/cryptonote_core/stake_transaction_storage.cpp @@ -23,15 +23,18 @@ struct stake_transaction_file_data StakeTransactionStorage::stake_transaction_array& stake_txs; StakeTransactionStorage::block_hash_list& block_hashes; StakeTransactionStorage::disqualification_array& disqualifications; + StakeTransactionStorage::disqualification2_storage_array& disqualifications2_storage; stake_transaction_file_data(uint64_t in_last_processed_block_index, StakeTransactionStorage::stake_transaction_array& in_stake_txs, - size_t in_last_processed_block_hashes_count, StakeTransactionStorage::block_hash_list& in_block_hashes, StakeTransactionStorage::disqualification_array& disqualifications) + size_t in_last_processed_block_hashes_count, StakeTransactionStorage::block_hash_list& in_block_hashes, + StakeTransactionStorage::disqualification_array& disqualifications, StakeTransactionStorage::disqualification2_storage_array& disqualifications2_storage) : last_processed_block_index(in_last_processed_block_index) , last_processed_block_hashes_count(in_last_processed_block_hashes_count) , stake_txs(in_stake_txs) , block_hashes(in_block_hashes) , disqualifications(disqualifications) + , disqualifications2_storage(disqualifications2_storage) { } @@ -41,6 +44,7 @@ struct stake_transaction_file_data FIELD(block_hashes) FIELD(stake_txs) FIELD(disqualifications) + FIELD(disqualifications2_storage) END_SERIALIZE() }; @@ -66,6 +70,7 @@ void StakeTransactionStorage::add_tx(const stake_transaction& tx) void StakeTransactionStorage::add_disquals(const disqualification_array& disqs) { + if(disqs.empty()) return; //sort and merge auto middle = m_disqualifications.insert(m_disqualifications.end(), disqs.begin(), disqs.end()); std::sort(middle, m_disqualifications.end(), disqualification::less_id_str); @@ -74,11 +79,54 @@ void StakeTransactionStorage::add_disquals(const disqualification_array& disqs) m_need_store = true; } +StakeTransactionStorage::disqualification2_array StakeTransactionStorage::disquals2_from_storage(const disqualification2_storage_array& disqs_store) +{ + disqualification2_array disqs; + disqs.reserve(disqs_store.size()); //at least + for(const auto& item_store : disqs_store) + { + tx_extra_graft_disqualification2 extra_d2; + bool res = serialization::parse_binary(item_store.blob, extra_d2); + assert(res); + for(const auto& id : extra_d2.item.ids) + { + disqualification2 d2; + d2.block_index = item_store.block_index; + d2.id_str = epee::string_tools::pod_to_hex(id); + disqs.emplace_back( std::move(d2) ); + } + } + //sort + std::sort(disqs.begin(), disqs.end(), disqualification2::less_id_str); + return disqs; +} + +void StakeTransactionStorage::add_disquals2(const disqualification2_storage_array& disqs_store) +{ + if(disqs_store.empty()) return; + m_disqualifications2_storage.insert(m_disqualifications2_storage.end(), disqs_store.begin(), disqs_store.end()); + //make disqualification2 array from storage items + disqualification2_array disqs = disquals2_from_storage(disqs_store); + //merge + auto middle = m_disqualifications2.insert(m_disqualifications2.end(), disqs.begin(), disqs.end()); + std::inplace_merge(m_disqualifications2.begin(), middle, m_disqualifications2.end(), disqualification2::less_id_str); + + m_need_store = true; +} + bool StakeTransactionStorage::is_disqualified(uint64_t block_number, const std::string& supernode_public_id) const { - disqualification tmp; tmp.id_str = supernode_public_id; - auto pair = std::equal_range(m_disqualifications.begin(), m_disqualifications.end(), tmp, disqualification::less_id_str); - return std::any_of(pair.first, pair.second, [block_number](const disqualification& v){ return v.is_active_for(block_number); }); + {//disqualification + disqualification tmp; tmp.id_str = supernode_public_id; + auto pair = std::equal_range(m_disqualifications.begin(), m_disqualifications.end(), tmp, disqualification::less_id_str); + bool res = std::any_of(pair.first, pair.second, [block_number](const disqualification& v){ return v.is_active_for(block_number); }); + if(res) return true; + } + {//disqualification2 + disqualification2 tmp; tmp.id_str = supernode_public_id; + auto pair = std::equal_range(m_disqualifications2.begin(), m_disqualifications2.end(), tmp, disqualification2::less_id_str); + return std::any_of(pair.first, pair.second, [block_number](const disqualification2& v){ return v.is_active_for(block_number); }); + } } const crypto::hash& StakeTransactionStorage::get_last_processed_block_hash() const @@ -347,7 +395,8 @@ void StakeTransactionStorage::load() StakeTransactionStorage::stake_transaction_array tmp_stake_txs; StakeTransactionStorage::block_hash_list tmp_block_hashes; StakeTransactionStorage::disqualification_array tmp_disqualifications; - stake_transaction_file_data data(0, tmp_stake_txs, 0, tmp_block_hashes, tmp_disqualifications); + StakeTransactionStorage::disqualification2_storage_array tmp_disqualifications2_storage; + stake_transaction_file_data data(0, tmp_stake_txs, 0, tmp_block_hashes, tmp_disqualifications, tmp_disqualifications2_storage); r = ::serialization::parse_binary(buffer, data); @@ -359,6 +408,10 @@ void StakeTransactionStorage::load() std::swap(m_stake_txs, data.stake_txs); std::swap(m_last_processed_block_hashes, data.block_hashes); std::swap(m_disqualifications, data.disqualifications); + std::swap(m_disqualifications2_storage, data.disqualifications2_storage); + + disqualification2_array disqs2 = disquals2_from_storage(m_disqualifications2_storage); + m_disqualifications2.swap(disqs2); m_need_store = false; } @@ -373,7 +426,8 @@ void StakeTransactionStorage::store() const { stake_transaction_file_data data(m_last_processed_block_index, const_cast(m_stake_txs), m_last_processed_block_hashes_count, const_cast(m_last_processed_block_hashes) - , const_cast(m_disqualifications)); + , const_cast(m_disqualifications) + , const_cast(m_disqualifications2_storage)); std::ofstream ostr; ostr.open(m_storage_file_name, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc); diff --git a/src/cryptonote_core/stake_transaction_storage.h b/src/cryptonote_core/stake_transaction_storage.h index 3fa5e6ac9..80f343658 100644 --- a/src/cryptonote_core/stake_transaction_storage.h +++ b/src/cryptonote_core/stake_transaction_storage.h @@ -51,6 +51,7 @@ struct supernode_stake //TODO: find good place for it common with supernode constexpr size_t DISQUALIFICATION_DURATION_BLOCK_COUNT = 10; +constexpr size_t DISQUALIFICATION2_DURATION_BLOCK_COUNT = 10; struct disqualification { @@ -82,6 +83,34 @@ struct disqualification } }; +struct disqualification2_storage_item +{ + std::string blob; //tx_extra_graft_disqualification2 + uint64_t block_index; //block_index where transaction is stored, not that from the blob. disqualifications should be applied from next block + BEGIN_SERIALIZE_OBJECT() + FIELD(blob) + FIELD(block_index) + END_SERIALIZE() +}; + +struct disqualification2 +{ + std::string id_str; + uint64_t block_index; //block_index where transaction is stored, not that from the blob. disqualifications should be applied from next block + //uint64_t unlock_time == block_height + DISQUALIFICATION2_DURATION_BLOCK_COUNT + + bool is_active_for(uint64_t block_index_target) const + { + //from next block where the transaction is stored + return block_index < block_index_target && block_index_target <= block_index + DISQUALIFICATION2_DURATION_BLOCK_COUNT; + } + + static bool less_id_str(const disqualification2& l, const disqualification2& r) + { + return l.id_str < r.id_str; + } +}; + class StakeTransactionStorage { public: @@ -89,6 +118,8 @@ class StakeTransactionStorage typedef std::list block_hash_list; typedef std::vector supernode_stake_array; typedef std::vector disqualification_array; + typedef std::vector disqualification2_storage_array; + typedef std::vector disqualification2_array; typedef vector supernode_disqualification_array; StakeTransactionStorage(const std::string& storage_file_name, uint64_t first_block_number); @@ -117,6 +148,7 @@ class StakeTransactionStorage /// Add transaction void add_tx(const stake_transaction&); void add_disquals(const disqualification_array& disqs); + void add_disquals2(const disqualification2_storage_array& disqs_store); /// List of supernode stakes const supernode_stake_array& get_supernode_stakes(uint64_t block_number); @@ -144,6 +176,9 @@ class StakeTransactionStorage typedef std::unordered_map supernode_stake_index_map; + //returns sorted array of disqualification2 from storage items + disqualification2_array disquals2_from_storage(const disqualification2_storage_array& disqs_store); + private: std::string m_storage_file_name; uint64_t m_last_processed_block_index; @@ -157,6 +192,8 @@ class StakeTransactionStorage mutable bool m_need_store; disqualification_array m_disqualifications; //sorted by id_str + disqualification2_array m_disqualifications2; //sorted by id_str + disqualification2_storage_array m_disqualifications2_storage; supernode_disqualification_array m_supernode_disqualifications; //an array valid for m_supernode_stakes_update_block_number }; diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index e25493d8c..b88101f95 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -189,6 +189,7 @@ namespace cryptonote // 2. if tx.version >= 3 and tx.rta_signatures.size() > 0 bool is_disqualification_tx = (tx.version == 123); + bool is_disqualification2_tx = (tx.version == 124); bool is_rta_tx = tx.type == transaction::tx_type_rta; if(is_disqualification_tx) { @@ -199,6 +200,15 @@ namespace cryptonote return false; } } + else if (is_disqualification2_tx) + { + if(!graft_check_disqualification2(tx)) + { + MERROR("Invalid disqualification2 transaction with id " << id); + tvc.m_verifivation_failed = true; + return false; + } + } else if (is_rta_tx) { cryptonote::rta_header rta_hdr; if (!cryptonote::get_graft_rta_header_from_extra(tx, rta_hdr)) { @@ -272,7 +282,8 @@ namespace cryptonote crypto::hash max_used_block_id = null_hash; uint64_t max_used_block_height = 0; cryptonote::txpool_tx_meta_t meta; - bool ch_inp_res = is_disqualification_tx || 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 = is_disqualification_tx || is_disqualification2_tx + || 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 diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index e8ba71e92..448688262 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -2275,7 +2275,7 @@ namespace cryptonote { uint64_t block_height; std::vector stakes; - std::vector disqualifications; //tx_extra_graft_disqualification; + std::vector disqualifications; //tx_extra_graft_disqualification; is it required? looks like is not BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(block_height) KV_SERIALIZE(stakes) diff --git a/src/utils/sample_generator.h b/src/utils/sample_generator.h index 77183a2b8..6765097cd 100644 --- a/src/utils/sample_generator.h +++ b/src/utils/sample_generator.h @@ -112,6 +112,7 @@ constexpr int32_t AUTH_SAMPLE_SIZE = TIERS * ITEMS_PER_TIER; constexpr int32_t BBQS_SIZE = TIERS * ITEMS_PER_TIER; constexpr int32_t QCL_SIZE = TIERS * ITEMS_PER_TIER; constexpr int32_t REQUIRED_BBQS_VOTES = (BBQS_SIZE*2 + (3-1))/3; +constexpr int32_t REQUIRED_DISQUAL2_VOTES = 5; /*! * \brief selectSample - selects a sample such as BBQS and QCl. From 1b83c67eb6371e816eba9239d6eceadc53f4476c Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Mon, 27 May 2019 17:15:43 +0300 Subject: [PATCH 12/12] Disqualification transaction check is performed on tx_memory_pool::add_tx instead of on a new block. --- .../stake_transaction_processor.cpp | 120 ++++++++++++------ .../stake_transaction_processor.h | 4 + src/cryptonote_core/tx_pool.cpp | 9 +- 3 files changed, 90 insertions(+), 43 deletions(-) diff --git a/src/cryptonote_core/stake_transaction_processor.cpp b/src/cryptonote_core/stake_transaction_processor.cpp index c33db14df..f0c8c7063 100644 --- a/src/cryptonote_core/stake_transaction_processor.cpp +++ b/src/cryptonote_core/stake_transaction_processor.cpp @@ -185,44 +185,49 @@ Ids fromIndexes(const Tiers& bbl_tiers, const std::vector& idxs) } //namespace -void StakeTransactionProcessor::process_disqualification_transaction(const transaction& tx, const crypto::hash tx_hash, uint64_t block_index, const crypto::hash& block_hash, StakeTransactionStorage::disqualification_array& disquals) +bool StakeTransactionProcessor::check_disqualification_transaction(const transaction& tx, const crypto::hash tx_hash) { assert(tx.version == 123); + //TODO: find out if such disqualification transaction with tx_hash already exists + tx_extra_graft_disqualification disq_extra; - if(graft_check_disqualification(tx, &disq_extra)) + if(!graft_check_disqualification(tx, &disq_extra)) { - MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash); - return; + MWARNING("Ignore invalid disqualification transaction, tx_hash=" << tx_hash); + return false; } + //TODO: it should be checked somewhere instead of this + /* if(block_index <= disq_extra.item.block_height) { - MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash + MWARNING("Ignore invalid disqualification transaction, tx_hash=" << tx_hash << "; invalid block_height " << disq_extra.item.block_height << " current " << block_index); - return; + return false; } + */ crypto::hash b_hash = m_blockchain.get_block_id_by_height(disq_extra.item.block_height); if(b_hash != disq_extra.item.block_hash) { - MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash + MWARNING("Ignore invalid disqualification transaction, tx_hash=" << tx_hash << "; invalid block_hash "); - return; + return false; } //get BBL size_t depth = m_blockchain_based_list->block_height() - disq_extra.item.block_height; if(m_blockchain_based_list->history_depth() <= depth) { - MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash + MWARNING("Ignore invalid disqualification transaction, tx_hash=" << tx_hash << "; out of history "); - return; + return false; } if(disq_extra.signers.size() < graft::generator::REQUIRED_BBQS_VOTES) { - MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash + MWARNING("Ignore invalid disqualification transaction, tx_hash=" << tx_hash << "; lack of signers "); - return; + return false; } auto& tiers = m_blockchain_based_list->tiers(depth); @@ -236,9 +241,9 @@ void StakeTransactionProcessor::process_disqualification_transaction(const trans if(std::none_of(qcl.begin(), qcl.end(), [&disq_extra](crypto::public_key& v)->bool { return v == disq_extra.item.id; } )) { std::string id_str = epee::string_tools::pod_to_hex(disq_extra.item.id); - MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash + MWARNING("Ignore invalid disqualification transaction, tx_hash=" << tx_hash << "; disqualified id " << id_str << " is not in QCL"); - return; + return false; } for(auto& si : disq_extra.signers) @@ -246,61 +251,58 @@ void StakeTransactionProcessor::process_disqualification_transaction(const trans if(std::none_of(bbqs.begin(), bbqs.end(), [&si](crypto::public_key& v)->bool { return v == si.signer_id; } )) { std::string id_str = epee::string_tools::pod_to_hex(si.signer_id); - MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash + MWARNING("Ignore invalid disqualification transaction, tx_hash=" << tx_hash << "; signer id " << id_str << " is not in BBQS"); - return; + return false; } } - disqualification disq; - serialization::dump_binary(disq_extra, disq.blob); - disq.block_index = block_index; - disq.id = disq_extra.item.id; - disq.id_str = epee::string_tools::pod_to_hex(disq.id); - - MDEBUG("New disqualification transaction found at block #" << block_index << ", tx_hash=" << tx_hash << ", supernode_id '" << disq.id_str << "'"); - - disquals.push_back(std::move(disq)); + return true; } -void StakeTransactionProcessor::process_disqualification2_transaction(const transaction& tx, const crypto::hash tx_hash, uint64_t block_index, const crypto::hash& block_hash, StakeTransactionStorage::disqualification2_storage_array& disquals2) +bool StakeTransactionProcessor::check_disqualification2_transaction(const transaction& tx, const crypto::hash tx_hash) { assert(tx.version == 124); + //TODO: find out if such disqualification transaction with tx_hash already exists + tx_extra_graft_disqualification2 disq_extra; - if(graft_check_disqualification2(tx, &disq_extra)) + if(!graft_check_disqualification2(tx, &disq_extra)) { - MWARNING("Ignore invalid disqualification2 transaction at block #" << block_index << ", tx_hash=" << tx_hash); - return; + MWARNING("Ignore invalid disqualification2 transaction, tx_hash=" << tx_hash); + return false; } + //TODO: it should be checked somewhere instead of this +/* if(block_index <= disq_extra.item.block_height) { - MWARNING("Ignore invalid disqualification2 transaction at block #" << block_index << ", tx_hash=" << tx_hash + MWARNING("Ignore invalid disqualification2 transaction, tx_hash=" << tx_hash << "; invalid block_height " << disq_extra.item.block_height << " current " << block_index); - return; + return false; } +*/ crypto::hash b_hash = m_blockchain.get_block_id_by_height(disq_extra.item.block_height); if(b_hash != disq_extra.item.block_hash) { - MWARNING("Ignore invalid disqualification2 transaction at block #" << block_index << ", tx_hash=" << tx_hash + MWARNING("Ignore invalid disqualification2 transaction, tx_hash=" << tx_hash << "; invalid block_hash "); - return; + return false; } //get BBL size_t depth = m_blockchain_based_list->block_height() - disq_extra.item.block_height; if(m_blockchain_based_list->history_depth() <= depth) { - MWARNING("Ignore invalid disqualification2 transaction at block #" << block_index << ", tx_hash=" << tx_hash + MWARNING("Ignore invalid disqualification2 transaction, tx_hash=" << tx_hash << "; out of history "); - return; + return false; } if(disq_extra.signers.size() < graft::generator::REQUIRED_DISQUAL2_VOTES) { - MWARNING("Ignore invalid disqualification2 transaction at block #" << block_index << ", tx_hash=" << tx_hash + MWARNING("Ignore invalid disqualification2 transaction, tx_hash=" << tx_hash << "; lack of signers "); - return; + return false; } auto& tiers = m_blockchain_based_list->tiers(depth); @@ -315,9 +317,9 @@ void StakeTransactionProcessor::process_disqualification2_transaction(const tran if(std::none_of(auths.begin(), auths.end(), [&id](crypto::public_key& v)->bool { return v == id; } )) { std::string id_str = epee::string_tools::pod_to_hex(id); - MWARNING("Ignore invalid disqualification2 transaction at block #" << block_index << ", tx_hash=" << tx_hash + MWARNING("Ignore invalid disqualification2 transaction, tx_hash=" << tx_hash << "; disqualified id " << id_str << " is not in the auth sample"); - return; + return false; } } @@ -326,12 +328,48 @@ void StakeTransactionProcessor::process_disqualification2_transaction(const tran if(std::none_of(auths.begin(), auths.end(), [&si](crypto::public_key& v)->bool { return v == si.signer_id; } )) { std::string id_str = epee::string_tools::pod_to_hex(si.signer_id); - MWARNING("Ignore invalid disqualification2 transaction at block #" << block_index << ", tx_hash=" << tx_hash + MWARNING("Ignore invalid disqualification2 transaction, tx_hash=" << tx_hash << "; signer id " << id_str << " is not in the auth sample"); - return; + return false; } } + return true; +} + +void StakeTransactionProcessor::process_disqualification_transaction(const transaction& tx, const crypto::hash tx_hash, uint64_t block_index, const crypto::hash& block_hash, StakeTransactionStorage::disqualification_array& disquals) +{ + assert(tx.version == 123); + + tx_extra_graft_disqualification disq_extra; + if(!graft_get_disqualification(tx, disq_extra)) + { + MWARNING("Ignore invalid disqualification transaction at block #" << block_index << ", tx_hash=" << tx_hash); + return; + } + + disqualification disq; + serialization::dump_binary(disq_extra, disq.blob); + disq.block_index = block_index; + disq.id = disq_extra.item.id; + disq.id_str = epee::string_tools::pod_to_hex(disq.id); + + MDEBUG("New disqualification transaction found at block #" << block_index << ", tx_hash=" << tx_hash << ", supernode_id '" << disq.id_str << "'"); + + disquals.push_back(std::move(disq)); +} + +void StakeTransactionProcessor::process_disqualification2_transaction(const transaction& tx, const crypto::hash tx_hash, uint64_t block_index, const crypto::hash& block_hash, StakeTransactionStorage::disqualification2_storage_array& disquals2) +{ + assert(tx.version == 124); + + tx_extra_graft_disqualification2 disq_extra; + if(!graft_get_disqualification2(tx, disq_extra)) + { + MWARNING("Ignore invalid disqualification2 transaction at block #" << block_index << ", tx_hash=" << tx_hash); + return; + } + disqualification2_storage_item disq; serialization::dump_binary(disq_extra, disq.blob); disq.block_index = block_index; diff --git a/src/cryptonote_core/stake_transaction_processor.h b/src/cryptonote_core/stake_transaction_processor.h index 0ec5f5a7c..44519c1e1 100644 --- a/src/cryptonote_core/stake_transaction_processor.h +++ b/src/cryptonote_core/stake_transaction_processor.h @@ -44,6 +44,10 @@ class StakeTransactionProcessor /// Force invoke update handler for blockchain based list void invoke_update_blockchain_based_list_handler(bool force = true, size_t depth = 1); + /// Check that transaction with tx_hash does not exist yet. Those to be disqualified, and signers are in corresponding sets. + bool check_disqualification_transaction(const transaction& tx, const crypto::hash tx_hash); + bool check_disqualification2_transaction(const transaction& tx, const crypto::hash tx_hash); + private: void init_storages_impl(); void process_block(uint64_t block_index, const block& block, const crypto::hash& block_hash, bool update_storage = true); diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index b88101f95..4550262b0 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -193,7 +193,12 @@ namespace cryptonote bool is_rta_tx = tx.type == transaction::tx_type_rta; if(is_disqualification_tx) { - if(!graft_check_disqualification(tx)) + //kept_by_block means that we already have checked the transaction once, and the check should pass. + //But in case we pop more than one block, and as such pop others stake and disqualification transactions in the popped blocks, + //it is possible that check of disqualification transaction will not pass again because of the history change, + //Note, that history change means a change of BBL as a function of block_height and previous stakes and disqualifications. + //Currently the fact is ignored, if we had checked once I assume the disqualifications will be valid, even if cannot be checked against previous history. + if(!kept_by_block && !m_stp->check_disqualification_transaction(tx, id)) { MERROR("Invalid disqualification transaction with id " << id); tvc.m_verifivation_failed = true; @@ -202,7 +207,7 @@ namespace cryptonote } else if (is_disqualification2_tx) { - if(!graft_check_disqualification2(tx)) + if(!kept_by_block && !m_stp->check_disqualification2_transaction(tx, id)) { MERROR("Invalid disqualification2 transaction with id " << id); tvc.m_verifivation_failed = true;