aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md10
-rw-r--r--contrib/gitian/README.md4
-rw-r--r--contrib/gitian/gitian-android.yml2
-rw-r--r--contrib/gitian/gitian-freebsd.yml2
-rw-r--r--contrib/gitian/gitian-linux.yml2
-rw-r--r--contrib/gitian/gitian-osx.yml2
-rw-r--r--contrib/gitian/gitian-win.yml2
-rw-r--r--src/blocks/checkpoints.datbin261572 -> 272772 bytes
-rw-r--r--src/checkpoints/checkpoints.cpp1
-rw-r--r--src/common/dns_utils.cpp4
-rw-r--r--src/common/threadpool.cpp11
-rw-r--r--src/common/threadpool.h8
-rw-r--r--src/crypto/rx-slow-hash.c50
-rw-r--r--src/cryptonote_basic/miner.cpp10
-rw-r--r--src/cryptonote_basic/miner.h6
-rw-r--r--src/cryptonote_config.h1
-rw-r--r--src/cryptonote_core/blockchain.cpp127
-rw-r--r--src/cryptonote_core/blockchain.h39
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp59
-rw-r--r--src/cryptonote_core/cryptonote_core.h4
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.cpp17
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.h1
-rw-r--r--src/device_trezor/trezor/protocol.cpp29
-rw-r--r--src/hardforks/hardforks.cpp6
-rw-r--r--src/ringct/rctSigs.cpp15
-rw-r--r--src/rpc/core_rpc_server.cpp32
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h4
-rw-r--r--src/rpc/daemon_handler.cpp1
-rw-r--r--src/rpc/message_data_structs.h1
-rw-r--r--src/serialization/json_object.cpp2
-rw-r--r--src/simplewallet/simplewallet.cpp8
-rw-r--r--src/version.cpp.in4
-rw-r--r--src/wallet/node_rpc_proxy.cpp11
-rw-r--r--src/wallet/node_rpc_proxy.h2
-rw-r--r--src/wallet/wallet2.cpp76
-rw-r--r--src/wallet/wallet2.h20
-rw-r--r--tests/core_tests/chaingen.cpp6
-rwxr-xr-xtests/functional_tests/functional_tests_rpc.py3
-rwxr-xr-xtests/functional_tests/mining.py114
-rwxr-xr-xtests/functional_tests/p2p.py19
-rw-r--r--tests/trezor/daemon.cpp2
-rw-r--r--tests/trezor/trezor_tests.cpp40
-rw-r--r--tests/unit_tests/threadpool.cpp40
43 files changed, 584 insertions, 213 deletions
diff --git a/README.md b/README.md
index 75d8d5960..cd67f8022 100644
--- a/README.md
+++ b/README.md
@@ -134,6 +134,8 @@ Dates are provided in the format YYYY-MM-DD.
| 1788000 | 2019-03-09 | v10 | v0.14.0.0 | v0.14.1.2 | New PoW based on Cryptonight-R, new block weight algorithm, slightly more efficient RingCT format
| 1788720 | 2019-03-10 | v11 | v0.14.0.0 | v0.14.1.2 | forbid old RingCT transaction format
| 1978433 | 2019-11-30* | v12 | v0.15.0.0 | v0.16.0.0 | New PoW based on RandomX, only allow >= 2 outputs, change to the block median used to calculate penalty, v1 coinbases are forbidden, rct sigs in coinbase forbidden, 10 block lock time for incoming outputs
+| 2210000 | 2020-10-17 | v13 | v0.17.0.0 | v0.17.0.0 | New CLSAG transaction format
+| 2210720 | 2020-10-18 | v14 | v0.17.0.0 | v0.17.0.0 | forbid old MLSAG transaction format
| XXXXXXX | XXX-XX-XX | XXX | vX.XX.X.X | vX.XX.X.X | XXX |
X's indicate that these details have not been determined as of commit date.
@@ -220,7 +222,7 @@ invokes cmake commands as needed.
```bash
cd monero
- git checkout release-v0.16
+ git checkout release-v0.17
make
```
@@ -293,7 +295,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch (
```bash
git clone https://github.com/monero-project/monero.git
cd monero
- git checkout tags/v0.16.0.0
+ git checkout tags/v0.17.0.0
```
* Build:
@@ -410,10 +412,10 @@ application.
cd monero
```
-* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.16.0.0'. If you don't care about the version and just want binaries from master, skip this step:
+* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.17.0.0'. If you don't care about the version and just want binaries from master, skip this step:
```bash
- git checkout v0.16.0.0
+ git checkout v0.17.0.0
```
* If you are on a 64-bit system, run:
diff --git a/contrib/gitian/README.md b/contrib/gitian/README.md
index c0f230887..1938462aa 100644
--- a/contrib/gitian/README.md
+++ b/contrib/gitian/README.md
@@ -126,7 +126,7 @@ Setup for LXC:
```bash
GH_USER=fluffypony
-VERSION=v0.16.0.0
+VERSION=v0.17.0.0
./gitian-build.py --setup $GH_USER $VERSION
```
@@ -182,7 +182,7 @@ If you chose to do detached signing using `--detach-sign` above (recommended), y
```bash
GH_USER=fluffypony
-VERSION=v0.16.0.0
+VERSION=v0.17.0.0
gpg --detach-sign ${VERSION}-linux/${GH_USER}/monero-linux-*-build.assert
gpg --detach-sign ${VERSION}-win/${GH_USER}/monero-win-*-build.assert
diff --git a/contrib/gitian/gitian-android.yml b/contrib/gitian/gitian-android.yml
index de98efafe..b8eaa8af9 100644
--- a/contrib/gitian/gitian-android.yml
+++ b/contrib/gitian/gitian-android.yml
@@ -1,5 +1,5 @@
---
-name: "monero-android-0.16"
+name: "monero-android-0.17"
enable_cache: true
suites:
- "bionic"
diff --git a/contrib/gitian/gitian-freebsd.yml b/contrib/gitian/gitian-freebsd.yml
index e97c3802b..c104bfd02 100644
--- a/contrib/gitian/gitian-freebsd.yml
+++ b/contrib/gitian/gitian-freebsd.yml
@@ -1,5 +1,5 @@
---
-name: "monero-freebsd-0.16"
+name: "monero-freebsd-0.17"
enable_cache: true
suites:
- "bionic"
diff --git a/contrib/gitian/gitian-linux.yml b/contrib/gitian/gitian-linux.yml
index bd42637aa..4a2f3798a 100644
--- a/contrib/gitian/gitian-linux.yml
+++ b/contrib/gitian/gitian-linux.yml
@@ -1,5 +1,5 @@
---
-name: "monero-linux-0.16"
+name: "monero-linux-0.17"
enable_cache: true
suites:
- "bionic"
diff --git a/contrib/gitian/gitian-osx.yml b/contrib/gitian/gitian-osx.yml
index 4d44c4845..9889ca45f 100644
--- a/contrib/gitian/gitian-osx.yml
+++ b/contrib/gitian/gitian-osx.yml
@@ -1,5 +1,5 @@
---
-name: "monero-osx-0.16"
+name: "monero-osx-0.17"
enable_cache: true
suites:
- "bionic"
diff --git a/contrib/gitian/gitian-win.yml b/contrib/gitian/gitian-win.yml
index 196b6ebbe..c53086144 100644
--- a/contrib/gitian/gitian-win.yml
+++ b/contrib/gitian/gitian-win.yml
@@ -1,5 +1,5 @@
---
-name: "monero-win-0.16"
+name: "monero-win-0.17"
enable_cache: true
suites:
- "bionic"
diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat
index fa58387ab..e75e379f2 100644
--- a/src/blocks/checkpoints.dat
+++ b/src/blocks/checkpoints.dat
Binary files differ
diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp
index 6e7e1acba..c88a630cc 100644
--- a/src/checkpoints/checkpoints.cpp
+++ b/src/checkpoints/checkpoints.cpp
@@ -236,6 +236,7 @@ namespace cryptonote
ADD_CHECKPOINT2(1958000, "98a5d6e51afdf3146e0eefb10a66e8648d8d4d5c2742be8835e976ba217c9bb2", "0x79dd46d2a0971a");
ADD_CHECKPOINT2(2046000, "5e867f0b8baefed9244a681df97fc885d8ab36c3dfcd24c7a3abf3b8ac8b8314", "0x9cb8b6ff2978c6");
ADD_CHECKPOINT2(2092500, "c4e00820c9c7989b49153d5e90ae095a18a11d990e82fcc3be54e6ed785472b5", "0xb4e585a31369cb");
+ ADD_CHECKPOINT2(2182500, "0d22b5f81982eff21d094af9e821dc2007e6342069e3b1a37b15d97646353124", "0xead4a874083492");
return true;
}
diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp
index c871177b1..4f4efcd81 100644
--- a/src/common/dns_utils.cpp
+++ b/src/common/dns_utils.cpp
@@ -521,14 +521,14 @@ bool load_txt_records_from_dns(std::vector<std::string> &good_records, const std
// send all requests in parallel
std::deque<bool> avail(dns_urls.size(), false), valid(dns_urls.size(), false);
tools::threadpool& tpool = tools::threadpool::getInstance();
- tools::threadpool::waiter waiter;
+ tools::threadpool::waiter waiter(tpool);
for (size_t n = 0; n < dns_urls.size(); ++n)
{
tpool.submit(&waiter,[n, dns_urls, &records, &avail, &valid](){
records[n] = tools::DNSResolver::instance().get_txt_record(dns_urls[n], avail[n], valid[n]);
});
}
- waiter.wait(&tpool);
+ waiter.wait();
size_t cur_index = first_index;
do
diff --git a/src/common/threadpool.cpp b/src/common/threadpool.cpp
index a1737778c..edc87fc48 100644
--- a/src/common/threadpool.cpp
+++ b/src/common/threadpool.cpp
@@ -120,7 +120,7 @@ threadpool::waiter::~waiter()
catch (...) { /* ignore */ }
try
{
- wait(NULL);
+ wait();
}
catch (const std::exception &e)
{
@@ -128,12 +128,12 @@ threadpool::waiter::~waiter()
}
}
-void threadpool::waiter::wait(threadpool *tpool) {
- if (tpool)
- tpool->run(true);
+bool threadpool::waiter::wait() {
+ pool.run(true);
boost::unique_lock<boost::mutex> lock(mt);
while(num)
cv.wait(lock);
+ return !error();
}
void threadpool::waiter::inc() {
@@ -166,7 +166,8 @@ void threadpool::run(bool flush) {
lock.unlock();
++depth;
is_leaf = e.leaf;
- e.f();
+ try { e.f(); }
+ catch (const std::exception &ex) { e.wo->set_error(); try { MERROR("Exception in threadpool job: " << ex.what()); } catch (...) {} }
--depth;
is_leaf = false;
diff --git a/src/common/threadpool.h b/src/common/threadpool.h
index 91f9fdf47..66b08fece 100644
--- a/src/common/threadpool.h
+++ b/src/common/threadpool.h
@@ -55,12 +55,16 @@ public:
class waiter {
boost::mutex mt;
boost::condition_variable cv;
+ threadpool &pool;
int num;
+ bool error_flag;
public:
void inc();
void dec();
- void wait(threadpool *tpool); //! Wait for a set of tasks to finish.
- waiter() : num(0){}
+ bool wait(); //! Wait for a set of tasks to finish, returns false iff any error
+ void set_error() noexcept { error_flag = true; }
+ bool error() const noexcept { return error_flag; }
+ waiter(threadpool &pool) : pool(pool), num(0), error_flag(false) {}
~waiter();
};
diff --git a/src/crypto/rx-slow-hash.c b/src/crypto/rx-slow-hash.c
index 1d7f09cab..fa35a32e2 100644
--- a/src/crypto/rx-slow-hash.c
+++ b/src/crypto/rx-slow-hash.c
@@ -116,6 +116,46 @@ static inline int enabled_flags(void) {
#define SEEDHASH_EPOCH_BLOCKS 2048 /* Must be same as BLOCKS_SYNCHRONIZING_MAX_COUNT in cryptonote_config.h */
#define SEEDHASH_EPOCH_LAG 64
+static inline int is_power_of_2(uint64_t n) { return n && (n & (n-1)) == 0; }
+
+static int get_seedhash_epoch_lag(void)
+{
+ static unsigned int lag = (unsigned int)-1;
+ if (lag != (unsigned int)-1)
+ return lag;
+ const char *e = getenv("SEEDHASH_EPOCH_LAG");
+ if (e)
+ {
+ lag = atoi(e);
+ if (lag > SEEDHASH_EPOCH_LAG || !is_power_of_2(lag))
+ lag = SEEDHASH_EPOCH_LAG;
+ }
+ else
+ {
+ lag = SEEDHASH_EPOCH_LAG;
+ }
+ return lag;
+}
+
+static unsigned int get_seedhash_epoch_blocks(void)
+{
+ static unsigned int blocks = (unsigned int)-1;
+ if (blocks != (unsigned int)-1)
+ return blocks;
+ const char *e = getenv("SEEDHASH_EPOCH_BLOCKS");
+ if (e)
+ {
+ blocks = atoi(e);
+ if (blocks < 2 || blocks > SEEDHASH_EPOCH_BLOCKS || !is_power_of_2(blocks))
+ blocks = SEEDHASH_EPOCH_BLOCKS;
+ }
+ else
+ {
+ blocks = SEEDHASH_EPOCH_BLOCKS;
+ }
+ return blocks;
+}
+
void rx_reorg(const uint64_t split_height) {
int i;
CTHR_MUTEX_LOCK(rx_mutex);
@@ -130,14 +170,16 @@ void rx_reorg(const uint64_t split_height) {
}
uint64_t rx_seedheight(const uint64_t height) {
- uint64_t s_height = (height <= SEEDHASH_EPOCH_BLOCKS+SEEDHASH_EPOCH_LAG) ? 0 :
- (height - SEEDHASH_EPOCH_LAG - 1) & ~(SEEDHASH_EPOCH_BLOCKS-1);
+ const uint64_t seedhash_epoch_lag = get_seedhash_epoch_lag();
+ const uint64_t seedhash_epoch_blocks = get_seedhash_epoch_blocks();
+ uint64_t s_height = (height <= seedhash_epoch_blocks+seedhash_epoch_lag) ? 0 :
+ (height - seedhash_epoch_lag - 1) & ~(seedhash_epoch_blocks-1);
return s_height;
}
void rx_seedheights(const uint64_t height, uint64_t *seedheight, uint64_t *nextheight) {
*seedheight = rx_seedheight(height);
- *nextheight = rx_seedheight(height + SEEDHASH_EPOCH_LAG);
+ *nextheight = rx_seedheight(height + get_seedhash_epoch_lag());
}
typedef struct seedinfo {
@@ -194,7 +236,7 @@ static void rx_initdata(randomx_cache *rs_cache, const int miners, const uint64_
void rx_slow_hash(const uint64_t mainheight, const uint64_t seedheight, const char *seedhash, const void *data, size_t length,
char *hash, int miners, int is_alt) {
uint64_t s_height = rx_seedheight(mainheight);
- int toggle = (s_height & SEEDHASH_EPOCH_BLOCKS) != 0;
+ int toggle = (s_height & get_seedhash_epoch_blocks()) != 0;
randomx_flags flags = enabled_flags() & ~disabled_flags();
rx_state *rx_sp;
randomx_cache *cache;
diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp
index 34a559b83..29f6dce5a 100644
--- a/src/cryptonote_basic/miner.cpp
+++ b/src/cryptonote_basic/miner.cpp
@@ -169,7 +169,9 @@ namespace cryptonote
extra_nonce = m_extra_messages[m_config.current_extra_message_index];
}
- if(!m_phandler->get_block_template(bl, m_mine_address, di, height, expected_reward, extra_nonce))
+ uint64_t seed_height;
+ crypto::hash seed_hash;
+ if(!m_phandler->get_block_template(bl, m_mine_address, di, height, expected_reward, extra_nonce, seed_height, seed_hash))
{
LOG_ERROR("Failed to get_block_template(), stopping mining");
return false;
@@ -471,12 +473,12 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------------
- bool miner::find_nonce_for_given_block(const get_block_hash_t &gbh, block& bl, const difficulty_type& diffic, uint64_t height)
+ bool miner::find_nonce_for_given_block(const get_block_hash_t &gbh, block& bl, const difficulty_type& diffic, uint64_t height, const crypto::hash *seed_hash)
{
for(; bl.nonce != std::numeric_limits<uint32_t>::max(); bl.nonce++)
{
crypto::hash h;
- gbh(bl, height, diffic <= 100 ? 0 : tools::get_max_concurrency(), h);
+ gbh(bl, height, seed_hash, diffic <= 100 ? 0 : tools::get_max_concurrency(), h);
if(check_hash(h, diffic))
{
@@ -572,7 +574,7 @@ namespace cryptonote
b.nonce = nonce;
crypto::hash h;
- m_gbh(b, height, tools::get_max_concurrency(), h);
+ m_gbh(b, height, NULL, tools::get_max_concurrency(), h);
if(check_hash(h, local_diff))
{
diff --git a/src/cryptonote_basic/miner.h b/src/cryptonote_basic/miner.h
index b23253d4a..df3f56f68 100644
--- a/src/cryptonote_basic/miner.h
+++ b/src/cryptonote_basic/miner.h
@@ -47,12 +47,12 @@ namespace cryptonote
struct i_miner_handler
{
virtual bool handle_block_found(block& b, block_verification_context &bvc) = 0;
- virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce) = 0;
+ virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash) = 0;
protected:
~i_miner_handler(){};
};
- typedef std::function<bool(const cryptonote::block&, uint64_t, unsigned int, crypto::hash&)> get_block_hash_t;
+ typedef std::function<bool(const cryptonote::block&, uint64_t, const crypto::hash*, unsigned int, crypto::hash&)> get_block_hash_t;
/************************************************************************/
/* */
@@ -76,7 +76,7 @@ namespace cryptonote
bool on_idle();
void on_synchronized();
//synchronous analog (for fast calls)
- static bool find_nonce_for_given_block(const get_block_hash_t &gbh, block& bl, const difficulty_type& diffic, uint64_t height);
+ static bool find_nonce_for_given_block(const get_block_hash_t &gbh, block& bl, const difficulty_type& diffic, uint64_t height, const crypto::hash *seed_hash = NULL);
void pause();
void resume();
void do_print_hashrate(bool do_hr);
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
index f50ab6a40..9bd1f44eb 100644
--- a/src/cryptonote_config.h
+++ b/src/cryptonote_config.h
@@ -180,6 +180,7 @@
#define HF_VERSION_EFFECTIVE_SHORT_TERM_MEDIAN_IN_PENALTY 12
#define HF_VERSION_EXACT_COINBASE 13
#define HF_VERSION_CLSAG 13
+#define HF_VERSION_DETERMINISTIC_UNLOCK_TIME 13
#define PER_KB_FEE_QUANTIZATION_DECIMALS 8
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 9d4c5a66c..93e3ef3bc 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -1506,13 +1506,15 @@ uint64_t Blockchain::get_current_cumulative_block_weight_median() const
// in a lot of places. That flag is not referenced in any of the code
// nor any of the makefiles, howeve. Need to look into whether or not it's
// necessary at all.
-bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce)
+bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
{
LOG_PRINT_L3("Blockchain::" << __func__);
size_t median_weight;
uint64_t already_generated_coins;
uint64_t pool_cookie;
+ seed_hash = crypto::null_hash;
+
m_tx_pool.lock();
const auto unlock_guard = epee::misc_utils::create_scope_leave_handler([&]() { m_tx_pool.unlock(); });
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -1531,6 +1533,8 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
diffic = m_btc_difficulty;
height = m_btc_height;
expected_reward = m_btc_expected_reward;
+ seed_height = m_btc_seed_height;
+ seed_hash = m_btc_seed_hash;
return true;
}
MDEBUG("Not using cached template: address " << (!memcmp(&miner_address, &m_btc_address, sizeof(cryptonote::account_public_address))) << ", nonce " << (m_btc_nonce == ex_nonce) << ", cookie " << (m_btc_pool_cookie == m_tx_pool.cookie()) << ", from_block " << (!!from_block));
@@ -1564,10 +1568,34 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
CHECK_AND_ASSERT_MES(get_block_by_hash(*from_block, prev_block), false, "From block not found"); // TODO
uint64_t from_block_height = cryptonote::get_block_height(prev_block);
height = from_block_height + 1;
+ if (m_hardfork->get_current_version() >= RX_BLOCK_VERSION)
+ {
+ uint64_t next_height;
+ crypto::rx_seedheights(height, &seed_height, &next_height);
+ seed_hash = get_block_id_by_height(seed_height);
+ }
}
else
{
height = alt_chain.back().height + 1;
+ uint64_t next_height;
+ crypto::rx_seedheights(height, &seed_height, &next_height);
+
+ if (alt_chain.size() && alt_chain.front().height <= seed_height)
+ {
+ for (auto it=alt_chain.begin(); it != alt_chain.end(); it++)
+ {
+ if (it->height == seed_height+1)
+ {
+ seed_hash = it->bl.prev_id;
+ break;
+ }
+ }
+ }
+ else
+ {
+ seed_hash = get_block_id_by_height(seed_height);
+ }
}
b.major_version = m_hardfork->get_ideal_version(height);
b.minor_version = m_hardfork->get_ideal_version();
@@ -1602,6 +1630,12 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
median_weight = m_current_block_cumul_weight_limit / 2;
diffic = get_difficulty_for_next_block();
already_generated_coins = m_db->get_block_already_generated_coins(height - 1);
+ if (m_hardfork->get_current_version() >= RX_BLOCK_VERSION)
+ {
+ uint64_t next_height;
+ crypto::rx_seedheights(height, &seed_height, &next_height);
+ seed_hash = get_block_id_by_height(seed_height);
+ }
}
b.timestamp = time(NULL);
@@ -1734,16 +1768,16 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
#endif
if (!from_block)
- cache_block_template(b, miner_address, ex_nonce, diffic, height, expected_reward, pool_cookie);
+ cache_block_template(b, miner_address, ex_nonce, diffic, height, expected_reward, seed_height, seed_hash, pool_cookie);
return true;
}
LOG_ERROR("Failed to create_block_template with " << 10 << " tries");
return false;
}
//------------------------------------------------------------------
-bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce)
+bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
{
- return create_block_template(b, NULL, miner_address, diffic, height, expected_reward, ex_nonce);
+ return create_block_template(b, NULL, miner_address, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash);
}
//------------------------------------------------------------------
// for an alternate chain, get the timestamps from the main chain to complete
@@ -2233,8 +2267,9 @@ bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMA
MERROR("Unexpected output data size: expected " << req.outputs.size() << ", got " << data.size());
return false;
}
+ const uint8_t hf_version = m_hardfork->get_current_version();
for (const auto &t: data)
- res.outs.push_back({t.pubkey, t.commitment, is_tx_spendtime_unlocked(t.unlock_time), t.height, crypto::null_hash});
+ res.outs.push_back({t.pubkey, t.commitment, is_tx_spendtime_unlocked(t.unlock_time, hf_version), t.height, crypto::null_hash});
if (req.get_txid)
{
@@ -2258,7 +2293,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
@@ -3298,8 +3334,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
results.resize(tx.vin.size(), 0);
tools::threadpool& tpool = tools::threadpool::getInstance();
- tools::threadpool::waiter waiter;
- const auto waiter_guard = epee::misc_utils::create_scope_leave_handler([&]() { waiter.wait(&tpool); });
+ tools::threadpool::waiter waiter(tpool);
int threads = tpool.get_max_concurrency();
uint64_t max_used_block_height = 0;
@@ -3330,7 +3365,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, in_to_key, tx_prefix_hash, tx.version == 1 ? tx.signatures[sig_index] : std::vector<crypto::signature>(), tx.rct_signatures, pubkeys[sig_index], pmax_used_block_height))
+ if (!check_tx_input(tx.version, in_to_key, tx_prefix_hash, tx.version == 1 ? tx.signatures[sig_index] : std::vector<crypto::signature>(), tx.rct_signatures, pubkeys[sig_index], pmax_used_block_height, hf_version))
{
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);
if (pmax_used_block_height) // a default value of NULL is used when called from Blockchain::handle_block_to_main_chain()
@@ -3369,7 +3404,8 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
sig_index++;
}
if (tx.version == 1 && threads > 1)
- waiter.wait(&tpool);
+ if (!waiter.wait())
+ return false;
// enforce min output age
if (hf_version >= HF_VERSION_ENFORCE_MIN_AGE)
@@ -3722,7 +3758,7 @@ uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) 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)
@@ -3737,7 +3773,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
@@ -3749,7 +3785,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, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, const rct::rctSig &rct_signatures, std::vector<rct::ctkey> &output_keys, uint64_t* pmax_related_block_height) const
+bool Blockchain::check_tx_input(size_t tx_version, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, const rct::rctSig &rct_signatures, std::vector<rct::ctkey> &output_keys, uint64_t* pmax_related_block_height, uint8_t hf_version) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
@@ -3761,14 +3797,15 @@ bool Blockchain::check_tx_input(size_t tx_version, const txin_to_key& txin, cons
{
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;
@@ -3787,7 +3824,7 @@ bool Blockchain::check_tx_input(size_t tx_version, const txin_to_key& txin, cons
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());
@@ -3806,12 +3843,38 @@ bool Blockchain::check_tx_input(size_t tx_version, const txin_to_key& txin, cons
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
@@ -3839,9 +3902,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;
}
@@ -4015,8 +4078,8 @@ leave:
MCINFO("verify", "No pre-validated hash at height " << blockchain_height << ", verifying fully");
}
}
- else
#endif
+ if (!fast_check)
{
auto it = m_blocks_longhash_table.find(id);
if (it != m_blocks_longhash_table.end())
@@ -4908,7 +4971,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
{
m_blocks_longhash_table.clear();
uint64_t thread_height = height;
- tools::threadpool::waiter waiter;
+ tools::threadpool::waiter waiter(tpool);
m_prepare_height = height;
m_prepare_nblocks = blocks_entry.size();
m_prepare_blocks = &blocks;
@@ -4921,7 +4984,8 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
thread_height += nblocks;
}
- waiter.wait(&tpool);
+ if (!waiter.wait())
+ return false;
m_prepare_height = 0;
if (m_cancel)
@@ -5055,14 +5119,15 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
if (threads > 1 && amounts.size() > 1)
{
- tools::threadpool::waiter waiter;
+ tools::threadpool::waiter waiter(tpool);
for (size_t i = 0; i < amounts.size(); i++)
{
uint64_t amount = amounts[i];
tpool.submit(&waiter, boost::bind(&Blockchain::output_scan_worker, this, amount, std::cref(offset_map[amount]), std::ref(tx_map[amount])), true);
}
- waiter.wait(&tpool);
+ if (!waiter.wait())
+ return false;
}
else
{
@@ -5299,7 +5364,7 @@ void Blockchain::cancel()
}
#if defined(PER_BLOCK_CHECKPOINT)
-static const char expected_block_hashes_hash[] = "8b48d259d4b1126801b1f329683a26e1d16237420197cd3ccc76af2c55a36e83";
+static const char expected_block_hashes_hash[] = "8da80ca560793f252d1d4ed449c85d75c74867f3f86b8832c8e3f88b1cbb6ae3";
void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints)
{
if (get_checkpoints == nullptr || !m_fast_sync)
@@ -5441,7 +5506,7 @@ void Blockchain::invalidate_block_template_cache()
m_btc_valid = false;
}
-void Blockchain::cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t height, uint64_t expected_reward, uint64_t pool_cookie)
+void Blockchain::cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t height, uint64_t expected_reward, uint64_t seed_height, const crypto::hash &seed_hash, uint64_t pool_cookie)
{
MDEBUG("Setting block template cache");
m_btc = b;
@@ -5450,6 +5515,8 @@ void Blockchain::cache_block_template(const block &b, const cryptonote::account_
m_btc_difficulty = diff;
m_btc_height = height;
m_btc_expected_reward = expected_reward;
+ m_btc_seed_hash = seed_hash;
+ m_btc_seed_height = seed_height;
m_btc_pool_cookie = pool_cookie;
m_btc_valid = true;
}
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index 85aa5d4e2..a9b7ca1da 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -366,8 +366,8 @@ namespace cryptonote
*
* @return true if block template filled in successfully, else false
*/
- bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce);
- bool create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce);
+ bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
+ bool create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
/**
* @brief checks if a block is known about with a given hash
@@ -1042,6 +1042,21 @@ namespace cryptonote
*/
void flush_invalid_blocks();
+ /**
+ * @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
@@ -1124,6 +1139,8 @@ namespace cryptonote
uint64_t m_btc_height;
uint64_t m_btc_pool_cookie;
uint64_t m_btc_expected_reward;
+ crypto::hash m_btc_seed_hash;
+ uint64_t m_btc_seed_height;
bool m_btc_valid;
@@ -1180,10 +1197,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,const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, const rct::rctSig &rct_signatures, std::vector<rct::ctkey> &output_keys, uint64_t* pmax_related_block_height) const;
+ bool check_tx_input(size_t tx_version,const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, const rct::rctSig &rct_signatures, std::vector<rct::ctkey> &output_keys, uint64_t* pmax_related_block_height, uint8_t hf_version) const;
/**
* @brief validate a transaction's inputs and their keys
@@ -1371,10 +1389,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
@@ -1436,16 +1455,6 @@ namespace cryptonote
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
*
* for an alternate chain, get the timestamps from the main chain to complete
@@ -1520,6 +1529,6 @@ namespace cryptonote
*
* At some point, may be used to push an update to miners
*/
- void cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t height, uint64_t expected_reward, uint64_t pool_cookie);
+ void cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t height, uint64_t expected_reward, uint64_t seed_height, const crypto::hash &seed_hash, uint64_t pool_cookie);
};
} // namespace cryptonote
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 474362ed0..fef411a0c 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -226,8 +226,8 @@ namespace cryptonote
core::core(i_cryptonote_protocol* pprotocol):
m_mempool(m_blockchain_storage),
m_blockchain_storage(m_mempool),
- m_miner(this, [this](const cryptonote::block &b, uint64_t height, unsigned int threads, crypto::hash &hash) {
- return cryptonote::get_block_longhash(&m_blockchain_storage, b, hash, height, threads);
+ m_miner(this, [this](const cryptonote::block &b, uint64_t height, const crypto::hash *seed_hash, unsigned int threads, crypto::hash &hash) {
+ return cryptonote::get_block_longhash(&m_blockchain_storage, b, hash, height, seed_hash, threads);
}),
m_starter_message_showed(false),
m_target_blockchain_height(0),
@@ -985,7 +985,7 @@ namespace cryptonote
CRITICAL_REGION_LOCAL(m_incoming_tx_lock);
tools::threadpool& tpool = tools::threadpool::getInstance();
- tools::threadpool::waiter waiter;
+ tools::threadpool::waiter waiter(tpool);
epee::span<tx_blob_entry>::const_iterator it = tx_blobs.begin();
for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
tpool.submit(&waiter, [&, i, it] {
@@ -1001,7 +1001,8 @@ namespace cryptonote
}
});
}
- waiter.wait(&tpool);
+ if (!waiter.wait())
+ return false;
it = tx_blobs.begin();
std::vector<bool> already_have(tx_blobs.size(), false);
for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
@@ -1033,7 +1034,8 @@ namespace cryptonote
});
}
}
- waiter.wait(&tpool);
+ if (!waiter.wait())
+ return false;
std::vector<tx_verification_batch_info> tx_info;
tx_info.reserve(tx_blobs.size());
@@ -1182,11 +1184,42 @@ namespace cryptonote
size_t core::get_block_sync_size(uint64_t height) const
{
static const uint64_t quick_height = m_nettype == TESTNET ? 801219 : m_nettype == MAINNET ? 1220516 : 0;
+ size_t res = 0;
if (block_sync_size > 0)
- return block_sync_size;
- if (height >= quick_height)
- return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT;
- return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT_PRE_V4;
+ res = block_sync_size;
+ else if (height >= quick_height)
+ res = BLOCKS_SYNCHRONIZING_DEFAULT_COUNT;
+ else
+ res = BLOCKS_SYNCHRONIZING_DEFAULT_COUNT_PRE_V4;
+
+ static size_t max_block_size = 0;
+ if (max_block_size == 0)
+ {
+ const char *env = getenv("SEEDHASH_EPOCH_BLOCKS");
+ if (env)
+ {
+ int n = atoi(env);
+ if (n <= 0)
+ n = BLOCKS_SYNCHRONIZING_MAX_COUNT;
+ size_t p = 1;
+ while (p < (size_t)n)
+ p <<= 1;
+ max_block_size = p;
+ }
+ else
+ max_block_size = BLOCKS_SYNCHRONIZING_MAX_COUNT;
+ }
+ if (res > max_block_size)
+ {
+ static bool warned = false;
+ if (!warned)
+ {
+ MWARNING("Clamping block sync size to " << max_block_size);
+ warned = true;
+ }
+ res = max_block_size;
+ }
+ return res;
}
//-----------------------------------------------------------------------------------------------
bool core::are_key_images_spent_in_pool(const std::vector<crypto::key_image>& key_im, std::vector<bool> &spent) const
@@ -1358,14 +1391,14 @@ namespace cryptonote
m_mempool.set_relayed(epee::to_span(tx_hashes), tx_relay);
}
//-----------------------------------------------------------------------------------------------
- bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce)
+ bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
{
- return m_blockchain_storage.create_block_template(b, adr, diffic, height, expected_reward, ex_nonce);
+ return m_blockchain_storage.create_block_template(b, adr, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash);
}
//-----------------------------------------------------------------------------------------------
- bool core::get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce)
+ bool core::get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
{
- return m_blockchain_storage.create_block_template(b, prev_block, adr, diffic, height, expected_reward, ex_nonce);
+ return m_blockchain_storage.create_block_template(b, prev_block, adr, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash);
}
//-----------------------------------------------------------------------------------------------
bool core::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, bool clip_pruned, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index a53596c2c..c9d26e0ed 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -231,8 +231,8 @@ namespace cryptonote
*
* @note see Blockchain::create_block_template
*/
- virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce);
- virtual bool get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce);
+ virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
+ virtual bool get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
/**
* @brief called when a transaction is relayed.
diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp
index 7ea7e81d9..7400c4328 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.cpp
+++ b/src/cryptonote_core/cryptonote_tx_utils.cpp
@@ -663,9 +663,9 @@ namespace cryptonote
bl.minor_version = CURRENT_BLOCK_MINOR_VERSION;
bl.timestamp = 0;
bl.nonce = nonce;
- miner::find_nonce_for_given_block([](const cryptonote::block &b, uint64_t height, unsigned int threads, crypto::hash &hash){
- return cryptonote::get_block_longhash(NULL, b, hash, height, threads);
- }, bl, 1, 0);
+ miner::find_nonce_for_given_block([](const cryptonote::block &b, uint64_t height, const crypto::hash *seed_hash, unsigned int threads, crypto::hash &hash){
+ return cryptonote::get_block_longhash(NULL, b, hash, height, seed_hash, threads);
+ }, bl, 1, 0, NULL);
bl.invalidate_hashes();
return true;
}
@@ -676,7 +676,7 @@ namespace cryptonote
rx_slow_hash(main_height, seed_height, seed_hash.data, bd.data(), bd.size(), res.data, 0, 1);
}
- bool get_block_longhash(const Blockchain *pbc, const block& b, crypto::hash& res, const uint64_t height, const int miners)
+ bool get_block_longhash(const Blockchain *pbc, const block& b, crypto::hash& res, const uint64_t height, const crypto::hash *seed_hash, const int miners)
{
// block 202612 bug workaround
if (height == 202612)
@@ -693,7 +693,7 @@ namespace cryptonote
if (pbc != NULL)
{
seed_height = rx_seedheight(height);
- hash = pbc->get_pending_block_id_by_height(seed_height);
+ hash = seed_hash ? *seed_hash : pbc->get_pending_block_id_by_height(seed_height);
main_height = pbc->get_current_blockchain_height();
} else
{
@@ -701,7 +701,7 @@ namespace cryptonote
seed_height = 0;
main_height = 0;
}
- rx_slow_hash(main_height, seed_height, hash.data, bd.data(), bd.size(), res.data, miners, 0);
+ rx_slow_hash(main_height, seed_height, hash.data, bd.data(), bd.size(), res.data, seed_hash ? 0 : miners, !!seed_hash);
} else {
const int pow_variant = b.major_version >= 7 ? b.major_version - 6 : 0;
crypto::cn_slow_hash(bd.data(), bd.size(), res, pow_variant, height);
@@ -709,6 +709,11 @@ namespace cryptonote
return true;
}
+ bool get_block_longhash(const Blockchain *pbc, const block& b, crypto::hash& res, const uint64_t height, const int miners)
+ {
+ return get_block_longhash(pbc, b, res, height, NULL, miners);
+ }
+
crypto::hash get_block_longhash(const Blockchain *pbc, const block& b, const uint64_t height, const int miners)
{
crypto::hash p = crypto::null_hash;
diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h
index 3622029e0..dbdf409b5 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.h
+++ b/src/cryptonote_core/cryptonote_tx_utils.h
@@ -134,6 +134,7 @@ namespace cryptonote
class Blockchain;
bool get_block_longhash(const Blockchain *pb, const block& b, crypto::hash& res, const uint64_t height, const int miners);
+ bool get_block_longhash(const Blockchain *pb, const block& b, crypto::hash& res, const uint64_t height, const crypto::hash *seed_hash, const int miners);
void get_altblock_longhash(const block& b, crypto::hash& res, const uint64_t main_height, const uint64_t height,
const uint64_t seed_height, const crypto::hash& seed_hash);
crypto::hash get_block_longhash(const Blockchain *pb, const block& b, const uint64_t height, const int miners);
diff --git a/src/device_trezor/trezor/protocol.cpp b/src/device_trezor/trezor/protocol.cpp
index 25c3d816d..288f3ddca 100644
--- a/src/device_trezor/trezor/protocol.cpp
+++ b/src/device_trezor/trezor/protocol.cpp
@@ -561,11 +561,6 @@ namespace tx {
assign_to_repeatable(tsx_data.mutable_minor_indices(), tx.subaddr_indices.begin(), tx.subaddr_indices.end());
}
- // TODO: use HF_VERSION_CLSAG after CLSAG is merged
- if (tsx_data.hard_fork() >= 13){
- throw exc::ProtocolException("CLSAG is not yet implemented");
- }
-
// Rsig decision
auto rsig_data = tsx_data.mutable_rsig_data();
m_ct.rsig_type = get_rsig_type(tx.rct_config, tx.splitted_dsts.size());
@@ -1017,14 +1012,24 @@ namespace tx {
}
}
- // CLSAG support comes here once it is merged to the Monero
- m_ct.rv->p.MGs.reserve(m_ct.signatures.size());
- for(size_t i = 0; i < m_ct.signatures.size(); ++i) {
- rct::mgSig mg;
- if (!cn_deserialize(m_ct.signatures[i], mg)) {
- throw exc::ProtocolException("Cannot deserialize mg[i]");
+ if (m_ct.rv->type == rct::RCTTypeCLSAG){
+ m_ct.rv->p.CLSAGs.reserve(m_ct.signatures.size());
+ for (size_t i = 0; i < m_ct.signatures.size(); ++i) {
+ rct::clsag clsag;
+ if (!cn_deserialize(m_ct.signatures[i], clsag)) {
+ throw exc::ProtocolException("Cannot deserialize clsag[i]");
+ }
+ m_ct.rv->p.CLSAGs.push_back(clsag);
+ }
+ } else {
+ m_ct.rv->p.MGs.reserve(m_ct.signatures.size());
+ for (size_t i = 0; i < m_ct.signatures.size(); ++i) {
+ rct::mgSig mg;
+ if (!cn_deserialize(m_ct.signatures[i], mg)) {
+ throw exc::ProtocolException("Cannot deserialize mg[i]");
+ }
+ m_ct.rv->p.MGs.push_back(mg);
}
- m_ct.rv->p.MGs.push_back(mg);
}
m_ct.tx.rct_signatures = *(m_ct.rv);
diff --git a/src/hardforks/hardforks.cpp b/src/hardforks/hardforks.cpp
index c94884fd8..9055b92e3 100644
--- a/src/hardforks/hardforks.cpp
+++ b/src/hardforks/hardforks.cpp
@@ -93,6 +93,8 @@ const hardfork_t testnet_hard_forks[] = {
{ 10, 1154318, 0, 1550153694 },
{ 11, 1155038, 0, 1550225678 },
{ 12, 1308737, 0, 1569582000 },
+ { 13, 1543939, 0, 1599069376 },
+ { 14, 1544659, 0, 1599069377 },
};
const size_t num_testnet_hard_forks = sizeof(testnet_hard_forks) / sizeof(testnet_hard_forks[0]);
const uint64_t testnet_hard_fork_version_1_till = 624633;
@@ -113,7 +115,7 @@ const hardfork_t stagenet_hard_forks[] = {
{ 10, 269000, 0, 1550153694 },
{ 11, 269720, 0, 1550225678 },
{ 12, 454721, 0, 1571419280 },
- { 13, 699045, 0, 1598180817 },
- { 14, 699765, 0, 1598180818 },
+ { 13, 675405, 0, 1598180817 },
+ { 14, 676125, 0, 1598180818 },
};
const size_t num_stagenet_hard_forks = sizeof(stagenet_hard_forks) / sizeof(stagenet_hard_forks[0]);
diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp
index 5fd7ac06d..93eb52d4e 100644
--- a/src/ringct/rctSigs.cpp
+++ b/src/ringct/rctSigs.cpp
@@ -1278,12 +1278,13 @@ namespace rct {
{
if (semantics) {
tools::threadpool& tpool = tools::threadpool::getInstance();
- tools::threadpool::waiter waiter;
+ tools::threadpool::waiter waiter(tpool);
std::deque<bool> results(rv.outPk.size(), false);
DP("range proofs verified?");
for (size_t i = 0; i < rv.outPk.size(); i++)
tpool.submit(&waiter, [&, i] { results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); });
- waiter.wait(&tpool);
+ if (!waiter.wait())
+ return false;
for (size_t i = 0; i < results.size(); ++i) {
if (!results[i]) {
@@ -1327,7 +1328,7 @@ namespace rct {
PERF_TIMER(verRctSemanticsSimple);
tools::threadpool& tpool = tools::threadpool::getInstance();
- tools::threadpool::waiter waiter;
+ tools::threadpool::waiter waiter(tpool);
std::deque<bool> results;
std::vector<const Bulletproof*> proofs;
size_t max_non_bp_proofs = 0, offset = 0;
@@ -1410,7 +1411,8 @@ namespace rct {
return false;
}
- waiter.wait(&tpool);
+ if (!waiter.wait())
+ return false;
for (size_t i = 0; i < results.size(); ++i) {
if (!results[i]) {
LOG_PRINT_L1("Range proof verified failed for proof " << i);
@@ -1458,7 +1460,7 @@ namespace rct {
std::deque<bool> results(threads);
tools::threadpool& tpool = tools::threadpool::getInstance();
- tools::threadpool::waiter waiter;
+ tools::threadpool::waiter waiter(tpool);
const keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts;
@@ -1476,7 +1478,8 @@ namespace rct {
results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], pseudoOuts[i]);
});
}
- waiter.wait(&tpool);
+ if (!waiter.wait())
+ return false;
for (size_t i = 0; i < results.size(); ++i) {
if (!results[i]) {
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 36e86ae5d..a50c70418 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -461,6 +461,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.start_time = restricted ? 0 : (uint64_t)m_core.get_start_time();
res.free_space = restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space();
res.offline = m_core.offline();
@@ -1306,8 +1308,8 @@ namespace cryptonote
case 1: res.pow_algorithm = "CNv1 (Cryptonight variant 1)"; break;
case 2: case 3: res.pow_algorithm = "CNv2 (Cryptonight variant 2)"; break;
case 4: case 5: res.pow_algorithm = "CNv4 (Cryptonight variant 4)"; break;
- case 6: case 7: res.pow_algorithm = "RandomX"; break;
- default: res.pow_algorithm = "I'm not sure actually"; break;
+ case 6: case 7: case 8: case 9: res.pow_algorithm = "RandomX"; break;
+ default: res.pow_algorithm = "RandomX"; break; // assumed
}
if (res.is_background_mining_enabled)
{
@@ -1642,7 +1644,7 @@ namespace cryptonote
bool core_rpc_server::get_block_template(const account_public_address &address, const crypto::hash *prev_block, const cryptonote::blobdata &extra_nonce, size_t &reserved_offset, cryptonote::difficulty_type &difficulty, uint64_t &height, uint64_t &expected_reward, block &b, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, epee::json_rpc::error &error_resp)
{
b = boost::value_initialized<cryptonote::block>();
- if(!m_core.get_block_template(b, prev_block, address, difficulty, height, expected_reward, extra_nonce))
+ if(!m_core.get_block_template(b, prev_block, address, difficulty, height, expected_reward, extra_nonce, seed_height, seed_hash))
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Internal error: failed to create block template";
@@ -1659,17 +1661,6 @@ namespace cryptonote
return false;
}
- if (b.major_version >= RX_BLOCK_VERSION)
- {
- uint64_t next_height;
- crypto::rx_seedheights(height, &seed_height, &next_height);
- seed_hash = m_core.get_block_id_by_height(seed_height);
- if (next_height != seed_height)
- next_seed_hash = m_core.get_block_id_by_height(next_height);
- else
- next_seed_hash = seed_hash;
- }
-
if (extra_nonce.empty())
{
reserved_offset = 0;
@@ -1897,9 +1888,16 @@ namespace cryptonote
return false;
}
b.nonce = req.starting_nonce;
- miner::find_nonce_for_given_block([this](const cryptonote::block &b, uint64_t height, unsigned int threads, crypto::hash &hash) {
- return cryptonote::get_block_longhash(&(m_core.get_blockchain_storage()), b, hash, height, threads);
- }, b, template_res.difficulty, template_res.height);
+ crypto::hash seed_hash = crypto::null_hash;
+ if (b.major_version >= RX_BLOCK_VERSION && !epee::string_tools::hex_to_pod(template_res.seed_hash, seed_hash))
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Error converting seed hash";
+ return false;
+ }
+ miner::find_nonce_for_given_block([this](const cryptonote::block &b, uint64_t height, const crypto::hash *seed_hash, unsigned int threads, crypto::hash &hash) {
+ return cryptonote::get_block_longhash(&(m_core.get_blockchain_storage()), b, hash, height, seed_hash, threads);
+ }, b, template_res.difficulty, template_res.height, &seed_hash);
submit_req.front() = string_tools::buff_to_hex_nodelimer(block_to_blob(b));
r = on_submitblock(submit_req, submit_res, error_resp, ctx);
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index 09cd67d7d..8748b0540 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -88,7 +88,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 3
-#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)
@@ -675,6 +675,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;
@@ -713,6 +714,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)
diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp
index 248c54afb..e256322cb 100644
--- a/src/rpc/daemon_handler.cpp
+++ b/src/rpc/daemon_handler.cpp
@@ -538,6 +538,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.info.version = MONERO_VERSION;
diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h
index 085148d8a..86424653f 100644
--- a/src/rpc/message_data_structs.h
+++ b/src/rpc/message_data_structs.h
@@ -196,6 +196,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;
std::string version;
diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp
index 7c48cf6c3..67f042c2e 100644
--- a/src/serialization/json_object.cpp
+++ b/src/serialization/json_object.cpp
@@ -1342,6 +1342,7 @@ void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::r
INSERT_INTO_JSON_OBJECT(dest, block_weight_limit, info.block_weight_limit);
INSERT_INTO_JSON_OBJECT(dest, block_size_median, info.block_size_median);
INSERT_INTO_JSON_OBJECT(dest, block_weight_median, info.block_weight_median);
+ INSERT_INTO_JSON_OBJECT(dest, adjusted_time, info.adjusted_time);
INSERT_INTO_JSON_OBJECT(dest, start_time, info.start_time);
dest.EndObject();
@@ -1375,6 +1376,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);
}
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index f37d77933..5fb4131fa 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -8553,8 +8553,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));
}
@@ -10265,8 +10265,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
diff --git a/src/version.cpp.in b/src/version.cpp.in
index 1e720537a..9f6ffd97b 100644
--- a/src/version.cpp.in
+++ b/src/version.cpp.in
@@ -1,6 +1,6 @@
#define DEF_MONERO_VERSION_TAG "@VERSIONTAG@"
-#define DEF_MONERO_VERSION "0.16.0.0"
-#define DEF_MONERO_RELEASE_NAME "Nitrogen Nebula"
+#define DEF_MONERO_VERSION "0.17.0.0"
+#define DEF_MONERO_RELEASE_NAME "Oxygen Orion"
#define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG
#define DEF_MONERO_VERSION_IS_RELEASE @VERSION_IS_RELEASE@
diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp
index 95b8ce8bb..48a602bf3 100644
--- a/src/wallet/node_rpc_proxy.cpp
+++ b/src/wallet/node_rpc_proxy.cpp
@@ -72,6 +72,7 @@ void NodeRPCProxy::invalidate()
m_rpc_version = 0;
m_target_height = 0;
m_block_weight_limit = 0;
+ m_adjusted_time = 0;
m_get_info_time = 0;
m_rpc_payment_info_time = 0;
m_rpc_payment_seed_height = 0;
@@ -131,6 +132,7 @@ boost::optional<std::string> NodeRPCProxy::get_info()
m_height = resp_t.height;
m_target_height = resp_t.target_height;
m_block_weight_limit = resp_t.block_weight_limit ? resp_t.block_weight_limit : resp_t.block_size_limit;
+ m_adjusted_time = resp_t.adjusted_time;
m_get_info_time = now;
m_height_time = now;
}
@@ -171,6 +173,15 @@ boost::optional<std::string> NodeRPCProxy::get_block_weight_limit(uint64_t &bloc
return boost::optional<std::string>();
}
+boost::optional<std::string> NodeRPCProxy::get_adjusted_time(uint64_t &adjusted_time)
+{
+ auto res = get_info();
+ if (res)
+ return res;
+ adjusted_time = m_adjusted_time;
+ return boost::optional<std::string>();
+}
+
boost::optional<std::string> NodeRPCProxy::get_earliest_height(uint8_t version, uint64_t &earliest_height)
{
if (m_offline)
diff --git a/src/wallet/node_rpc_proxy.h b/src/wallet/node_rpc_proxy.h
index 500ba81d4..51b7f01dd 100644
--- a/src/wallet/node_rpc_proxy.h
+++ b/src/wallet/node_rpc_proxy.h
@@ -52,6 +52,7 @@ public:
void set_height(uint64_t h);
boost::optional<std::string> get_target_height(uint64_t &height);
boost::optional<std::string> get_block_weight_limit(uint64_t &block_weight_limit);
+ boost::optional<std::string> get_adjusted_time(uint64_t &adjusted_time);
boost::optional<std::string> get_earliest_height(uint8_t version, uint64_t &earliest_height);
boost::optional<std::string> get_dynamic_base_fee_estimate(uint64_t grace_blocks, uint64_t &fee);
boost::optional<std::string> get_fee_quantization_mask(uint64_t &fee_quantization_mask);
@@ -84,6 +85,7 @@ private:
uint64_t m_dynamic_base_fee_estimate_cached_height;
uint64_t m_dynamic_base_fee_estimate_grace_blocks;
uint64_t m_fee_quantization_mask;
+ uint64_t m_adjusted_time;
uint32_t m_rpc_version;
uint64_t m_target_height;
uint64_t m_block_weight_limit;
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index c7a3e7904..063c493ce 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -301,7 +301,7 @@ struct options {
const command_line::arg_descriptor<std::string> tx_notify = { "tx-notify" , "Run a program for each new incoming transaction, '%s' will be replaced by the transaction hash" , "" };
const command_line::arg_descriptor<bool> no_dns = {"no-dns", tools::wallet2::tr("Do not use DNS"), false};
const command_line::arg_descriptor<bool> offline = {"offline", tools::wallet2::tr("Do not connect to a daemon, nor use DNS"), false};
- const command_line::arg_descriptor<std::string> extra_entropy = {"extra-entropy", tools::wallet2::tr("File containing extra entropy to initialize the PRNG (any data, aim for 256 bits of entropy to be useful, wihch typically means more than 256 bits of data)")};
+ const command_line::arg_descriptor<std::string> extra_entropy = {"extra-entropy", tools::wallet2::tr("File containing extra entropy to initialize the PRNG (any data, aim for 256 bits of entropy to be useful, which typically means more than 256 bits of data)")};
};
void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file, std::string &mms_file)
@@ -1949,7 +1949,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
int num_vouts_received = 0;
tx_pub_key = pub_key_field.pub_key;
tools::threadpool& tpool = tools::threadpool::getInstance();
- tools::threadpool::waiter waiter;
+ tools::threadpool::waiter waiter(tpool);
const cryptonote::account_keys& keys = m_account.get_keys();
crypto::key_derivation derivation;
@@ -2022,7 +2022,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i,
std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]), std::ref(output_found[i])), true);
}
- waiter.wait(&tpool);
+ THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
// then scan all outputs from 0
hw::device &hwdev = m_account.get_device();
boost::unique_lock<hw::device> hwdev_lock (hwdev);
@@ -2049,7 +2049,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i,
std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]), std::ref(output_found[i])), true);
}
- waiter.wait(&tpool);
+ THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
hw::device &hwdev = m_account.get_device();
boost::unique_lock<hw::device> hwdev_lock (hwdev);
@@ -2687,7 +2687,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
THROW_WALLET_EXCEPTION_IF(!m_blockchain.is_in_bounds(current_index), error::out_of_hashchain_bounds_error);
tools::threadpool& tpool = tools::threadpool::getInstance();
- tools::threadpool::waiter waiter;
+ tools::threadpool::waiter waiter(tpool);
size_t num_txes = 0;
std::vector<tx_cache_data> tx_cache_data;
@@ -2714,7 +2714,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
}
}
THROW_WALLET_EXCEPTION_IF(txidx != num_txes, error::wallet_internal_error, "txidx does not match tx_cache_data size");
- waiter.wait(&tpool);
+ THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
hw::device &hwdev = m_account.get_device();
hw::reset_mode rst(hwdev);
@@ -2743,7 +2743,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
gender(iod);
}, true);
}
- waiter.wait(&tpool);
+ THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
auto geniod = [&](const cryptonote::transaction &tx, size_t n_vouts, size_t txidx) {
for (size_t k = 0; k < n_vouts; ++k)
@@ -2791,7 +2791,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
}
}
THROW_WALLET_EXCEPTION_IF(txidx != tx_cache_data.size(), error::wallet_internal_error, "txidx did not reach expected value");
- waiter.wait(&tpool);
+ THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
hwdev.set_mode(hw::device::NONE);
size_t tx_cache_data_offset = 0;
@@ -2863,14 +2863,14 @@ void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks
THROW_WALLET_EXCEPTION_IF(blocks.size() != o_indices.size(), error::wallet_internal_error, "Mismatched sizes of blocks and o_indices");
tools::threadpool& tpool = tools::threadpool::getInstance();
- tools::threadpool::waiter waiter;
+ tools::threadpool::waiter waiter(tpool);
parsed_blocks.resize(blocks.size());
for (size_t i = 0; i < blocks.size(); ++i)
{
tpool.submit(&waiter, boost::bind(&wallet2::parse_block_round, this, std::cref(blocks[i].block),
std::ref(parsed_blocks[i].block), std::ref(parsed_blocks[i].hash), std::ref(parsed_blocks[i].error)), true);
}
- waiter.wait(&tpool);
+ THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
for (size_t i = 0; i < blocks.size(); ++i)
{
if (parsed_blocks[i].error)
@@ -2896,7 +2896,7 @@ void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks
}, true);
}
}
- waiter.wait(&tpool);
+ THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
last = !blocks.empty() && cryptonote::get_block_height(parsed_blocks.back().block) + 1 == current_height;
}
catch(...)
@@ -3338,7 +3338,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
crypto::hash last_tx_hash_id = m_transfers.size() ? m_transfers.back().m_txid : null_hash;
std::list<crypto::hash> short_chain_history;
tools::threadpool& tpool = tools::threadpool::getInstance();
- tools::threadpool::waiter waiter;
+ tools::threadpool::waiter waiter(tpool);
uint64_t blocks_start_height;
std::vector<cryptonote::block_complete_entry> blocks;
std::vector<parsed_block> parsed_blocks;
@@ -3446,7 +3446,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
}
blocks_fetched += added_blocks;
}
- waiter.wait(&tpool);
+ THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
if(!first && blocks_start_height == next_blocks_start_height)
{
m_node_rpc_proxy.set_height(m_blockchain.size());
@@ -3478,19 +3478,19 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
catch (const tools::error::password_needed&)
{
blocks_fetched += added_blocks;
- waiter.wait(&tpool);
+ THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
throw;
}
catch (const error::payment_required&)
{
// no point in trying again, it'd just eat up credits
- waiter.wait(&tpool);
+ THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
throw;
}
catch (const std::exception&)
{
blocks_fetched += added_blocks;
- waiter.wait(&tpool);
+ THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
if(try_count < 3)
{
LOG_PRINT_L1("Another try pull_blocks (try_count=" << try_count << ")...");
@@ -5969,7 +5969,7 @@ uint64_t wallet2::balance(uint32_t index_major, bool strict) const
return amount;
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::unlocked_balance(uint32_t index_major, bool strict, uint64_t *blocks_to_unlock, uint64_t *time_to_unlock) const
+uint64_t wallet2::unlocked_balance(uint32_t index_major, bool strict, uint64_t *blocks_to_unlock, uint64_t *time_to_unlock)
{
uint64_t amount = 0;
if (blocks_to_unlock)
@@ -6021,7 +6021,7 @@ std::map<uint32_t, uint64_t> wallet2::balance_per_subaddress(uint32_t index_majo
return amount_per_subaddr;
}
//----------------------------------------------------------------------------------------------------
-std::map<uint32_t, std::pair<uint64_t, std::pair<uint64_t, uint64_t>>> wallet2::unlocked_balance_per_subaddress(uint32_t index_major, bool strict) const
+std::map<uint32_t, std::pair<uint64_t, std::pair<uint64_t, uint64_t>>> wallet2::unlocked_balance_per_subaddress(uint32_t index_major, bool strict)
{
std::map<uint32_t, std::pair<uint64_t, std::pair<uint64_t, uint64_t>>> amount_per_subaddr;
const uint64_t blockchain_height = get_blockchain_current_height();
@@ -6069,7 +6069,7 @@ uint64_t wallet2::balance_all(bool strict) const
return r;
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::unlocked_balance_all(bool strict, uint64_t *blocks_to_unlock, uint64_t *time_to_unlock) const
+uint64_t wallet2::unlocked_balance_all(bool strict, uint64_t *blocks_to_unlock, uint64_t *time_to_unlock)
{
uint64_t r = 0;
if (blocks_to_unlock)
@@ -6234,12 +6234,12 @@ void wallet2::rescan_blockchain(bool hard, bool refresh, bool keep_key_images)
finish_rescan_bc_keep_key_images(transfers_cnt, transfers_hash);
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::is_transfer_unlocked(const transfer_details& td) const
+bool wallet2::is_transfer_unlocked(const transfer_details& td)
{
return is_transfer_unlocked(td.m_tx.unlock_time, td.m_block_height);
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height) const
+bool wallet2::is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height)
{
if(!is_tx_spendtime_unlocked(unlock_time, block_height))
return false;
@@ -6250,7 +6250,7 @@ bool wallet2::is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height)
return true;
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_height) const
+bool wallet2::is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_height)
{
if(unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER)
{
@@ -6262,12 +6262,14 @@ bool wallet2::is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_heig
}else
{
//interpret as time
- uint64_t current_time = static_cast<uint64_t>(time(NULL));
+ uint64_t adjusted_time;
+ try { adjusted_time = get_daemon_adjusted_time(); }
+ catch(...) { adjusted_time = time(NULL); } // use local time if no daemon to report blockchain time
// XXX: this needs to be fast, so we'd need to get the starting heights
// from the daemon to be correct once voting kicks in
uint64_t v2height = m_nettype == TESTNET ? 624634 : m_nettype == STAGENET ? 32000 : 1009827;
uint64_t leeway = block_height < v2height ? CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V1 : CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2;
- if(current_time + leeway >= unlock_time)
+ if(adjusted_time + leeway >= unlock_time)
return true;
else
return false;
@@ -8052,13 +8054,16 @@ std::pair<std::set<uint64_t>, size_t> outs_unique(const std::vector<std::vector<
return std::make_pair(std::move(unique), total);
}
-void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count)
+void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, bool rct)
{
std::vector<uint64_t> rct_offsets;
for (size_t attempts = 3; attempts > 0; --attempts)
{
get_outs(outs, selected_transfers, fake_outputs_count, rct_offsets);
+ if (!rct)
+ return;
+
const auto unique = outs_unique(outs);
if (tx_sanity_check(unique.first, unique.second, rct_offsets.empty() ? 0 : rct_offsets.back()))
{
@@ -8696,7 +8701,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
THROW_WALLET_EXCEPTION_IF(subaddr_account != m_transfers[*i].m_subaddr_index.major, error::wallet_internal_error, "the tx uses funds from multiple accounts");
if (outs.empty())
- get_outs(outs, selected_transfers, fake_outputs_count); // may throw
+ get_outs(outs, selected_transfers, fake_outputs_count, false); // may throw
//prepare inputs
LOG_PRINT_L2("preparing outputs");
@@ -8897,10 +8902,12 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
MDEBUG("We will create " << n_multisig_txes << " txes");
}
+ bool all_rct = true;
uint64_t found_money = 0;
for(size_t idx: selected_transfers)
{
found_money += m_transfers[idx].amount();
+ all_rct &= m_transfers[idx].is_rct();
}
LOG_PRINT_L2("wanted " << print_money(needed_money) << ", found " << print_money(found_money) << ", fee " << print_money(fee));
@@ -8911,7 +8918,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
THROW_WALLET_EXCEPTION_IF(subaddr_account != m_transfers[*i].m_subaddr_index.major, error::wallet_internal_error, "the tx uses funds from multiple accounts");
if (outs.empty())
- get_outs(outs, selected_transfers, fake_outputs_count); // may throw
+ get_outs(outs, selected_transfers, fake_outputs_count, all_rct); // may throw
//prepare inputs
LOG_PRINT_L2("preparing outputs");
@@ -9104,7 +9111,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
LOG_PRINT_L2("transfer_selected_rct done");
}
-std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices) const
+std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices)
{
std::vector<size_t> picks;
float current_output_relatdness = 1.0f;
@@ -10682,7 +10689,7 @@ void wallet2::cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_
hw::wallet_shim wallet_shim;
setup_shim(&wallet_shim, this);
aux_data.tx_recipients = dsts_info;
- aux_data.bp_version = use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1;
+ aux_data.bp_version = (use_fork_rules(HF_VERSION_CLSAG, -10) ? 3 : use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1);
aux_data.hard_fork = get_current_hard_fork();
dev_cold->tx_sign(&wallet_shim, txs, exported_txs, aux_data);
tx_device_aux = aux_data.tx_device_aux;
@@ -10775,7 +10782,7 @@ uint64_t wallet2::get_upper_transaction_weight_limit()
return full_reward_zone - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
}
//----------------------------------------------------------------------------------------------------
-std::vector<size_t> wallet2::select_available_outputs(const std::function<bool(const transfer_details &td)> &f) const
+std::vector<size_t> wallet2::select_available_outputs(const std::function<bool(const transfer_details &td)> &f)
{
std::vector<size_t> outputs;
size_t n = 0;
@@ -12080,6 +12087,15 @@ uint64_t wallet2::get_daemon_blockchain_height(string &err)
return height;
}
+uint64_t wallet2::get_daemon_adjusted_time()
+{
+ uint64_t adjusted_time;
+
+ boost::optional<std::string> result = m_node_rpc_proxy.get_adjusted_time(adjusted_time);
+ THROW_WALLET_EXCEPTION_IF(result, error::wallet_internal_error, "Invalid adjusted time from daemon");
+ return adjusted_time;
+}
+
uint64_t wallet2::get_daemon_blockchain_target_height(string &err)
{
err = "";
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 62ed111f1..eac99185c 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -939,13 +939,13 @@ private:
// locked & unlocked balance of given or current subaddress account
uint64_t balance(uint32_t subaddr_index_major, bool strict) const;
- uint64_t unlocked_balance(uint32_t subaddr_index_major, bool strict, uint64_t *blocks_to_unlock = NULL, uint64_t *time_to_unlock = NULL) const;
+ uint64_t unlocked_balance(uint32_t subaddr_index_major, bool strict, uint64_t *blocks_to_unlock = NULL, uint64_t *time_to_unlock = NULL);
// locked & unlocked balance per subaddress of given or current subaddress account
std::map<uint32_t, uint64_t> balance_per_subaddress(uint32_t subaddr_index_major, bool strict) const;
- std::map<uint32_t, std::pair<uint64_t, std::pair<uint64_t, uint64_t>>> unlocked_balance_per_subaddress(uint32_t subaddr_index_major, bool strict) const;
+ std::map<uint32_t, std::pair<uint64_t, std::pair<uint64_t, uint64_t>>> unlocked_balance_per_subaddress(uint32_t subaddr_index_major, bool strict);
// all locked & unlocked balances of all subaddress accounts
uint64_t balance_all(bool strict) const;
- uint64_t unlocked_balance_all(bool strict, uint64_t *blocks_to_unlock = NULL, uint64_t *time_to_unlock = NULL) const;
+ uint64_t unlocked_balance_all(bool strict, uint64_t *blocks_to_unlock = NULL, uint64_t *time_to_unlock = NULL);
template<typename T>
void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
@@ -1003,8 +1003,8 @@ private:
uint64_t get_blockchain_current_height() const { return m_light_wallet_blockchain_height ? m_light_wallet_blockchain_height : m_blockchain.size(); }
void rescan_spent();
void rescan_blockchain(bool hard, bool refresh = true, bool keep_key_images = false);
- bool is_transfer_unlocked(const transfer_details& td) const;
- bool is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height) const;
+ bool is_transfer_unlocked(const transfer_details& td);
+ bool is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height);
uint64_t get_last_block_reward() const { return m_last_block_reward; }
uint64_t get_device_last_key_image_sync() const { return m_device_last_key_image_sync; }
@@ -1301,13 +1301,15 @@ private:
const boost::optional<epee::net_utils::http::login>& get_daemon_login() const { return m_daemon_login; }
uint64_t get_daemon_blockchain_height(std::string& err);
uint64_t get_daemon_blockchain_target_height(std::string& err);
+ uint64_t get_daemon_adjusted_time();
+
/*!
* \brief Calculates the approximate blockchain height from current date/time.
*/
uint64_t get_approximate_blockchain_height() const;
uint64_t estimate_blockchain_height();
std::vector<size_t> select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct);
- std::vector<size_t> select_available_outputs(const std::function<bool(const transfer_details &td)> &f) const;
+ std::vector<size_t> select_available_outputs(const std::function<bool(const transfer_details &td)> &f);
std::vector<size_t> select_available_unmixable_outputs();
std::vector<size_t> select_available_mixable_outputs();
@@ -1536,7 +1538,7 @@ private:
void set_tx_notify(const std::shared_ptr<tools::Notify> &notify) { m_tx_notify = notify; }
- bool is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_height) const;
+ bool is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_height);
void hash_m_transfer(const transfer_details & transfer, crypto::hash &hash) const;
uint64_t hash_m_transfers(int64_t transfer_height, crypto::hash &hash) const;
void finish_rescan_bc_keep_key_images(uint64_t transfer_height, const crypto::hash &hash);
@@ -1600,12 +1602,12 @@ private:
std::vector<uint64_t> get_unspent_amounts_vector(bool strict);
uint64_t get_dynamic_base_fee_estimate();
float get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const;
- std::vector<size_t> pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices) const;
+ std::vector<size_t> pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices);
void set_spent(size_t idx, uint64_t height);
void set_unspent(size_t idx);
bool is_spent(const transfer_details &td, bool strict = true) const;
bool is_spent(size_t idx, bool strict = true) const;
- void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count);
+ void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, bool rct);
void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets);
bool tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const;
bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const;
diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp
index e8f070214..d1aeef488 100644
--- a/tests/core_tests/chaingen.cpp
+++ b/tests/core_tests/chaingen.cpp
@@ -409,9 +409,9 @@ void test_generator::fill_nonce(cryptonote::block& blk, const difficulty_type& d
}
blk.nonce = 0;
- while (!miner::find_nonce_for_given_block([blockchain](const cryptonote::block &b, uint64_t height, unsigned int threads, crypto::hash &hash){
- return cryptonote::get_block_longhash(blockchain, b, hash, height, threads);
- }, blk, diffic, height)) {
+ while (!miner::find_nonce_for_given_block([blockchain](const cryptonote::block &b, uint64_t height, const crypto::hash *seed_hash, unsigned int threads, crypto::hash &hash){
+ return cryptonote::get_block_longhash(blockchain, b, hash, height, seed_hash, threads);
+ }, blk, diffic, height, NULL)) {
blk.timestamp++;
}
}
diff --git a/tests/functional_tests/functional_tests_rpc.py b/tests/functional_tests/functional_tests_rpc.py
index 3be62c0ca..79e04b8a6 100755
--- a/tests/functional_tests/functional_tests_rpc.py
+++ b/tests/functional_tests/functional_tests_rpc.py
@@ -92,6 +92,9 @@ try:
os.environ['PYTHONIOENCODING'] = 'utf-8'
os.environ['DIFFICULTY'] = str(DIFFICULTY)
os.environ['MAKE_TEST_SIGNATURE'] = builddir + '/tests/functional_tests/make_test_signature'
+ os.environ['SEEDHASH_EPOCH_BLOCKS'] = "8"
+ os.environ['SEEDHASH_EPOCH_LAG'] = "4"
+
for i in range(len(command_lines)):
#print('Running: ' + str(command_lines[i]))
processes.append(subprocess.Popen(command_lines[i], stdout = outputs[i]))
diff --git a/tests/functional_tests/mining.py b/tests/functional_tests/mining.py
index d067c25e1..c60bf8396 100755
--- a/tests/functional_tests/mining.py
+++ b/tests/functional_tests/mining.py
@@ -30,6 +30,7 @@
from __future__ import print_function
import time
+import os
"""Test daemon mining RPC calls
@@ -49,6 +50,8 @@ class MiningTest():
self.mine(True)
self.mine(False)
self.submitblock()
+ self.reset()
+ self.test_randomx()
def reset(self):
print('Resetting blockchain')
@@ -169,6 +172,117 @@ class MiningTest():
assert res.height == height + i + 1
assert res.hash == block_hash
+ def test_randomx(self):
+ print("Test RandomX")
+
+ daemon = Daemon()
+ wallet = Wallet()
+
+ res = daemon.get_height()
+ daemon.pop_blocks(res.height - 1)
+ daemon.flush_txpool()
+
+ epoch = int(os.environ['SEEDHASH_EPOCH_BLOCKS'])
+ lag = int(os.environ['SEEDHASH_EPOCH_LAG'])
+ address = '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
+
+ # check we can generate blocks, and that the seed hash changes when expected
+ res = daemon.getblocktemplate(address)
+ first_seed_hash = res.seed_hash
+ daemon.generateblocks(address, 1 + lag)
+ res = daemon.mining_status()
+ assert res.active == False
+ assert res.pow_algorithm == 'RandomX'
+ res = daemon.getblocktemplate(address)
+ seed_hash = res.seed_hash
+ t0 = time.time()
+ daemon.generateblocks(address, epoch - 3)
+ t0 = time.time() - t0
+ res = daemon.get_info()
+ assert res.height == lag + epoch - 1
+ res = daemon.getblocktemplate(address)
+ assert seed_hash == res.seed_hash
+ t0 = time.time()
+ daemon.generateblocks(address, 1)
+ t0 = time.time() - t0
+ res = daemon.get_info()
+ assert res.height == lag + epoch
+ daemon.generateblocks(address, 1)
+ res = daemon.getblocktemplate(address)
+ assert seed_hash != res.seed_hash
+ new_seed_hash = res.seed_hash
+ t0 = time.time()
+ daemon.generateblocks(address, epoch - 1)
+ t0 = time.time() - t0
+ res = daemon.getblocktemplate(address)
+ assert new_seed_hash == res.seed_hash
+ daemon.generateblocks(address, 1)
+ res = daemon.getblocktemplate(address)
+ assert new_seed_hash != res.seed_hash
+ new_seed_hash = res.seed_hash
+ t0 = time.time()
+ daemon.generateblocks(address, epoch - 1)
+ t0 = time.time() - t0
+ res = daemon.getblocktemplate(address)
+ assert new_seed_hash == res.seed_hash
+ daemon.generateblocks(address, 1)
+ res = daemon.getblocktemplate(address)
+ assert new_seed_hash != res.seed_hash
+ #print('First mining: ' + str(t0))
+
+ # pop all these blocks, and feed them again to monerod
+ print('Recreating the chain')
+ res = daemon.get_info()
+ height = res.height
+ assert height == lag + epoch * 3 + 1
+ block_hashes = [x.hash for x in daemon.getblockheadersrange(0, height - 1).headers]
+ assert len(block_hashes) == height
+ blocks = []
+ for i in range(len(block_hashes)):
+ res = daemon.getblock(height = i)
+ assert res.block_header.hash == block_hashes[i]
+ blocks.append(res.blob)
+ daemon.pop_blocks(height)
+ res = daemon.get_info()
+ assert res.height == 1
+ res = daemon.getblocktemplate(address)
+ assert first_seed_hash == res.seed_hash
+ t0 = time.time()
+ for h in range(len(block_hashes)):
+ res = daemon.submitblock(blocks[h])
+ t0 = time.time() - t0
+ res = daemon.get_info()
+ assert height == res.height
+ res = daemon.getblocktemplate(address)
+ assert new_seed_hash != res.seed_hash
+ res = daemon.pop_blocks(1)
+ res = daemon.getblocktemplate(address)
+ assert new_seed_hash == res.seed_hash
+ #print('Submit: ' + str(t0))
+
+ # start mining from the genesis block again
+ print('Mining from genesis block again')
+ res = daemon.get_height()
+ top_hash = res.hash
+ res = daemon.getblockheaderbyheight(0)
+ genesis_block_hash = res.block_header.hash
+ t0 = time.time()
+ daemon.generateblocks(address, height - 2, prev_block = genesis_block_hash)
+ t0 = time.time() - t0
+ res = daemon.get_info()
+ assert res.height == height - 1
+ assert res.top_block_hash == top_hash
+ #print('Second mining: ' + str(t0))
+
+ # that one will cause a huge reorg
+ print('Adding one to reorg')
+ res = daemon.generateblocks(address, 1)
+ assert len(res.blocks) == 1
+ new_top_hash = res.blocks[0]
+ res = daemon.get_info()
+ assert res.height == height
+ assert res.top_block_hash == new_top_hash
+
class Guard:
def __enter__(self):
diff --git a/tests/functional_tests/p2p.py b/tests/functional_tests/p2p.py
index f36e9c0b1..0b33411f9 100755
--- a/tests/functional_tests/p2p.py
+++ b/tests/functional_tests/p2p.py
@@ -139,6 +139,25 @@ class P2PTest():
assert res.height == height + 6
assert res.top_block_hash == daemon2_top_block_hash
+ # disconnect and mine a lot on daemon3
+ daemon2.out_peers(0)
+ daemon3.out_peers(0)
+ res = daemon3.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 500)
+
+ # reconnect and wait for sync
+ daemon2.out_peers(8)
+ daemon3.out_peers(8)
+ loops = 100
+ while True:
+ res2 = daemon2.get_info()
+ res3 = daemon3.get_info()
+ if res2.top_block_hash == res3.top_block_hash:
+ break
+ time.sleep(10)
+ loops -= 1
+ assert loops >= 0
+
+
def test_p2p_tx_propagation(self):
print('Testing P2P tx propagation')
daemon2 = Daemon(idx = 2)
diff --git a/tests/trezor/daemon.cpp b/tests/trezor/daemon.cpp
index aba835ae2..dd9fd49ee 100644
--- a/tests/trezor/daemon.cpp
+++ b/tests/trezor/daemon.cpp
@@ -247,7 +247,7 @@ bool mock_daemon::run_main()
if (m_start_zmq)
{
- if (!zmq_server.addTCPSocket("127.0.0.1", m_zmq_bind_port))
+ if (!zmq_server.init_rpc("127.0.0.1", m_zmq_bind_port))
{
MERROR("Failed to add TCP Socket (127.0.0.1:" << m_zmq_bind_port << ") to ZMQ RPC Server");
diff --git a/tests/trezor/trezor_tests.cpp b/tests/trezor/trezor_tests.cpp
index 6a92868cf..972a588f3 100644
--- a/tests/trezor/trezor_tests.cpp
+++ b/tests/trezor/trezor_tests.cpp
@@ -139,7 +139,7 @@ int main(int argc, char* argv[])
// Bootstrapping common chain & accounts
const uint8_t initial_hf = (uint8_t)get_env_long("TEST_MIN_HF", 12);
- const uint8_t max_hf = (uint8_t)get_env_long("TEST_MAX_HF", 12);
+ const uint8_t max_hf = (uint8_t)get_env_long("TEST_MAX_HF", HF_VERSION_CLSAG);
auto sync_test = get_env_long("TEST_KI_SYNC", 1);
MINFO("Test versions " << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")");
MINFO("Testing hardforks [" << (int)initial_hf << ", " << (int)max_hf << "], sync-test: " << sync_test);
@@ -546,7 +546,7 @@ static void expand_tsx(cryptonote::transaction &tx)
for (size_t n = 0; n < tx.vin.size(); ++n)
rv.p.MGs[0].II[n] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image);
}
- else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2 || rv.type == rct::RCTTypeCLSAG)
+ else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2)
{
CHECK_AND_ASSERT_THROW_MES(rv.p.MGs.size() == tx.vin.size(), "Bad MGs size");
for (size_t n = 0; n < tx.vin.size(); ++n)
@@ -555,6 +555,21 @@ static void expand_tsx(cryptonote::transaction &tx)
rv.p.MGs[n].II[0] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image);
}
}
+ else if (rv.type == rct::RCTTypeCLSAG)
+ {
+ if (!tx.pruned)
+ {
+ CHECK_AND_ASSERT_THROW_MES(rv.p.CLSAGs.size() == tx.vin.size(), "Bad CLSAGs size");
+ for (size_t n = 0; n < tx.vin.size(); ++n)
+ {
+ rv.p.CLSAGs[n].I = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image);
+ }
+ }
+ }
+ else
+ {
+ CHECK_AND_ASSERT_THROW_MES(false, "Unsupported rct tx type: " + boost::lexical_cast<std::string>(rv.type));
+ }
}
static std::vector<tools::wallet2*> vct_wallets(tools::wallet2* w1=nullptr, tools::wallet2* w2=nullptr, tools::wallet2* w3=nullptr, tools::wallet2* w4=nullptr, tools::wallet2* w5=nullptr)
@@ -708,7 +723,9 @@ bool gen_trezor_base::generate(std::vector<test_event_entry>& events)
std::vector<size_t> block_weights;
generate_genesis_block(blk_gen, get_config(m_network_type).GENESIS_TX, get_config(m_network_type).GENESIS_NONCE);
events.push_back(blk_gen);
- generator.add_block(blk_gen, 0, block_weights, 0);
+ uint64_t rew = 0;
+ cryptonote::get_block_reward(0, get_transaction_weight(blk_gen.miner_tx), 0, rew, 1);
+ generator.add_block(blk_gen, 0, block_weights, 0, rew);
// First event has to be the genesis block
m_bob_account.generate();
@@ -926,7 +943,7 @@ void gen_trezor_base::fix_hf(std::vector<test_event_entry>& events)
// If current test requires higher hard-fork, move it up
auto current_hf = m_hard_forks.back().first;
CHECK_AND_ASSERT_THROW_MES(current_hf <= m_top_hard_fork, "Generated chain hardfork is higher than desired maximum");
- CHECK_AND_ASSERT_THROW_MES(m_rct_config.bp_version != 2 || m_top_hard_fork >= 10, "Desired maximum is too low for BPv2");
+ CHECK_AND_ASSERT_THROW_MES(m_rct_config.bp_version < 2 || m_top_hard_fork >= 10, "Desired maximum is too low for BPv2");
for(;current_hf < m_top_hard_fork; current_hf+=1)
{
@@ -1014,9 +1031,10 @@ void gen_trezor_base::test_trezor_tx(std::vector<test_event_entry>& events, std:
setup_shim(&wallet_shim);
aux_data.tx_recipients = dsts_info;
aux_data.bp_version = m_rct_config.bp_version;
+ aux_data.hard_fork = m_top_hard_fork;
dev_cold->tx_sign(&wallet_shim, txs, exported_txs, aux_data);
- MDEBUG("Signed tx data from hw: " << exported_txs.ptx.size() << " transactions");
+ MDEBUG("Signed tx data from hw: " << exported_txs.ptx.size() << " transactions, hf: " << (int)m_top_hard_fork << ", bpv: " << m_rct_config.bp_version);
CHECK_AND_ASSERT_THROW_MES(exported_txs.ptx.size() == ptxs.size(), "Invalid transaction sizes");
for (size_t i = 0; i < exported_txs.ptx.size(); ++i){
@@ -1245,10 +1263,14 @@ void gen_trezor_base::set_hard_fork(uint8_t hf)
m_top_hard_fork = hf;
if (hf < 9){
throw std::runtime_error("Minimal supported Hardfork is 9");
- } else if (hf == 9){
+ } else if (hf <= 11){
rct_config({rct::RangeProofPaddedBulletproof, 1});
- } else {
+ } else if (hf == 12){
rct_config({rct::RangeProofPaddedBulletproof, 2});
+ } else if (hf == HF_VERSION_CLSAG){
+ rct_config({rct::RangeProofPaddedBulletproof, 3});
+ } else {
+ throw std::runtime_error("Unsupported HF");
}
}
@@ -1844,7 +1866,7 @@ bool wallet_api_tests::generate(std::vector<test_event_entry>& events)
CHECK_AND_ASSERT_THROW_MES(w->refresh(), "Refresh fail");
uint64_t balance = w->balance(0);
MDEBUG("Balance: " << balance);
- CHECK_AND_ASSERT_THROW_MES(w->status() == Monero::PendingTransaction::Status_Ok, "Status nok");
+ CHECK_AND_ASSERT_THROW_MES(w->status() == Monero::PendingTransaction::Status_Ok, "Status nok, " << w->errorString());
auto addr = get_address(m_eve_account);
auto recepient_address = cryptonote::get_account_address_as_str(m_network_type, false, addr);
@@ -1855,7 +1877,7 @@ bool wallet_api_tests::generate(std::vector<test_event_entry>& events)
Monero::PendingTransaction::Priority_Medium,
0,
std::set<uint32_t>{});
- CHECK_AND_ASSERT_THROW_MES(transaction->status() == Monero::PendingTransaction::Status_Ok, "Status nok");
+ CHECK_AND_ASSERT_THROW_MES(transaction->status() == Monero::PendingTransaction::Status_Ok, "Status nok: " << transaction->status() << ", msg: " << transaction->errorString());
w->refresh();
CHECK_AND_ASSERT_THROW_MES(w->balance(0) == balance, "Err");
diff --git a/tests/unit_tests/threadpool.cpp b/tests/unit_tests/threadpool.cpp
index 1307cd738..1017f04ff 100644
--- a/tests/unit_tests/threadpool.cpp
+++ b/tests/unit_tests/threadpool.cpp
@@ -34,46 +34,46 @@
TEST(threadpool, wait_nothing)
{
std::shared_ptr<tools::threadpool> tpool(tools::threadpool::getNewForUnitTests());
- tools::threadpool::waiter waiter;
- waiter.wait(tpool.get());
+ tools::threadpool::waiter waiter(*tpool);;
+ waiter.wait();
}
TEST(threadpool, wait_waits)
{
std::shared_ptr<tools::threadpool> tpool(tools::threadpool::getNewForUnitTests());
- tools::threadpool::waiter waiter;
+ tools::threadpool::waiter waiter(*tpool);
std::atomic<bool> b(false);
tpool->submit(&waiter, [&b](){ epee::misc_utils::sleep_no_w(1000); b = true; });
ASSERT_FALSE(b);
- waiter.wait(tpool.get());
+ waiter.wait();
ASSERT_TRUE(b);
}
TEST(threadpool, one_thread)
{
std::shared_ptr<tools::threadpool> tpool(tools::threadpool::getNewForUnitTests(1));
- tools::threadpool::waiter waiter;
+ tools::threadpool::waiter waiter(*tpool);
std::atomic<unsigned int> counter(0);
for (size_t n = 0; n < 4096; ++n)
{
tpool->submit(&waiter, [&counter](){++counter;});
}
- waiter.wait(tpool.get());
+ waiter.wait();
ASSERT_EQ(counter, 4096);
}
TEST(threadpool, many_threads)
{
std::shared_ptr<tools::threadpool> tpool(tools::threadpool::getNewForUnitTests(256));
- tools::threadpool::waiter waiter;
+ tools::threadpool::waiter waiter(*tpool);
std::atomic<unsigned int> counter(0);
for (size_t n = 0; n < 4096; ++n)
{
tpool->submit(&waiter, [&counter](){++counter;});
}
- waiter.wait(tpool.get());
+ waiter.wait();
ASSERT_EQ(counter, 4096);
}
@@ -82,44 +82,44 @@ static uint64_t fibonacci(std::shared_ptr<tools::threadpool> tpool, uint64_t n)
if (n <= 1)
return n;
uint64_t f1, f2;
- tools::threadpool::waiter waiter;
+ tools::threadpool::waiter waiter(*tpool);
tpool->submit(&waiter, [&tpool, &f1, n](){ f1 = fibonacci(tpool, n-1); });
tpool->submit(&waiter, [&tpool, &f2, n](){ f2 = fibonacci(tpool, n-2); });
- waiter.wait(tpool.get());
+ waiter.wait();
return f1 + f2;
}
TEST(threadpool, reentrency)
{
std::shared_ptr<tools::threadpool> tpool(tools::threadpool::getNewForUnitTests(4));
- tools::threadpool::waiter waiter;
+ tools::threadpool::waiter waiter(*tpool);
uint64_t f = fibonacci(tpool, 13);
- waiter.wait(tpool.get());
+ waiter.wait();
ASSERT_EQ(f, 233);
}
TEST(threadpool, reentrancy)
{
std::shared_ptr<tools::threadpool> tpool(tools::threadpool::getNewForUnitTests(4));
- tools::threadpool::waiter waiter;
+ tools::threadpool::waiter waiter(*tpool);
uint64_t f = fibonacci(tpool, 13);
- waiter.wait(tpool.get());
+ waiter.wait();
ASSERT_EQ(f, 233);
}
TEST(threadpool, leaf_throws)
{
std::shared_ptr<tools::threadpool> tpool(tools::threadpool::getNewForUnitTests());
- tools::threadpool::waiter waiter;
+ tools::threadpool::waiter waiter(*tpool);
bool thrown = false, executed = false;
tpool->submit(&waiter, [&](){
try { tpool->submit(&waiter, [&](){ executed = true; }); }
catch(const std::exception &e) { thrown = true; }
}, true);
- waiter.wait(tpool.get());
+ waiter.wait();
ASSERT_TRUE(thrown);
ASSERT_FALSE(executed);
}
@@ -127,20 +127,20 @@ TEST(threadpool, leaf_throws)
TEST(threadpool, leaf_reentrancy)
{
std::shared_ptr<tools::threadpool> tpool(tools::threadpool::getNewForUnitTests(4));
- tools::threadpool::waiter waiter;
+ tools::threadpool::waiter waiter(*tpool);
std::atomic<int> counter(0);
for (int i = 0; i < 1000; ++i)
{
tpool->submit(&waiter, [&](){
- tools::threadpool::waiter waiter;
+ tools::threadpool::waiter waiter(*tpool);
for (int j = 0; j < 500; ++j)
{
tpool->submit(&waiter, [&](){ ++counter; }, true);
}
- waiter.wait(tpool.get());
+ waiter.wait();
});
}
- waiter.wait(tpool.get());
+ waiter.wait();
ASSERT_EQ(counter, 500000);
}