From ebed0b77a8ff38eefe7eefd57f9c85a9f831cdc0 Mon Sep 17 00:00:00 2001 From: jeffro256 Date: Wed, 29 Jan 2025 00:07:52 -0600 Subject: [PATCH] cryptonote_core: misc v17 consensus rules --- src/cryptonote_config.h | 4 ++ src/cryptonote_core/blockchain.cpp | 4 +- src/cryptonote_core/tx_verification_utils.cpp | 53 +++++++++++++++++-- src/cryptonote_core/tx_verification_utils.h | 27 +++++++++- 4 files changed, 81 insertions(+), 7 deletions(-) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 88c8e0c20e3..356c55000c2 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -188,6 +188,10 @@ #define HF_VERSION_BULLETPROOF_PLUS 15 #define HF_VERSION_VIEW_TAGS 15 #define HF_VERSION_2021_SCALING 15 +#define HF_VERSION_FCMP 17 +#define HF_VERSION_REJECT_UNLOCK_TIME 17 +#define HF_VERSION_REJECT_LARGE_EXTRA 17 +#define HF_VERSION_REJECT_UNMIXABLE_V1 17 #define PER_KB_FEE_QUANTIZATION_DECIMALS 8 #define CRYPTONOTE_SCALING_2021_FEE_ROUNDING_PLACES 2 diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 65a72b25eca..42f68d41394 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3436,14 +3436,14 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } // min/max tx version based on HF, and we accept v1 txes if having a non mixable - const size_t max_tx_version = (hf_version <= 3) ? 1 : 2; + const size_t max_tx_version = get_maximum_transaction_version(hf_version); if (tx.version > max_tx_version) { MERROR_VER("transaction version " << (unsigned)tx.version << " is higher than max accepted version " << max_tx_version); tvc.m_verifivation_failed = true; return false; } - const size_t min_tx_version = (n_unmixable > 0 ? 1 : (hf_version >= HF_VERSION_ENFORCE_RCT) ? 2 : 1); + const size_t min_tx_version = get_minimum_transaction_version(hf_version, n_unmixable > 0); if (tx.version < min_tx_version) { MERROR_VER("transaction version " << (unsigned)tx.version << " is lower than min accepted version " << min_tx_version); diff --git a/src/cryptonote_core/tx_verification_utils.cpp b/src/cryptonote_core/tx_verification_utils.cpp index 38d74f0848e..d7dfadf272f 100644 --- a/src/cryptonote_core/tx_verification_utils.cpp +++ b/src/cryptonote_core/tx_verification_utils.cpp @@ -136,7 +136,10 @@ static bool ver_non_input_consensus_templated(TxForwardIt tx_begin, TxForwardIt std::vector rvv; rvv.reserve(static_cast(std::distance(tx_begin, tx_end))); - const size_t max_tx_version = hf_version < HF_VERSION_DYNAMIC_FEE ? 1 : 2; + // We assume transactions have an unmixable ring since it's more permissive. The version is + // checked again in Blockchain::check_tx_inputs() with `has_unmixable_ring` actually resolved. + const size_t min_tx_version = get_minimum_transaction_version(hf_version, /*has_unmixable_ring=*/true); + const size_t max_tx_version = get_maximum_transaction_version(hf_version); const size_t tx_weight_limit = get_transaction_weight_limit(hf_version); @@ -153,13 +156,29 @@ static bool ver_non_input_consensus_templated(TxForwardIt tx_begin, TxForwardIt return false; } - // Rule 2 & 3 - if (tx.version == 0 || tx.version > max_tx_version) + // Rule 2 and Rule 3 + if (tx.version < min_tx_version || tx.version > max_tx_version) { tvc.m_verifivation_failed = true; return false; } + // Rule 8 + if (hf_version >= HF_VERSION_REJECT_UNLOCK_TIME && tx.unlock_time != 0) + { + tvc.m_verifivation_failed = true; + tvc.m_nonzero_unlock_time = true; + return false; + } + + // Rule 9 + if (hf_version >= HF_VERSION_REJECT_LARGE_EXTRA && tx.extra.size() > MAX_TX_EXTRA_SIZE) + { + tvc.m_verifivation_failed = true; + tvc.m_tx_extra_too_big = true; + return false; + } + // Rule 4 const size_t tx_weight = get_transaction_weight(tx, blob_size); if (hf_version >= HF_VERSION_PER_BYTE_FEE && tx_weight > tx_weight_limit) @@ -207,6 +226,34 @@ uint64_t get_transaction_weight_limit(const uint8_t hf_version) return get_min_block_weight(hf_version) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; } +size_t get_minimum_transaction_version(uint8_t hf_version, bool has_unmixable_ring) +{ + if (hf_version >= HF_VERSION_REJECT_UNMIXABLE_V1) + { + return 2; + } + else if (hf_version < HF_VERSION_ENFORCE_RCT) + { + return 1; + } + else // HF_VERSION_ENFORCE_RCT <= hf_version < HF_VERSION_REJECT_UNMIXABLE_V1 + { + return has_unmixable_ring ? 1 : 2; + } +} + +size_t get_maximum_transaction_version(uint8_t hf_version) +{ + if (hf_version >= HF_VERSION_DYNAMIC_FEE) + { + return 2; + } + else // hf_version < HF_VERSION_DYNAMIC_FEE + { + return 1; + } +} + bool ver_rct_non_semantics_simple_cached ( transaction& tx, diff --git a/src/cryptonote_core/tx_verification_utils.h b/src/cryptonote_core/tx_verification_utils.h index 29b032f2162..f0bddf15ce7 100644 --- a/src/cryptonote_core/tx_verification_utils.h +++ b/src/cryptonote_core/tx_verification_utils.h @@ -43,6 +43,27 @@ namespace cryptonote */ uint64_t get_transaction_weight_limit(uint8_t hf_version); +/** + * @brief Get the minimum allowed transaction version + * + * An "unmixable" ring is a ring appearing in block index BI spending a pre-RingCT enote + * (i.e. referencible amount != 0) where the minimum required ring size is greater than the total + * number of pre-RingCT enotes on-chain at block indices < BI with that same amount. + * + * @param hf_version hard fork version + * @param has_unmixable_ring true iff at least one of the rings in the transaction is "unmixable" + * @return the minimum allowed transaction version + */ +size_t get_minimum_transaction_version(uint8_t hf_version, bool has_unmixable_ring); + +/** + * @brief Get the maximum allowed transaction version + * + * @param hf_version hard fork version + * @return the maximum allowed transaction version + */ +size_t get_maximum_transaction_version(uint8_t hf_version); + // Modifying this value should not affect consensus. You can adjust it for performance needs static constexpr const size_t RCT_VER_CACHE_SIZE = 8192; @@ -112,12 +133,14 @@ struct pool_supplement * * List of checks that we do for each transaction: * 1. Check tx blob size < get_max_tx_size() - * 2. Check tx version != 0 - * 3. Check tx version is less than maximum for given hard fork version + * 2. Check tx version >= get_minimum_transaction_version() + * 3. Check tx version <= get_maximum_transaction_version() * 4. Check tx weight < get_transaction_weight_limit() * 5. Passes core::check_tx_semantic() * 6. Passes Blockchain::check_tx_outputs() * 7. Passes ver_mixed_rct_semantics() [Uses batch RingCT verification when applicable] + * 8. Check unlock time is 0 from hardfork v17 + * 9. Check extra size <= MAX_TX_EXTRA_SIZE from hardfork v17 * * For pool_supplement input: * We assume the structure of the pool supplement is already correct: for each value entry, the