aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormoneromooo-monero <moneromooo-monero@users.noreply.github.com>2019-09-04 13:59:36 +0000
committermoneromooo-monero <moneromooo-monero@users.noreply.github.com>2019-09-17 11:39:25 +0000
commita444f06e53b218cc8bd091e5283828beb3e7d9af (patch)
tree5adef894dd90fb764b8ea332604f43ceafd955b9
parentMerge pull request #5861 (diff)
downloadmonero-a444f06e53b218cc8bd091e5283828beb3e7d9af.tar.xz
blockchain: enforce 10 block age for spending outputs
Some custom wallet code apparently ignores this, which causes users of that code to be fingerprinted
-rw-r--r--src/cryptonote_config.h1
-rw-r--r--src/cryptonote_core/blockchain.cpp10
-rw-r--r--tests/core_tests/chaingen_main.cpp1
-rw-r--r--tests/core_tests/rct.cpp31
-rw-r--r--tests/core_tests/rct.h14
5 files changed, 51 insertions, 6 deletions
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
index 4147b48ee..53c487d02 100644
--- a/src/cryptonote_config.h
+++ b/src/cryptonote_config.h
@@ -163,6 +163,7 @@
#define HF_VERSION_MIN_V2_COINBASE_TX 12
#define HF_VERSION_SAME_MIXIN 12
#define HF_VERSION_REJECT_SIGS_IN_COINBASE 12
+#define HF_VERSION_ENFORCE_MIN_AGE 12
#define PER_KB_FEE_QUANTIZATION_DECIMALS 8
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 798a73c4f..1b62233f5 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -2982,6 +2982,9 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
const auto waiter_guard = epee::misc_utils::create_scope_leave_handler([&]() { waiter.wait(&tpool); });
int threads = tpool.get_max_concurrency();
+ uint64_t max_used_block_height = 0;
+ if (!pmax_used_block_height)
+ pmax_used_block_height = &max_used_block_height;
for (const auto& txin : tx.vin)
{
// make sure output being spent is of type txin_to_key, rather than
@@ -3048,6 +3051,13 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
if (tx.version == 1 && threads > 1)
waiter.wait(&tpool);
+ // enforce min output age
+ if (hf_version >= HF_VERSION_ENFORCE_MIN_AGE)
+ {
+ CHECK_AND_ASSERT_MES(*pmax_used_block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE <= m_db->height(),
+ false, "Transaction spends at least one output which is too young");
+ }
+
if (tx.version == 1)
{
if (threads > 1)
diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp
index 4ee71466e..8406f416f 100644
--- a/tests/core_tests/chaingen_main.cpp
+++ b/tests/core_tests/chaingen_main.cpp
@@ -208,6 +208,7 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(gen_rct_tx_pre_rct_increase_vin_and_fee);
GENERATE_AND_PLAY(gen_rct_tx_pre_rct_altered_extra);
GENERATE_AND_PLAY(gen_rct_tx_rct_altered_extra);
+ GENERATE_AND_PLAY(gen_rct_tx_uses_output_too_early);
GENERATE_AND_PLAY(gen_multisig_tx_valid_22_1_2);
GENERATE_AND_PLAY(gen_multisig_tx_valid_22_1_2_many_inputs);
diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp
index 248354177..dc77e408f 100644
--- a/tests/core_tests/rct.cpp
+++ b/tests/core_tests/rct.cpp
@@ -40,8 +40,8 @@ using namespace cryptonote;
//----------------------------------------------------------------------------------------------------------------------
// Tests
-bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& events,
- const int *out_idx, int mixin, uint64_t amount_paid, bool valid,
+bool gen_rct_tx_validation_base::generate_with_full(std::vector<test_event_entry>& events,
+ const int *out_idx, int mixin, uint64_t amount_paid, size_t second_rewind, uint8_t last_version, const rct::RCTConfig &rct_config, bool valid,
const std::function<void(std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations)> &pre_tx,
const std::function<void(transaction &tx)> &post_tx) const
{
@@ -151,13 +151,13 @@ bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& ev
// rewind
{
- for (size_t i = 0; i < CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; ++i)
+ for (size_t i = 0; i < second_rewind; ++i)
{
cryptonote::block blk;
CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk, blk_last, miner_account,
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version | test_generator::bf_max_outs,
- 4, 4, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
- crypto::hash(), 0, transaction(), std::vector<crypto::hash>(), 0, 6, 4),
+ last_version, last_version, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
+ crypto::hash(), 0, transaction(), std::vector<crypto::hash>(), 0, 6, last_version),
false, "Failed to generate block");
events.push_back(blk);
blk_last = blk;
@@ -213,6 +213,8 @@ bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& ev
td.addr = miner_account.get_keys().m_account_address;
td.amount = amount_paid;
std::vector<tx_destination_entry> destinations;
+ // from v12, we need two outputs at least
+ destinations.push_back(td);
destinations.push_back(td);
if (pre_tx)
@@ -223,7 +225,7 @@ bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& ev
std::vector<crypto::secret_key> additional_tx_keys;
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[miner_accounts[0].get_keys().m_account_address.m_spend_public_key] = {0,0};
- bool r = construct_tx_and_get_tx_key(miner_accounts[0].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), tx, 0, tx_key, additional_tx_keys, true);
+ bool r = construct_tx_and_get_tx_key(miner_accounts[0].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), tx, 0, tx_key, additional_tx_keys, true, rct_config);
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
if (post_tx)
@@ -237,6 +239,15 @@ bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& ev
return true;
}
+bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& events,
+ const int *out_idx, int mixin, uint64_t amount_paid, bool valid,
+ const std::function<void(std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations)> &pre_tx,
+ const std::function<void(transaction &tx)> &post_tx) const
+{
+ const rct::RCTConfig rct_config { rct::RangeProofBorromean, 0 };
+ return generate_with_full(events, out_idx, mixin, amount_paid, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, 4, rct_config, valid, pre_tx, post_tx);
+}
+
bool gen_rct_tx_valid_from_pre_rct::generate(std::vector<test_event_entry>& events) const
{
const int mixin = 2;
@@ -507,3 +518,11 @@ bool gen_rct_tx_rct_altered_extra::generate(std::vector<test_event_entry>& event
NULL, [&failed](transaction &tx) {std::string extra_nonce; crypto::hash pid = crypto::null_hash; set_payment_id_to_tx_extra_nonce(extra_nonce, pid); if (!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce)) failed = true; }) && !failed;
}
+bool gen_rct_tx_uses_output_too_early::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 10;
+ const int out_idx[] = {1, -1};
+ const uint64_t amount_paid = 10000;
+ const rct::RCTConfig rct_config { rct::RangeProofPaddedBulletproof, 2 };
+ return generate_with_full(events, out_idx, mixin, amount_paid, CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE-3, HF_VERSION_ENFORCE_MIN_AGE, rct_config, false, NULL, NULL);
+}
diff --git a/tests/core_tests/rct.h b/tests/core_tests/rct.h
index 00a2bd88c..9433d4b02 100644
--- a/tests/core_tests/rct.h
+++ b/tests/core_tests/rct.h
@@ -69,6 +69,10 @@ struct gen_rct_tx_validation_base : public test_chain_unit_base
return true;
}
+ bool generate_with_full(std::vector<test_event_entry>& events, const int *out_idx, int mixin,
+ uint64_t amount_paid, size_t second_rewind, uint8_t last_version, const rct::RCTConfig &rct_config, bool valid,
+ const std::function<void(std::vector<cryptonote::tx_source_entry> &sources, std::vector<cryptonote::tx_destination_entry> &destinations)> &pre_tx,
+ const std::function<void(cryptonote::transaction &tx)> &post_tx) const;
bool generate_with(std::vector<test_event_entry>& events, const int *out_idx, int mixin,
uint64_t amount_paid, bool valid,
const std::function<void(std::vector<cryptonote::tx_source_entry> &sources, std::vector<cryptonote::tx_destination_entry> &destinations)> &pre_tx,
@@ -262,3 +266,13 @@ struct gen_rct_tx_rct_altered_extra : public gen_rct_tx_validation_base
};
template<> struct get_test_options<gen_rct_tx_rct_altered_extra>: public get_test_options<gen_rct_tx_validation_base> {};
+struct gen_rct_tx_uses_output_too_early : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_uses_output_too_early> {
+ const std::pair<uint8_t, uint64_t> hard_forks[5] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(4, 65), std::make_pair(12, 69), std::make_pair(0, 0)};
+ const cryptonote::test_options test_options = {
+ hard_forks, 0
+ };
+};