aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cryptonote_config.h3
-rw-r--r--src/cryptonote_core/blockchain.cpp49
-rw-r--r--src/cryptonote_core/blockchain.h3
-rw-r--r--tests/core_tests/block_validation.cpp15
-rw-r--r--tests/core_tests/block_validation.h12
-rw-r--r--tests/core_tests/chaingen_main.cpp1
6 files changed, 70 insertions, 13 deletions
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
index 173b454f6..4147b48ee 100644
--- a/src/cryptonote_config.h
+++ b/src/cryptonote_config.h
@@ -160,6 +160,9 @@
#define HF_VERSION_SMALLER_BP 10
#define HF_VERSION_LONG_TERM_BLOCK_WEIGHT 10
#define HF_VERSION_MIN_2_OUTPUTS 12
+#define HF_VERSION_MIN_V2_COINBASE_TX 12
+#define HF_VERSION_SAME_MIXIN 12
+#define HF_VERSION_REJECT_SIGS_IN_COINBASE 12
#define PER_KB_FEE_QUANTIZATION_DECIMALS 8
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 8ed0e526a..0e3edb8e1 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -1201,11 +1201,19 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
// one input, of type txin_gen, with height set to the block's height
// correct miner tx unlock time
// a non-overflowing tx amount (dubious necessity on this check)
-bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height)
+bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height, uint8_t hf_version)
{
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");
+ CHECK_AND_ASSERT_MES(b.miner_tx.version > 1 || hf_version < HF_VERSION_MIN_V2_COINBASE_TX, false, "Invalid coinbase transaction version");
+
+ // for v2 txes (ringct), we only accept empty rct signatures for miner transactions,
+ if (hf_version >= HF_VERSION_REJECT_SIGS_IN_COINBASE && b.miner_tx.version >= 2)
+ {
+ CHECK_AND_ASSERT_MES(b.miner_tx.rct_signatures.type == rct::RCTTypeNull, false, "RingCT signatures not allowed in coinbase transactions");
+ }
+
if(boost::get<txin_gen>(b.miner_tx.vin[0]).height != height)
{
MWARNING("The miner transaction in block has invalid height: " << boost::get<txin_gen>(b.miner_tx.vin[0]).height << ", expected: " << height);
@@ -1712,6 +1720,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
}
// this is a cheap test
+ const uint8_t hf_version = m_hardfork->get_ideal_version(block_height);
if (!m_hardfork->check_for_height(b, block_height))
{
LOG_PRINT_L1("Block with id: " << id << std::endl << "has old version for height " << block_height);
@@ -1770,7 +1779,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
return false;
}
- if(!prevalidate_miner_transaction(b, bei.height))
+ if(!prevalidate_miner_transaction(b, bei.height, hf_version))
{
MERROR_VER("Block with id: " << epee::string_tools::pod_to_hex(id) << " (as alternative) has incorrect miner transaction.");
bvc.m_verifivation_failed = true;
@@ -2858,7 +2867,8 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
if (hf_version >= 2)
{
size_t n_unmixable = 0, n_mixable = 0;
- size_t mixin = std::numeric_limits<size_t>::max();
+ size_t min_actual_mixin = std::numeric_limits<size_t>::max();
+ size_t max_actual_mixin = 0;
const size_t min_mixin = hf_version >= HF_VERSION_MIN_MIXIN_10 ? 10 : hf_version >= HF_VERSION_MIN_MIXIN_6 ? 6 : hf_version >= HF_VERSION_MIN_MIXIN_4 ? 4 : 2;
for (const auto& txin : tx.vin)
{
@@ -2883,29 +2893,43 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
else
++n_mixable;
}
- if (in_to_key.key_offsets.size() - 1 < mixin)
- mixin = in_to_key.key_offsets.size() - 1;
+ size_t ring_mixin = in_to_key.key_offsets.size() - 1;
+ if (ring_mixin < min_actual_mixin)
+ min_actual_mixin = ring_mixin;
+ if (ring_mixin > max_actual_mixin)
+ max_actual_mixin = ring_mixin;
+ }
+ }
+ MDEBUG("Mixin: " << min_actual_mixin << "-" << max_actual_mixin);
+
+ if (hf_version >= HF_VERSION_SAME_MIXIN)
+ {
+ if (min_actual_mixin != max_actual_mixin)
+ {
+ MERROR_VER("Tx " << get_transaction_hash(tx) << " has varying ring size (" << (min_actual_mixin + 1) << "-" << (max_actual_mixin + 1) << "), it should be constant");
+ tvc.m_low_mixin = true;
+ return false;
}
}
- if (((hf_version == HF_VERSION_MIN_MIXIN_10 || hf_version == HF_VERSION_MIN_MIXIN_10+1) && mixin != 10) || (hf_version >= HF_VERSION_MIN_MIXIN_10+2 && mixin > 10))
+ if (((hf_version == HF_VERSION_MIN_MIXIN_10 || hf_version == HF_VERSION_MIN_MIXIN_10+1) && min_actual_mixin != 10) || (hf_version >= HF_VERSION_MIN_MIXIN_10+2 && min_actual_mixin > 10))
{
- MERROR_VER("Tx " << get_transaction_hash(tx) << " has invalid ring size (" << (mixin + 1) << "), it should be 11");
+ MERROR_VER("Tx " << get_transaction_hash(tx) << " has invalid ring size (" << (min_actual_mixin + 1) << "), it should be 11");
tvc.m_low_mixin = true;
return false;
}
- if (mixin < min_mixin)
+ if (min_actual_mixin < min_mixin)
{
if (n_unmixable == 0)
{
- MERROR_VER("Tx " << get_transaction_hash(tx) << " has too low ring size (" << (mixin + 1) << "), and no unmixable inputs");
+ MERROR_VER("Tx " << get_transaction_hash(tx) << " has too low ring size (" << (min_actual_mixin + 1) << "), and no unmixable inputs");
tvc.m_low_mixin = true;
return false;
}
if (n_mixable > 1)
{
- MERROR_VER("Tx " << get_transaction_hash(tx) << " has too low ring size (" << (mixin + 1) << "), and more than one mixable input with unmixable inputs");
+ MERROR_VER("Tx " << get_transaction_hash(tx) << " has too low ring size (" << (min_actual_mixin + 1) << "), and more than one mixable input with unmixable inputs");
tvc.m_low_mixin = true;
return false;
}
@@ -3588,9 +3612,10 @@ leave:
}
// this is a cheap test
+ const uint8_t hf_version = get_current_hard_fork_version();
if (!m_hardfork->check(bl))
{
- MERROR_VER("Block with id: " << id << std::endl << "has old version: " << (unsigned)bl.major_version << std::endl << "current: " << (unsigned)m_hardfork->get_current_version());
+ MERROR_VER("Block with id: " << id << std::endl << "has old version: " << (unsigned)bl.major_version << std::endl << "current: " << (unsigned)hf_version);
bvc.m_verifivation_failed = true;
goto leave;
}
@@ -3695,7 +3720,7 @@ leave:
TIME_MEASURE_START(t3);
// sanity check basic miner tx properties;
- if(!prevalidate_miner_transaction(bl, blockchain_height))
+ if(!prevalidate_miner_transaction(bl, blockchain_height, hf_version))
{
MERROR_VER("Block with id: " << id << " failed to pass prevalidation");
bvc.m_verifivation_failed = true;
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index f58059a6d..f32645949 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -1245,10 +1245,11 @@ namespace cryptonote
*
* @param b the block containing the miner transaction
* @param height the height at which the block will be added
+ * @param hf_version the consensus rules to apply
*
* @return false if anything is found wrong with the miner transaction, otherwise true
*/
- bool prevalidate_miner_transaction(const block& b, uint64_t height);
+ bool prevalidate_miner_transaction(const block& b, uint64_t height, uint8_t hf_version);
/**
* @brief validates a miner (coinbase) transaction
diff --git a/tests/core_tests/block_validation.cpp b/tests/core_tests/block_validation.cpp
index 566ec1440..55312bc15 100644
--- a/tests/core_tests/block_validation.cpp
+++ b/tests/core_tests/block_validation.cpp
@@ -640,3 +640,18 @@ bool gen_block_invalid_binary_format::check_all_blocks_purged(cryptonote::core&
return true;
}
+
+bool gen_block_late_v1_coinbase_tx::generate(std::vector<test_event_entry>& events) const
+{
+ BLOCK_VALIDATION_INIT_GENERATE();
+
+ block blk_1;
+ generator.construct_block_manually(blk_1, blk_0, miner_account,
+ test_generator::bf_major_ver | test_generator::bf_minor_ver,
+ HF_VERSION_MIN_V2_COINBASE_TX, HF_VERSION_MIN_V2_COINBASE_TX);
+ events.push_back(blk_1);
+
+ DO_CALLBACK(events, "check_block_purged");
+
+ return true;
+}
diff --git a/tests/core_tests/block_validation.h b/tests/core_tests/block_validation.h
index 4a65b029e..2393e1b01 100644
--- a/tests/core_tests/block_validation.h
+++ b/tests/core_tests/block_validation.h
@@ -206,3 +206,15 @@ struct gen_block_invalid_binary_format : public test_chain_unit_base
private:
size_t m_corrupt_blocks_begin_idx;
};
+
+struct gen_block_late_v1_coinbase_tx : public gen_block_verification_base<1>
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<>
+struct get_test_options<gen_block_late_v1_coinbase_tx> {
+ const std::pair<uint8_t, uint64_t> hard_forks[3] = {std::make_pair(1, 0), std::make_pair(HF_VERSION_MIN_V2_COINBASE_TX, 1), std::make_pair(0, 0)};
+ const cryptonote::test_options test_options = {
+ hard_forks, 0
+ };
+};
diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp
index cb35cfde2..4ee71466e 100644
--- a/tests/core_tests/chaingen_main.cpp
+++ b/tests/core_tests/chaingen_main.cpp
@@ -133,6 +133,7 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(gen_block_has_invalid_tx);
GENERATE_AND_PLAY(gen_block_is_too_big);
GENERATE_AND_PLAY(gen_block_invalid_binary_format); // Takes up to 3 hours, if CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW == 500, up to 30 minutes, if CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW == 10
+ GENERATE_AND_PLAY(gen_block_late_v1_coinbase_tx);
// Transaction verification tests
GENERATE_AND_PLAY(gen_tx_big_version);