diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index c70ae1df1..4cc66c7c3 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -450,6 +450,7 @@ namespace cryptonote uint64_t timestamp; crypto::hash prev_id; uint32_t nonce; + crypto::signature signature; BEGIN_SERIALIZE() VARINT_FIELD(major_version) @@ -457,6 +458,8 @@ namespace cryptonote VARINT_FIELD(timestamp) FIELD(prev_id) FIELD(nonce) + if (major_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 58bc88f31..524df1f9f 100644 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -185,6 +185,10 @@ namespace boost a & b.timestamp; a & b.prev_id; a & b.nonce; + if (b.major_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 87c60a61d..22a422a7b 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -1286,6 +1286,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 636a88b9a..e9bd267ce 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -120,6 +120,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 43921d2ef..a6df0d953 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -99,6 +99,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}; } @@ -291,10 +292,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; @@ -573,6 +586,27 @@ namespace cryptonote } b.nonce = nonce; + + // Miner Block Header Signing + if (b.major_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 eph_pub_key = boost::get(b.miner_tx.vout[0].target).key; + crypto::generate_signature(sig_data, eph_pub_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 df3f56f68..9faf272ef 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_config.h b/src/cryptonote_config.h index fcfad08c7..d8bf8f729 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -43,6 +43,7 @@ #define CURRENT_TRANSACTION_VERSION 2 #define CURRENT_BLOCK_MAJOR_VERSION 7 #define CURRENT_BLOCK_MINOR_VERSION 7 +#define BLOCK_HEADER_MINER_SIG 18 #define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT_V2 300*2 #define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT 60*60*2 #define CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE 4 diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 96da2a6bc..1c63d1b6e 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1377,6 +1377,34 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std: // a non-overflowing tx amount (dubious necessity on this check) bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height, uint8_t hf_version) { + // Miner Block Header Signing + if (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 (b.miner_tx.vout[0].target.type() != typeid(txout_to_key)) + { + 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 eph_pub_key = boost::get(b.miner_tx.vout[0].target).key; + if (!crypto::check_signature(sig_data, eph_pub_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 b82fbbdc4..fea0084c0 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1934,6 +1934,10 @@ namespace cryptonote return false; } b.nonce = req.starting_nonce; + if (b.major_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 cdfc363a9..382f53697 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 >= 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 86424653f..d5ce770f5 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 28e207ff2..ebf4c3d32 100644 --- a/src/serialization/json_object.cpp +++ b/src/serialization/json_object.cpp @@ -312,6 +312,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); @@ -331,6 +332,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); }