diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index 6e9f5724..c9aae6fb 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -2215,7 +2215,7 @@ bool blockchain_storage::add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPU return false; //check if transaction is unlocked - if (!is_tx_spendtime_unlocked(get_tx_unlock_time(tx))) + if (!is_tx_spendtime_unlocked(get_tx_unlock_time(tx, out_ptr->out_no))) return false; //use appropriate mix_attr out @@ -3833,12 +3833,12 @@ bool blockchain_storage::get_output_keys_for_input_with_checks(const txin_to_key outputs_visitor(std::vector& results_collector, const blockchain_storage& bch) :m_results_collector(results_collector), m_bch(bch) {} - bool handle_output(const transaction& tx, const tx_out& out) + bool handle_output(const transaction& tx, const tx_out& out, uint64_t out_i) { //check tx unlock time - if (!m_bch.is_tx_spendtime_unlocked(get_tx_unlock_time(tx))) + if (!m_bch.is_tx_spendtime_unlocked(get_tx_unlock_time(tx, out_i))) { - LOG_PRINT_L0("One of outputs for one of inputs have wrong tx.unlock_time = " << get_tx_unlock_time(tx)); + LOG_PRINT_L0("One of outputs for one of inputs have wrong tx.unlock_time = " << get_tx_unlock_time(tx, out_i)); return false; } @@ -3927,7 +3927,7 @@ bool blockchain_storage::check_ms_input(const transaction& tx, size_t in_index, #define LOC_CHK(cond, msg) CHECK_AND_ASSERT_MES(cond, false, "ms input check failed: ms_id: " << txin.multisig_out_id << ", input #" << in_index << " in tx " << tx_prefix_hash << ", refers to ms output #" << out_n << " in source tx " << get_transaction_hash(source_tx) << ENDL << msg) CRITICAL_REGION_LOCAL(m_read_lock); - uint64_t unlock_time = get_tx_unlock_time(source_tx); + uint64_t unlock_time = get_tx_unlock_time(source_tx, out_n); LOC_CHK(is_tx_spendtime_unlocked(unlock_time), "Source transaction is LOCKED! unlock_time: " << unlock_time << ", now is " << m_core_runtime_config.get_core_time() << ", blockchain size is " << get_current_blockchain_size()); LOC_CHK(source_tx.vout.size() > out_n, "internal error: out_n==" << out_n << " is out-of-bounds of source_tx.vout, size=" << source_tx.vout.size()); diff --git a/src/currency_core/blockchain_storage.h b/src/currency_core/blockchain_storage.h index 68acade0..1beab605 100644 --- a/src/currency_core/blockchain_storage.h +++ b/src/currency_core/blockchain_storage.h @@ -707,7 +707,7 @@ namespace currency CHECK_AND_ASSERT_MES(mixattr_ok, false, "tx output #" << output_index << " violates mixin restrictions: mix_attr = " << static_cast(outtk.mix_attr) << ", key_offsets.size = " << tx_in_to_key.key_offsets.size()); TIME_MEASURE_START_PD(tx_check_inputs_loop_scan_outputkeys_loop_handle_output); - if (!vis.handle_output(tx_ptr->tx, tx_ptr->tx.vout[n])) + if (!vis.handle_output(tx_ptr->tx, tx_ptr->tx.vout[n], output_index)) { LOG_PRINT_L0("Failed to handle_output for output id = " << tx_id << ", no " << n); return false; diff --git a/src/currency_core/currency_basic.h b/src/currency_core/currency_basic.h index 25781224..c2d299db 100644 --- a/src/currency_core/currency_basic.h +++ b/src/currency_core/currency_basic.h @@ -336,7 +336,7 @@ namespace currency }; - //number of block (or time), used as a limitation: spend this tx not early then block/time + //number of block (or timestamp if v bigger then CURRENCY_MAX_BLOCK_NUMBER), used as a limitation: spend this tx not early then block/time struct etc_tx_details_unlock_time { uint64_t v; @@ -345,6 +345,16 @@ namespace currency END_SERIALIZE() }; + //number of block (or timestamp if unlock_time_array[i] bigger then CURRENCY_MAX_BLOCK_NUMBER), used as a limitation: spend this tx not early then block/time + //unlock_time_array[i], i - index of output, unlock_time_array.size() == vout.size() + struct etc_tx_details_unlock_time2 + { + std::vector unlock_time_array; + BEGIN_SERIALIZE() + VARINT_FIELD(unlock_time_array) + END_SERIALIZE() + }; + struct etc_tx_details_expiration_time { uint64_t v; @@ -380,7 +390,7 @@ namespace currency END_SERIALIZE() }; - typedef boost::mpl::vector all_payload_types; + typedef boost::mpl::vector all_payload_types; typedef boost::make_variant_over::type attachment_v; typedef boost::make_variant_over::type extra_v; typedef boost::make_variant_over::type payload_items_v; @@ -613,5 +623,6 @@ SET_VARIANT_TAGS(uint64_t, 26, "uint64_t"); SET_VARIANT_TAGS(currency::etc_tx_time, 27, "etc_tx_time"); SET_VARIANT_TAGS(uint32_t, 28, "uint32_t"); SET_VARIANT_TAGS(currency::tx_receiver, 29, "payer"); +SET_VARIANT_TAGS(currency::etc_tx_details_unlock_time2, 30, "unlock_time2"); #undef SET_VARIANT_TAGS diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index de309d9e..a3a28b03 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -891,16 +891,7 @@ namespace currency } return n; } - //--------------------------------------------------------------- - account_public_address get_crypt_address_from_destinations(const account_keys& sender_account_keys, const std::vector& destinations) - { - for (const auto& de : destinations) - { - if (de.addr.size() == 1 && sender_account_keys.m_account_address != de.addr.back()) - return de.addr.back(); // return the first destination address that is non-multisig and not equal to the sender's address - } - return sender_account_keys.m_account_address; // otherwise, fallback to sender's address - } + //--------------------------------------------------------------- bool construct_tx(const account_keys& sender_account_keys, const std::vector& sources, @@ -2524,16 +2515,6 @@ namespace currency { return epee::string_tools::parse_hexstr_to_binbuff(payment_id_str, payment_id); } - //------------------------------------------------------------------ - bool is_tx_expired(const transaction& tx, uint64_t expiration_ts_median) - { - /// tx expiration condition (tx is ok if the following is true) - /// tx_expiration_time - TX_EXPIRATION_MEDIAN_SHIFT > get_last_n_blocks_timestamps_median(TX_EXPIRATION_TIMESTAMP_CHECK_WINDOW) - uint64_t expiration_time = get_tx_expiration_time(tx); - if (expiration_time == 0) - return false; // 0 means it never expires - return expiration_time <= expiration_ts_median + TX_EXPIRATION_MEDIAN_SHIFT; - } //-------------------------------------------------------------------------------- crypto::hash prepare_prefix_hash_for_sign(const transaction& tx, uint64_t in_index, const crypto::hash& tx_id) { diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index 5528b073..0ebfe16a 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -208,32 +208,6 @@ namespace currency //--------------------------------------------------------------- - template - uint64_t get_tx_x_detail(const transaction& tx) - { - extra_type_t e = AUTO_VAL_INIT(e); - get_type_in_variant_container(tx.extra, e); - return e.v; - } - template - void set_tx_x_detail(transaction& tx, uint64_t v) - { - extra_type_t e = AUTO_VAL_INIT(e); - e.v = v; - update_or_add_field_to_extra(tx.extra, e); - } - - inline uint64_t get_tx_unlock_time(const transaction& tx){ return get_tx_x_detail(tx);} - inline uint64_t get_tx_flags(const transaction& tx){ return get_tx_x_detail(tx); } - inline uint64_t get_tx_expiration_time(const transaction& tx){ return get_tx_x_detail(tx); } - inline void set_tx_unlock_time(transaction& tx, uint64_t v){ set_tx_x_detail(tx, v); } - inline void set_tx_flags(transaction& tx, uint64_t v){ set_tx_x_detail(tx, v); } - inline void set_tx_expiration_time(transaction& tx, uint64_t v){ set_tx_x_detail(tx, v); } - account_public_address get_crypt_address_from_destinations(const account_keys& sender_account_keys, const std::vector& destinations); - - bool is_tx_expired(const transaction& tx, uint64_t expiration_ts_median); - - uint64_t get_string_uint64_hash(const std::string& str); bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set& deriv_cache, uint8_t tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED); bool validate_alias_name(const std::string& al); diff --git a/src/currency_core/currency_format_utils_transactions.cpp b/src/currency_core/currency_format_utils_transactions.cpp index 533313ae..b9f1b44c 100644 --- a/src/currency_core/currency_format_utils_transactions.cpp +++ b/src/currency_core/currency_format_utils_transactions.cpp @@ -11,6 +11,44 @@ namespace currency { + //--------------------------------------------------------------- + account_public_address get_crypt_address_from_destinations(const account_keys& sender_account_keys, const std::vector& destinations) + { + for (const auto& de : destinations) + { + if (de.addr.size() == 1 && sender_account_keys.m_account_address != de.addr.back()) + return de.addr.back(); // return the first destination address that is non-multisig and not equal to the sender's address + } + return sender_account_keys.m_account_address; // otherwise, fallback to sender's address + } + //------------------------------------------------------------------ + bool is_tx_expired(const transaction& tx, uint64_t expiration_ts_median) + { + /// tx expiration condition (tx is ok if the following is true) + /// tx_expiration_time - TX_EXPIRATION_MEDIAN_SHIFT > get_last_n_blocks_timestamps_median(TX_EXPIRATION_TIMESTAMP_CHECK_WINDOW) + uint64_t expiration_time = get_tx_expiration_time(tx); + if (expiration_time == 0) + return false; // 0 means it never expires + return expiration_time <= expiration_ts_median + TX_EXPIRATION_MEDIAN_SHIFT; + } + //--------------------------------------------------------------- + inline uint64_t get_tx_unlock_time(const transaction& tx, uint64_t o_i) + { + // etc_tx_details_expiration_time have priority over etc_tx_details_expiration_time2 + uint64_t v = get_tx_x_detail(tx); + if (v) + return v; + + etc_tx_details_unlock_time2 ut2 = AUTO_VAL_INIT(ut2); + get_type_in_variant_container(tx.extra, ut2); + if (!ut2.unlock_time_array.size()) + return 0; + + CHECK_AND_ASSERT_THROW_MES(ut2.unlock_time_array.size() > o_i, "unlock_time_array.size=" << ut2.unlock_time_array.size() + << " is less then o_i=" << o_i << " in tx: " << get_transaction_hash(tx)); + + return ut2.unlock_time_array[o_i]; + } //--------------------------------------------------------------- void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h) { diff --git a/src/currency_core/currency_format_utils_transactions.h b/src/currency_core/currency_format_utils_transactions.h index a4937fae..6c394706 100644 --- a/src/currency_core/currency_format_utils_transactions.h +++ b/src/currency_core/currency_format_utils_transactions.h @@ -13,6 +13,32 @@ namespace currency { + + template + uint64_t get_tx_x_detail(const transaction& tx) + { + extra_type_t e = AUTO_VAL_INIT(e); + get_type_in_variant_container(tx.extra, e); + return e.v; + } + template + void set_tx_x_detail(transaction& tx, uint64_t v) + { + extra_type_t e = AUTO_VAL_INIT(e); + e.v = v; + update_or_add_field_to_extra(tx.extra, e); + } + + inline uint64_t get_tx_unlock_time(const transaction& tx, uint64_t o_i); + inline uint64_t get_tx_flags(const transaction& tx) { return get_tx_x_detail(tx); } + inline uint64_t get_tx_expiration_time(const transaction& tx) return get_tx_x_detail(tx); } + inline void set_tx_unlock_time(transaction& tx, uint64_t v) { set_tx_x_detail(tx, v); } + inline void set_tx_flags(transaction& tx, uint64_t v) { set_tx_x_detail(tx, v); } + inline void set_tx_expiration_time(transaction& tx, uint64_t v) { set_tx_x_detail(tx, v); } + account_public_address get_crypt_address_from_destinations(const account_keys& sender_account_keys, const std::vector& destinations); + + bool is_tx_expired(const transaction& tx, uint64_t expiration_ts_median); + void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h); crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx); bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash);