Skip to content

Commit

Permalink
Merge pull request #201 from stoffu/aeon-dul
Browse files Browse the repository at this point in the history
blockchain: deterministic UNIX time unlock checks /monero#6745
  • Loading branch information
aeonix authored Oct 25, 2020
2 parents d5e8d62 + bdbb16a commit 612714c
Show file tree
Hide file tree
Showing 15 changed files with 128 additions and 57 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ Dates are provided in the format YYYY-MM-DD.
| 592000 | 2015-08-04 | v1 (exceptional, version not bumped) | v0.9.0.0 | v0.9.14.0 | blocktime = 240 seconds, CryptoNight-Lite, lower mining priority for ringsize < 3 |
| 963500 | 2018-06-03 | v7 | v0.12.0.0 | v0.12.9.0-aeon | Rebase to Monero's latest codebase with RingCT disabled, CryptoNight-Lite variant 1, limited use of ringsize 1, ban ringsize 2 |
| 1146200 | 2019-10-25 | v8 | v0.13.0.0-aeon | v0.13.1.0-aeon | Switch to K12 PoW, reduced tx size with Borromean sigs, fixed ringsize 3, long-term block size, enforced 10 block age |
| 1280000 | 2020-11-11 | v9 | v0.14.0.0-aeon | v0.14.0.0-aeon | Difficulty algorithm variant 9 (cut/sort removed, lag reduced to 8), change to the block median used to calculate penalty |
| 1280000 | 2020-11-11 | v9 | v0.14.1.0-aeon | v0.14.1.0-aeon | Difficulty algorithm variant 9 (cut/sort removed, lag reduced to 8), change to the block median used to calculate penalty, deterministic unlock times |

## Compiling Aeon from source

Expand Down Expand Up @@ -168,7 +168,7 @@ invokes cmake commands as needed.
* Change to the root of the source code directory, change to the most recent release tag, and build:

cd aeon
git checkout v0.14.0.0-aeon
git checkout v0.14.1.0-aeon
make

*Optional*: If your machine has several cores and enough memory, enable
Expand Down Expand Up @@ -232,7 +232,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch (
```
git clone https://github.com/aeonix/aeon.git
cd aeon
git checkout tags/v0.14.0.0-aeon
git checkout tags/v0.14.1.0-aeon
```
* Build:
```
Expand Down Expand Up @@ -329,9 +329,9 @@ application.

cd aeon

* If you would like a specific [version/tag](https://github.com/aeonix/aeon/tags), do a git checkout for that version. eg. 'v0.14.0.0-aeon'. If you dont care about the version and just want binaries from master, skip this step:
* If you would like a specific [version/tag](https://github.com/aeonix/aeon/tags), do a git checkout for that version. eg. 'v0.14.1.0-aeon'. If you dont care about the version and just want binaries from master, skip this step:

git checkout v0.14.0.0-aeon
git checkout v0.14.1.0-aeon

* If you are on a 64-bit system, run:

Expand Down
1 change: 1 addition & 0 deletions src/cryptonote_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ static_assert(DIFFICULTY_LAG_V9 < DIFFICULTY_LAG_V1, "");
#define HF_VERSION_ENFORCE_MIN_AGE 8
#define HF_VERSION_EFFECTIVE_SHORT_TERM_MEDIAN_IN_PENALTY 9
#define HF_VERSION_REMOVE_DIFFICULTY_SORT 9
#define HF_VERSION_DETERMINISTIC_UNLOCK_TIME 9

#define HASH_OF_HASHES_STEP 256

Expand Down
61 changes: 45 additions & 16 deletions src/cryptonote_core/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2031,7 +2031,8 @@ bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMA
// get tx_hash, tx_out_index from DB
const output_data_t od = m_db->get_output_key(i.amount, i.index);
tx_out_index toi = m_db->get_output_tx_and_index(i.amount, i.index);
bool unlocked = is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first));
const uint8_t hf_version = m_hardfork->get_current_version();
bool unlocked = is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first), hf_version);

res.outs.push_back({od.pubkey, od.commitment, unlocked, od.height, toi.first});
}
Expand All @@ -2049,7 +2050,8 @@ void Blockchain::get_output_key_mask_unlocked(const uint64_t& amount, const uint
key = o_data.pubkey;
mask = o_data.commitment;
tx_out_index toi = m_db->get_output_tx_and_index(amount, index);
unlocked = is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first));
const uint8_t hf_version = m_hardfork->get_current_version();
unlocked = is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first), hf_version);
}
//------------------------------------------------------------------
bool Blockchain::get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &base) const
Expand Down Expand Up @@ -2942,7 +2944,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,

// make sure that output being spent matches up correctly with the
// signature spending it.
if (!check_tx_input(tx.version, tx.minor_version, in_to_key, tx_prefix_hash, tx_v1_0 ? tx.signatures[sig_index] : std::vector<crypto::signature>(), tx_v1_1 ? tx.borromean_signature.r[sig_index] : std::vector<crypto::ec_scalar>(), tx.rct_signatures, pubkeys[sig_index], pmax_used_block_height))
if (!check_tx_input(tx.version, tx.minor_version, in_to_key, tx_prefix_hash, tx_v1_0 ? tx.signatures[sig_index] : std::vector<crypto::signature>(), tx_v1_1 ? tx.borromean_signature.r[sig_index] : std::vector<crypto::ec_scalar>(), tx.rct_signatures, pubkeys[sig_index], pmax_used_block_height, hf_version))
{
it->second[in_to_key.k_image] = false;
MERROR_VER("Failed to check ring signature for tx " << get_transaction_hash(tx) << " vin key with k_image: " << in_to_key.k_image << " sig_index: " << sig_index);
Expand Down Expand Up @@ -3241,7 +3243,7 @@ bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const
//------------------------------------------------------------------
// This function checks to see if a tx is unlocked. unlock_time is either
// a block index or a unix time.
bool Blockchain::is_tx_spendtime_unlocked(uint64_t unlock_time) const
bool Blockchain::is_tx_spendtime_unlocked(uint64_t unlock_time, uint8_t hf_version) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
if(unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER)
Expand All @@ -3256,7 +3258,7 @@ bool Blockchain::is_tx_spendtime_unlocked(uint64_t unlock_time) const
else
{
//interpret as time
uint64_t current_time = static_cast<uint64_t>(time(NULL));
const uint64_t current_time = hf_version >= HF_VERSION_DETERMINISTIC_UNLOCK_TIME ? get_adjusted_time(m_db->height()) : static_cast<uint64_t>(time(NULL));
if(current_time + (get_current_hard_fork_version() < 2 ? CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V1 : CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2) >= unlock_time)
return true;
else
Expand All @@ -3268,7 +3270,7 @@ bool Blockchain::is_tx_spendtime_unlocked(uint64_t unlock_time) const
// This function locates all outputs associated with a given input (mixins)
// and validates that they exist and are usable. It also checks the ring
// signature for each input.
bool Blockchain::check_tx_input(size_t tx_version, uint8_t tx_minor_version, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, const std::vector<crypto::ec_scalar>& borromean_sig_r, const rct::rctSig &rct_signatures, std::vector<rct::ctkey> &output_keys, uint64_t* pmax_related_block_height)
bool Blockchain::check_tx_input(size_t tx_version, uint8_t tx_minor_version, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, const std::vector<crypto::ec_scalar>& borromean_sig_r, const rct::rctSig &rct_signatures, std::vector<rct::ctkey> &output_keys, uint64_t* pmax_related_block_height, uint8_t hf_version)
{
LOG_PRINT_L3("Blockchain::" << __func__);

Expand All @@ -3280,14 +3282,15 @@ bool Blockchain::check_tx_input(size_t tx_version, uint8_t tx_minor_version, con
{
std::vector<rct::ctkey >& m_output_keys;
const Blockchain& m_bch;
outputs_visitor(std::vector<rct::ctkey>& output_keys, const Blockchain& bch) :
m_output_keys(output_keys), m_bch(bch)
const uint8_t hf_version;
outputs_visitor(std::vector<rct::ctkey>& output_keys, const Blockchain& bch, uint8_t hf_version) :
m_output_keys(output_keys), m_bch(bch), hf_version(hf_version)
{
}
bool handle_output(uint64_t unlock_time, const crypto::public_key &pubkey, const rct::key &commitment)
{
//check tx unlock time
if (!m_bch.is_tx_spendtime_unlocked(unlock_time))
if (!m_bch.is_tx_spendtime_unlocked(unlock_time, hf_version))
{
MERROR_VER("One of outputs for one of inputs has wrong tx.unlock_time = " << unlock_time);
return false;
Expand All @@ -3306,7 +3309,7 @@ bool Blockchain::check_tx_input(size_t tx_version, uint8_t tx_minor_version, con
output_keys.clear();

// collect output keys
outputs_visitor vi(output_keys, *this);
outputs_visitor vi(output_keys, *this, hf_version);
if (!scan_outputkeys_for_indexes(tx_version, txin, vi, tx_prefix_hash, pmax_related_block_height))
{
MERROR_VER("Failed to get output keys for tx with amount = " << print_money(txin.amount) << " and count indexes " << txin.key_offsets.size());
Expand All @@ -3328,12 +3331,38 @@ bool Blockchain::check_tx_input(size_t tx_version, uint8_t tx_minor_version, con
return true;
}
//------------------------------------------------------------------
//TODO: Is this intended to do something else? Need to look into the todo there.
uint64_t Blockchain::get_adjusted_time() const
// only works on the main chain
uint64_t Blockchain::get_adjusted_time(uint64_t height) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
//TODO: add collecting median time
return time(NULL);

// if not enough blocks, no proper median yet, return current time
if(height < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW)
{
return static_cast<uint64_t>(time(NULL));
}
std::vector<uint64_t> timestamps;

// need most recent 60 blocks, get index of first of those
size_t offset = height - BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW;
timestamps.reserve(height - offset);
for(;offset < height; ++offset)
{
timestamps.push_back(m_db->get_block_timestamp(offset));
}
uint64_t median_ts = epee::misc_utils::median(timestamps);

// project the median to match approximately when the block being validated will appear
// the median is calculated from a chunk of past blocks, so we use +1 to offset onto the current block
median_ts += (BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW + 1) * DIFFICULTY_TARGET_V2 / 2;

// project the current block's time based on the previous block's time
// we don't use the current block's time directly to mitigate timestamp manipulation
uint64_t adjusted_current_block_ts = timestamps.back() + DIFFICULTY_TARGET_V2;

// return minimum of ~current block time and adjusted median time
// we do this since it's better to report a time in the past than a time in the future
return (adjusted_current_block_ts < median_ts ? adjusted_current_block_ts : median_ts);
}
//------------------------------------------------------------------
//TODO: revisit, has changed a bit on upstream
Expand Down Expand Up @@ -3361,9 +3390,9 @@ bool Blockchain::check_block_timestamp(std::vector<uint64_t>& timestamps, const
bool Blockchain::check_block_timestamp(const block& b, uint64_t& median_ts) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
if(b.timestamp > get_adjusted_time() + CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT)
if(b.timestamp > (uint64_t)time(NULL) + CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT)
{
MERROR_VER("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than adjusted time + 2 hours");
MERROR_VER("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than local time + 2 hours");
return false;
}

Expand Down
31 changes: 19 additions & 12 deletions src/cryptonote_core/blockchain.h
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,21 @@ namespace cryptonote
*/
std::vector<time_t> get_last_block_timestamps(unsigned int blocks) const;

/**
* @brief get the "adjusted time"
*
* Computes the median timestamp of the previous 60 blocks, projects it
* onto the current block to get an 'adjusted median time' which approximates
* what the current block's timestamp should be. Also projects the previous
* block's timestamp to estimate the current block's timestamp.
*
* Returns the minimum of the two projections, or the current local time on
* the machine if less than 60 blocks are available.
*
* @return current time approximated from chain data
*/
uint64_t get_adjusted_time(uint64_t height) const;

#ifndef IN_UNIT_TESTS
private:
#endif
Expand Down Expand Up @@ -1082,10 +1097,11 @@ namespace cryptonote
* @param output_keys return-by-reference the public keys of the outputs in the input set
* @param rct_signatures the ringCT signatures, which are only valid if tx version > 1
* @param pmax_related_block_height return-by-pointer the height of the most recent block in the input set
* @param hf_version the consensus rules version to use
*
* @return false if any output is not yet unlocked, or is missing, otherwise true
*/
bool check_tx_input(size_t tx_version, uint8_t tx_minor_version, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, const std::vector<crypto::ec_scalar>& borromean_sig_r, const rct::rctSig &rct_signatures, std::vector<rct::ctkey> &output_keys, uint64_t* pmax_related_block_height);
bool check_tx_input(size_t tx_version, uint8_t tx_minor_version, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, const std::vector<crypto::ec_scalar>& borromean_sig_r, const rct::rctSig &rct_signatures, std::vector<rct::ctkey> &output_keys, uint64_t* pmax_related_block_height, uint8_t hf_version);

/**
* @brief validate a transaction's inputs and their keys
Expand Down Expand Up @@ -1259,10 +1275,11 @@ namespace cryptonote
* unlock_time is either a block index or a unix time.
*
* @param unlock_time the unlock parameter (height or time)
* @param hf_version the consensus rules version to use
*
* @return true if spendable, otherwise false
*/
bool is_tx_spendtime_unlocked(uint64_t unlock_time) const;
bool is_tx_spendtime_unlocked(uint64_t unlock_time, uint8_t hf_version) const;

/**
* @brief stores an invalid block in a separate container
Expand Down Expand Up @@ -1323,16 +1340,6 @@ namespace cryptonote
bool check_block_timestamp(std::vector<uint64_t>& timestamps, const block& b, uint64_t& median_ts) const;
bool check_block_timestamp(std::vector<uint64_t>& timestamps, const block& b) const { uint64_t median_ts; return check_block_timestamp(timestamps, b, median_ts); }

/**
* @brief get the "adjusted time"
*
* Currently this simply returns the current time according to the
* user's machine.
*
* @return the current time
*/
uint64_t get_adjusted_time() const;

/**
* @brief finish an alternate chain's timestamp window from the main chain
*
Expand Down
2 changes: 2 additions & 0 deletions src/rpc/core_rpc_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ namespace cryptonote
res.cumulative_difficulty, res.wide_cumulative_difficulty, res.cumulative_difficulty_top64);
res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit();
res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median();
res.adjusted_time = m_core.get_blockchain_storage().get_adjusted_time(res.height);

res.status = CORE_RPC_STATUS_OK;
res.start_time = m_restricted ? 0 : (uint64_t)m_core.get_start_time();
res.free_space = m_restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space();
Expand Down
4 changes: 3 additions & 1 deletion src/rpc/core_rpc_server_commands_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ namespace cryptonote
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define CORE_RPC_VERSION_MAJOR 2
#define CORE_RPC_VERSION_MINOR 1
#define CORE_RPC_VERSION_MINOR 2
#define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR)

Expand Down Expand Up @@ -889,6 +889,7 @@ namespace cryptonote
uint64_t block_weight_limit;
uint64_t block_size_median;
uint64_t block_weight_median;
uint64_t adjusted_time;
uint64_t start_time;
uint64_t free_space;
bool offline;
Expand Down Expand Up @@ -927,6 +928,7 @@ namespace cryptonote
KV_SERIALIZE_OPT(block_weight_limit, (uint64_t)0)
KV_SERIALIZE(block_size_median)
KV_SERIALIZE_OPT(block_weight_median, (uint64_t)0)
KV_SERIALIZE(adjusted_time)
KV_SERIALIZE(start_time)
KV_SERIALIZE(free_space)
KV_SERIALIZE(offline)
Expand Down
1 change: 1 addition & 0 deletions src/rpc/daemon_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ namespace rpc
res.info.cumulative_difficulty = (res.info.wide_cumulative_difficulty & 0xffffffffffffffff).convert_to<uint64_t>();
res.info.block_size_limit = res.info.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit();
res.info.block_size_median = res.info.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median();
res.info.adjusted_time = m_core.get_blockchain_storage().get_adjusted_time(res.info.height);
res.info.start_time = (uint64_t)m_core.get_start_time();

res.status = Message::STATUS_OK;
Expand Down
1 change: 1 addition & 0 deletions src/rpc/message_data_structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ namespace rpc
uint64_t block_size_limit;
uint64_t block_weight_limit;
uint64_t block_size_median;
uint64_t adjusted_time;
uint64_t block_weight_median;
uint64_t start_time;
};
Expand Down
2 changes: 2 additions & 0 deletions src/serialization/json_object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1223,6 +1223,7 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::DaemonInfo& in
INSERT_INTO_JSON_OBJECT(val, doc, block_weight_limit, info.block_weight_limit);
INSERT_INTO_JSON_OBJECT(val, doc, block_size_median, info.block_size_median);
INSERT_INTO_JSON_OBJECT(val, doc, block_weight_median, info.block_weight_median);
INSERT_INTO_JSON_OBJECT(val, doc, adjusted_time, info.adjusted_time);
INSERT_INTO_JSON_OBJECT(val, doc, start_time, info.start_time);
}

Expand Down Expand Up @@ -1252,6 +1253,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& inf
GET_FROM_JSON_OBJECT(val, info.block_weight_limit, block_weight_limit);
GET_FROM_JSON_OBJECT(val, info.block_size_median, block_size_median);
GET_FROM_JSON_OBJECT(val, info.block_weight_median, block_weight_median);
GET_FROM_JSON_OBJECT(val, info.adjusted_time, adjusted_time);
GET_FROM_JSON_OBJECT(val, info.start_time, start_time);
}

Expand Down
8 changes: 4 additions & 4 deletions src/simplewallet/simplewallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6764,8 +6764,8 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec
}
else
{
uint64_t current_time = static_cast<uint64_t>(time(NULL));
uint64_t threshold = current_time + (m_wallet->use_fork_rules(2, 0) ? CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2 : CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V1);
const uint64_t adjusted_time = m_wallet->get_daemon_adjusted_time();
uint64_t threshold = adjusted_time + (m_wallet->use_fork_rules(2, 0) ? CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2 : CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V1);
if (threshold < pd.m_unlock_time)
locked_msg = get_human_readable_timespan(std::chrono::seconds(pd.m_unlock_time - threshold));
}
Expand Down Expand Up @@ -8176,8 +8176,8 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args)
}
else
{
uint64_t current_time = static_cast<uint64_t>(time(NULL));
uint64_t threshold = current_time + (m_wallet->use_fork_rules(2, 0) ? CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2 : CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V1);
const uint64_t adjusted_time = m_wallet->get_daemon_adjusted_time();
uint64_t threshold = adjusted_time + (m_wallet->use_fork_rules(2, 0) ? CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2 : CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V1);
if (threshold >= pd.m_unlock_time)
success_msg_writer() << "unlocked for " << get_human_readable_timespan(std::chrono::seconds(threshold - pd.m_unlock_time));
else
Expand Down
2 changes: 1 addition & 1 deletion src/version.cpp.in
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#define DEF_MONERO_VERSION_TAG "@VERSIONTAG@"
#define DEF_MONERO_VERSION "0.14.0.0"
#define DEF_MONERO_VERSION "0.14.1.0"
#define DEF_MONERO_RELEASE_NAME "Chronos"
#define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG

Expand Down
Loading

0 comments on commit 612714c

Please sign in to comment.