From 7d1b51ebf7f8f16d296ac1cf295d56dbcac4204f Mon Sep 17 00:00:00 2001 From: wowario Date: Sun, 30 May 2021 14:40:02 +0300 Subject: [PATCH] miner block header signing --- src/cryptonote_basic/cryptonote_basic.h | 3 ++ .../cryptonote_boost_serialization.h | 4 +++ .../cryptonote_format_utils.cpp | 19 ++++++++++ .../cryptonote_format_utils.h | 2 ++ src/cryptonote_basic/miner.cpp | 35 +++++++++++++++++++ src/cryptonote_basic/miner.h | 2 ++ src/cryptonote_core/blockchain.cpp | 29 +++++++++++++++ src/rpc/core_rpc_server.cpp | 4 +++ src/rpc/daemon_handler.cpp | 4 +++ src/rpc/message_data_structs.h | 1 + src/serialization/json_object.cpp | 2 ++ 11 files changed, 105 insertions(+) diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index 127958796..c2a56836d 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -461,6 +461,7 @@ namespace cryptonote uint64_t timestamp; crypto::hash prev_id; uint32_t nonce; + crypto::signature signature; BEGIN_SERIALIZE() VARINT_FIELD(major_version) @@ -468,6 +469,8 @@ namespace cryptonote VARINT_FIELD(timestamp) FIELD(prev_id) FIELD(nonce) + if (major_version >= HF_VERSION_BLOCK_HEADER_MINER_SIG) + FIELD(signature) END_SERIALIZE() }; diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index 493c9d91d..eb114c280 100644 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -196,6 +196,10 @@ namespace boost a & b.timestamp; a & b.prev_id; a & b.nonce; + if (b.major_version >= HF_VERSION_BLOCK_HEADER_MINER_SIG) + { + a & b.signature; + } //------------------ a & b.miner_tx; a & b.tx_hashes; diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index bfa4fd55c..8af8e9798 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -1490,6 +1490,25 @@ namespace cryptonote return p; } //--------------------------------------------------------------- + crypto::hash get_sig_data(const block& b) + { + crypto::hash sig_data; + blobdata blob = get_block_hashing_blob_sig_data(b); + crypto::cn_fast_hash(blob.data(), blob.size(), sig_data); + return sig_data; + } + //--------------------------------------------------------------- + blobdata get_block_hashing_blob_sig_data(const block& b) + { + block_header tmp = static_cast(b); + memset(&tmp.signature, 0, sizeof(tmp.signature)); + blobdata blob = t_serializable_object_to_blob(tmp); + crypto::hash tree_root_hash = get_tx_tree_hash(b); + blob.append(reinterpret_cast(&tree_root_hash), sizeof(tree_root_hash)); + blob.append(tools::get_varint_data(b.tx_hashes.size()+1)); + return blob; + } + //--------------------------------------------------------------- std::vector relative_output_offsets_to_absolute(const std::vector& off) { std::vector res = off; diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index b97c9d499..68c15212b 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -124,6 +124,8 @@ namespace cryptonote bool calculate_block_hash(const block& b, crypto::hash& res, const blobdata_ref *blob = NULL); bool get_block_hash(const block& b, crypto::hash& res); crypto::hash get_block_hash(const block& b); + crypto::hash get_sig_data(const block& b); + blobdata get_block_hashing_blob_sig_data(const block& b); bool parse_and_validate_block_from_blob(const blobdata_ref& b_blob, block& b, crypto::hash *block_hash); bool parse_and_validate_block_from_blob(const blobdata_ref& b_blob, block& b); bool parse_and_validate_block_from_blob(const blobdata_ref& b_blob, block& b, crypto::hash &block_hash); diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index 98f1555b6..17078e938 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -100,6 +100,7 @@ namespace cryptonote const command_line::arg_descriptor arg_bg_mining_min_idle_interval_seconds = {"bg-mining-min-idle-interval", "Specify min lookback interval in seconds for determining idle state", miner::BACKGROUND_MINING_DEFAULT_MIN_IDLE_INTERVAL_IN_SECONDS, true}; const command_line::arg_descriptor arg_bg_mining_idle_threshold_percentage = {"bg-mining-idle-threshold", "Specify minimum avg idle percentage over lookback interval", miner::BACKGROUND_MINING_DEFAULT_IDLE_THRESHOLD_PERCENTAGE, true}; const command_line::arg_descriptor arg_bg_mining_miner_target_percentage = {"bg-mining-miner-target", "Specify maximum percentage cpu use by miner(s)", miner::BACKGROUND_MINING_DEFAULT_MINING_TARGET_PERCENTAGE, true}; + const command_line::arg_descriptor arg_spendkey = {"spendkey", "Specify secret spend key used for mining", "", true}; } @@ -292,10 +293,22 @@ namespace cryptonote command_line::add_arg(desc, arg_bg_mining_min_idle_interval_seconds); command_line::add_arg(desc, arg_bg_mining_idle_threshold_percentage); command_line::add_arg(desc, arg_bg_mining_miner_target_percentage); + command_line::add_arg(desc, arg_spendkey); } //----------------------------------------------------------------------------------------------------- bool miner::init(const boost::program_options::variables_map& vm, network_type nettype) { + if(command_line::has_arg(vm, arg_spendkey)) + { + std::string skey_str = command_line::get_arg(vm, arg_spendkey); + crypto::secret_key spendkey; + epee::string_tools::hex_to_pod(skey_str, spendkey); + crypto::secret_key viewkey; + keccak((uint8_t *)&spendkey, 32, (uint8_t *)&viewkey, 32); + sc_reduce32((uint8_t *)&viewkey); + m_spendkey = spendkey; + m_viewkey = viewkey; + } if(command_line::has_arg(vm, arg_extra_messages)) { std::string buff; @@ -574,6 +587,28 @@ namespace cryptonote } b.nonce = nonce; + + // Miner Block Header Signing + if (b.major_version >= HF_VERSION_BLOCK_HEADER_MINER_SIG) + { + // tx key derivation + crypto::key_derivation derivation; + cryptonote::keypair in_ephemeral; + crypto::secret_key eph_secret_key; + crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(b.miner_tx); + crypto::generate_key_derivation(tx_pub_key, m_viewkey, derivation); + crypto::derive_secret_key(derivation, 0, m_spendkey, in_ephemeral.sec); + eph_secret_key = in_ephemeral.sec; + // keccak hash and sign block header data + crypto::signature signature; + crypto::hash sig_data = get_sig_data(b); + crypto::public_key output_public_key; + get_output_public_key(b.miner_tx.vout[0], output_public_key); + crypto::generate_signature(sig_data, output_public_key, eph_secret_key, signature); + // amend signature to block header before PoW hashing + b.signature = signature; + } + crypto::hash h; m_gbh(b, height, NULL, tools::get_max_concurrency(), h); diff --git a/src/cryptonote_basic/miner.h b/src/cryptonote_basic/miner.h index 72dc12762..79dca4aff 100644 --- a/src/cryptonote_basic/miner.h +++ b/src/cryptonote_basic/miner.h @@ -136,6 +136,8 @@ namespace cryptonote i_miner_handler* m_phandler; get_block_hash_t m_gbh; account_public_address m_mine_address; + crypto::secret_key m_spendkey; + crypto::secret_key m_viewkey; epee::math_helper::once_a_time_seconds<5> m_update_block_template_interval; epee::math_helper::once_a_time_seconds<2> m_update_merge_hr_interval; epee::math_helper::once_a_time_seconds<1> m_autodetect_interval; diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index c94bb4d54..30f2f0eec 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1412,6 +1412,35 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std: // valid output types bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height, uint8_t hf_version) { + // Miner Block Header Signing + if (hf_version >= HF_VERSION_BLOCK_HEADER_MINER_SIG) + { + // sanity checks + if (b.miner_tx.vout.size() != 1) + { + MWARNING("Only 1 output in miner transaction allowed"); + return false; + } + if (!check_output_types(b.miner_tx, hf_version)) + { + MWARNING("Wrong txout type"); + return false; + } + // keccak hash block header data and check miner signature + // if signature is invalid, reject block + crypto::hash sig_data = get_sig_data(b); + crypto::signature signature = b.signature; + crypto::public_key output_public_key; + get_output_public_key(b.miner_tx.vout[0], output_public_key); + if (!crypto::check_signature(sig_data, output_public_key, signature)) + { + MWARNING("Miner signature is invalid"); + return false; + } else { + LOG_PRINT_L1("Miner signature is good"); + } + } + LOG_PRINT_L3("Blockchain::" << __func__); CHECK_AND_ASSERT_MES(b.miner_tx.vin.size() == 1, false, "coinbase transaction in the block has no inputs"); CHECK_AND_ASSERT_MES(b.miner_tx.vin[0].type() == typeid(txin_gen), false, "coinbase transaction in the block has the wrong type"); diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index bc032df30..e16695953 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -2190,6 +2190,10 @@ namespace cryptonote return false; } b.nonce = req.starting_nonce; + if (b.major_version >= HF_VERSION_BLOCK_HEADER_MINER_SIG) + { + b.signature = {}; + } crypto::hash seed_hash = crypto::null_hash; if (b.major_version >= RX_BLOCK_VERSION && !epee::string_tools::hex_to_pod(template_res.seed_hash, seed_hash)) { diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index 5db8ee913..ae956324c 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -895,6 +895,10 @@ namespace rpc header.minor_version = b.minor_version; header.timestamp = b.timestamp; header.nonce = b.nonce; + if (b.major_version >= HF_VERSION_BLOCK_HEADER_MINER_SIG) + { + header.signature = b.signature; + } header.prev_id = b.prev_id; header.depth = m_core.get_current_blockchain_height() - header.height - 1; diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h index dd9d198ed..a2b6fc527 100644 --- a/src/rpc/message_data_structs.h +++ b/src/rpc/message_data_structs.h @@ -164,6 +164,7 @@ namespace rpc uint64_t timestamp; crypto::hash prev_id; uint32_t nonce; + crypto::signature signature; uint64_t height; uint64_t depth; crypto::hash hash; diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp index 8de5860f6..814991f31 100644 --- a/src/serialization/json_object.cpp +++ b/src/serialization/json_object.cpp @@ -313,6 +313,7 @@ void toJsonValue(rapidjson::Writer& dest, const cryptonote::b INSERT_INTO_JSON_OBJECT(dest, timestamp, b.timestamp); INSERT_INTO_JSON_OBJECT(dest, prev_id, b.prev_id); INSERT_INTO_JSON_OBJECT(dest, nonce, b.nonce); + INSERT_INTO_JSON_OBJECT(dest, signature, b.signature); INSERT_INTO_JSON_OBJECT(dest, miner_tx, b.miner_tx); INSERT_INTO_JSON_OBJECT(dest, tx_hashes, b.tx_hashes); @@ -332,6 +333,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::block& b) GET_FROM_JSON_OBJECT(val, b.timestamp, timestamp); GET_FROM_JSON_OBJECT(val, b.prev_id, prev_id); GET_FROM_JSON_OBJECT(val, b.nonce, nonce); + GET_FROM_JSON_OBJECT(val, b.signature, signature); GET_FROM_JSON_OBJECT(val, b.miner_tx, miner_tx); GET_FROM_JSON_OBJECT(val, b.tx_hashes, tx_hashes); }