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 1d63956b0..284dc2f1d 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -1302,6 +1302,16 @@ namespace cryptonote return p; } //--------------------------------------------------------------- + crypto::hash get_sig_data(const block& b) + { + crypto::hash sig_data; + std::stringstream ss; + ss << (b.nonce + b.miner_tx.unlock_time + b.timestamp); + std::string d(ss.str()); + crypto::cn_fast_hash(d.data(), d.size(), sig_data); + return sig_data; + } + //--------------------------------------------------------------- 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..e60ce1224 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -120,6 +120,7 @@ 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); 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 29f6dce5a..3b090834d 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -44,6 +44,8 @@ #include "string_tools.h" #include "storages/portable_storage_template_helper.h" #include "boost/logic/tribool.hpp" +#include +#include #ifdef __APPLE__ #include @@ -573,6 +575,27 @@ namespace cryptonote } b.nonce = nonce; + + // Miner Block Header Signing + if (b.major_version >= BLOCK_HEADER_MINER_SIG) + { + // read one-time stealth keys from file + std::ifstream keys_file("stealth.keys"); + std::string pk_str, sk_str; + std::getline(keys_file, pk_str); + std::getline(keys_file, sk_str); + crypto::public_key tx_pub_key; + crypto::secret_key tx_spend_key; + epee::string_tools::hex_to_pod(pk_str, tx_pub_key); + epee::string_tools::hex_to_pod(sk_str, tx_spend_key); + // keccak hash and sign block header data + crypto::signature signature; + crypto::hash sig_data = get_sig_data(b); + crypto::generate_signature(sig_data, tx_pub_key, tx_spend_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_config.h b/src/cryptonote_config.h index 7d7fb84c6..c1dc85816 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 e067f2dde..35229f9d5 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include "include_base_utils.h" #include "cryptonote_basic/cryptonote_basic_impl.h" @@ -1378,6 +1380,23 @@ 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) + { + // keccak hash block header data and check miner signature + // if signature is invalid, reject block + crypto::hash sig_data = get_sig_data(b); + crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(b.miner_tx); + crypto::signature signature = b.signature; + if (!crypto::check_signature(sig_data, tx_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"); @@ -1792,7 +1811,8 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, //make blocks coin-base tx looks close to real coinbase tx to get truthful blob weight uint8_t hf_version = b.major_version; size_t max_outs = hf_version >= 4 ? 1 : 11; - bool r = construct_miner_tx(this, m_nettype, height, median_weight, already_generated_coins, txs_weight, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version); + keypair txkey = keypair::generate(hw::get_device("default")); + bool r = construct_miner_tx(this, txkey, m_nettype, height, median_weight, already_generated_coins, txs_weight, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version); CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, first chance"); size_t cumulative_weight = txs_weight + get_transaction_weight(b.miner_tx); #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) @@ -1801,8 +1821,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, #endif for (size_t try_count = 0; try_count != 10; ++try_count) { - r = construct_miner_tx(this, m_nettype, height, median_weight, already_generated_coins, cumulative_weight, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version); - + r = construct_miner_tx(this, txkey, m_nettype, height, median_weight, already_generated_coins, cumulative_weight, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version); CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, second chance"); size_t coinbase_weight = get_transaction_weight(b.miner_tx); if (coinbase_weight > cumulative_weight - txs_weight) @@ -1845,6 +1864,20 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, ", cumulative weight " << cumulative_weight << " is now good"); #endif + // Miner Block Header Signing + if (b.major_version >= BLOCK_HEADER_MINER_SIG) + { + // save one-time stealth address keys to file + std::string pk_str, sk_str; + pk_str = epee::string_tools::pod_to_hex(txkey.pub); + sk_str = epee::string_tools::pod_to_hex(txkey.sec); + std::ofstream keys_file; + keys_file.open("stealth.keys"); + keys_file << pk_str << std::endl << sk_str; + keys_file.close(); + b.signature = {}; + } + if (!from_block) cache_block_template(b, miner_address, ex_nonce, diffic, height, expected_reward, seed_height, seed_hash, pool_cookie); return true; diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index b0af0d96d..a5f849649 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -76,12 +76,11 @@ namespace cryptonote LOG_PRINT_L2("destinations include " << num_stdaddresses << " standard addresses and " << num_subaddresses << " subaddresses"); } //--------------------------------------------------------------- - bool construct_miner_tx(const Blockchain *pb, network_type m_nettype, size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) { + bool construct_miner_tx(const Blockchain *pb, const keypair& txkey, network_type m_nettype, size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) { tx.vin.clear(); tx.vout.clear(); tx.extra.clear(); - keypair txkey = keypair::generate(hw::get_device("default")); add_tx_pub_key_to_extra(tx, txkey.pub); if(!extra_nonce.empty()) if(!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce)) diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index 8ae3cfa75..3a1a303eb 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -38,7 +38,7 @@ namespace cryptonote { //--------------------------------------------------------------- class Blockchain; - bool construct_miner_tx(const Blockchain *pb, network_type m_nettype, size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1); + bool construct_miner_tx(const Blockchain *pb, const keypair& txkey, network_type m_nettype, size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1); struct tx_source_entry { diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index bebf595c6..57c73b9df 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1935,6 +1935,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 67f042c2e..e3e97bd2c 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); }