diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index 215903a0c..9469d2630 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -470,7 +470,7 @@ namespace cryptonote VARINT_FIELD(timestamp) FIELD(prev_id) FIELD(nonce) - if (major_version >= HF_VERSION_BLOCK_HEADER_MINER_SIG) + if (major_version >= HF_VERSION_BLOCK_HEADER_MINER_SIG && major_version < HF_VERSION_P2POOL) { FIELD(signature) FIELD(vote) diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index 6d2b7e55b..74c8098da 100644 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -196,7 +196,7 @@ namespace boost a & b.timestamp; a & b.prev_id; a & b.nonce; - if (b.major_version >= HF_VERSION_BLOCK_HEADER_MINER_SIG) + if (b.major_version >= HF_VERSION_BLOCK_HEADER_MINER_SIG && b.major_version < HF_VERSION_P2POOL) { a & b.signature; a & b.vote; diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index 74b787500..a951f5c97 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -610,7 +610,7 @@ namespace cryptonote b.nonce = nonce; // Miner Block Header Signing - if (b.major_version >= HF_VERSION_BLOCK_HEADER_MINER_SIG) + if (b.major_version >= HF_VERSION_BLOCK_HEADER_MINER_SIG && b.major_version < HF_VERSION_P2POOL) { // tx key derivation crypto::key_derivation derivation; diff --git a/src/cryptonote_basic/verification_context.h b/src/cryptonote_basic/verification_context.h index 34157218f..931cab9c5 100644 --- a/src/cryptonote_basic/verification_context.h +++ b/src/cryptonote_basic/verification_context.h @@ -53,6 +53,7 @@ namespace cryptonote bool m_overspend; bool m_fee_too_low; bool m_too_few_outputs; + bool m_two_outputs; }; struct block_verification_context diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 821a36f65..5b0438257 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -183,6 +183,7 @@ #define HF_VERSION_PER_BYTE_FEE 12 #define HF_VERSION_SMALLER_BP 13 #define HF_VERSION_LONG_TERM_BLOCK_WEIGHT 13 +#define HF_VERSION_ENFORCE_2_OUTPUTS 20 #define HF_VERSION_MIN_2_OUTPUTS 15 #define HF_VERSION_MIN_V2_COINBASE_TX 15 #define HF_VERSION_SAME_MIXIN 15 @@ -198,10 +199,13 @@ #define HF_VERSION_VIEW_TAGS 20 #define HF_VERSION_2021_SCALING 20 #define HF_VERSION_BLOCK_HEADER_MINER_SIG 18 +#define HF_VERSION_P2POOL 20 #define PER_KB_FEE_QUANTIZATION_DECIMALS 8 #define CRYPTONOTE_SCALING_2021_FEE_ROUNDING_PLACES 2 +#define MIN_MINER_OUTPUTS 50 + #define HASH_OF_HASHES_STEP 512 #define DEFAULT_TXPOOL_MAX_WEIGHT 648000000ull // 3 days at 300000, in bytes diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index f4513b69f..a670787c7 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1412,7 +1412,7 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std: 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) + if (hf_version >= HF_VERSION_BLOCK_HEADER_MINER_SIG && hf_version < HF_VERSION_P2POOL) { // sanity checks if (b.miner_tx.vout.size() != 1) @@ -1445,6 +1445,19 @@ bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height, } } + if (hf_version >= HF_VERSION_P2POOL) + { + if (b.miner_tx.vout.size() < MIN_MINER_OUTPUTS) + { + MWARNING("Coinbase transaction must have more than " << MIN_MINER_OUTPUTS << " outputs"); + return false; + } + for (const auto &o: b.miner_tx.vout) + { + CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_key), false, "Wrong txout type: " << o.target.type().name() << ", expected txout_to_key in transaction id=" << get_transaction_hash(b.miner_tx)); + } + } + 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"); @@ -3417,7 +3430,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, const uint8_t hf_version = m_hardfork->get_current_version(); - if (hf_version >= HF_VERSION_MIN_2_OUTPUTS) + if (hf_version >= HF_VERSION_MIN_2_OUTPUTS && hf_version < HF_VERSION_ENFORCE_2_OUTPUTS) { if (tx.version >= 2) { @@ -3430,6 +3443,22 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } } + if (hf_version >= HF_VERSION_ENFORCE_2_OUTPUTS) + { + if (tx.vout.size() != 2) + { + MERROR_VER("Tx " << get_transaction_hash(tx) << " must have only two outputs"); + tvc.m_two_outputs = true; + return false; + } + // cap the size of TX extra field + if (tx.extra.size() > 44) + { + MERROR_VER("TX extra field is too large"); + return false; + } + } + // from hard fork 2, we require mixin at least 2 unless one output cannot mix with 2 others // if one output cannot mix with 2 others, we accept at most 1 output that can mix if (hf_version >= 2) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 4d50839fe..63b6ad72f 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1286,6 +1286,8 @@ namespace cryptonote add_reason(reason, "fee too low"); if ((res.too_few_outputs = tvc.m_too_few_outputs)) add_reason(reason, "too few outputs"); + if ((res.two_outputs = tvc.m_two_outputs)) + add_reason(reason, "only two outputs allowed"); const std::string punctuation = reason.empty() ? "" : ": "; if (tvc.m_verifivation_failed) { @@ -2201,7 +2203,7 @@ namespace cryptonote return false; } b.nonce = req.starting_nonce; - if (b.major_version >= HF_VERSION_BLOCK_HEADER_MINER_SIG) + if (b.major_version >= HF_VERSION_BLOCK_HEADER_MINER_SIG && b.major_version < HF_VERSION_P2POOL) { b.signature = {}; b.vote = 0; diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 72f971174..a456a5675 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -591,6 +591,7 @@ namespace cryptonote bool overspend; bool fee_too_low; bool too_few_outputs; + bool two_outputs; bool sanity_check_failed; BEGIN_KV_SERIALIZE_MAP() @@ -605,6 +606,7 @@ namespace cryptonote KV_SERIALIZE(overspend) KV_SERIALIZE(fee_too_low) KV_SERIALIZE(too_few_outputs) + KV_SERIALIZE(two_outputs) KV_SERIALIZE(sanity_check_failed) END_KV_SERIALIZE_MAP() }; diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index 2acc66bd6..cdd6ba004 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -422,6 +422,11 @@ namespace rpc if (!res.error_details.empty()) res.error_details += " and "; res.error_details += "too few outputs"; } + if (tvc.m_two_outputs) + { + if (!res.error_details.empty()) res.error_details += " and "; + res.error_details += "only two outputs allowed"; + } if (res.error_details.empty()) { res.error_details = "an unknown issue was found with the transaction"; @@ -895,7 +900,7 @@ 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) + if (b.major_version >= HF_VERSION_BLOCK_HEADER_MINER_SIG && b.major_version < HF_VERSION_P2POOL) { header.signature = b.signature; header.vote = b.vote; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 982014508..621b5283d 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -206,6 +206,8 @@ namespace add_reason(reason, "invalid output"); if (res.too_few_outputs) add_reason(reason, "too few outputs"); + if (res.two_outputs) + add_reason(reason, "only two outputs allowed"); if (res.too_big) add_reason(reason, "too big"); if (res.overspend)