aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/rpc/core_rpc_server.cpp30
-rw-r--r--src/rpc/core_rpc_server.h2
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h24
-rw-r--r--src/simplewallet/simplewallet.cpp80
-rw-r--r--src/wallet/wallet2.cpp86
-rw-r--r--src/wallet/wallet2.h2
6 files changed, 207 insertions, 17 deletions
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 0cec3c26e..a4f8283bd 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -190,6 +190,36 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_get_blocks_by_height(const COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::response& res)
+ {
+ CHECK_CORE_BUSY();
+ res.status = "Failed";
+ res.blocks.clear();
+ res.blocks.reserve(req.heights.size());
+ for (uint64_t height : req.heights)
+ {
+ block blk;
+ try
+ {
+ blk = m_core.get_blockchain_storage().get_db().get_block_from_height(height);
+ }
+ catch (...)
+ {
+ res.status = "Error retrieving block at height " + height;
+ return true;
+ }
+ std::list<transaction> txs;
+ std::list<crypto::hash> missed_txs;
+ m_core.get_transactions(blk.tx_hashes, txs, missed_txs);
+ res.blocks.resize(res.blocks.size() + 1);
+ res.blocks.back().block = block_to_blob(blk);
+ for (auto& tx : txs)
+ res.blocks.back().txs.push_back(tx_to_blob(tx));
+ }
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_hashes(const COMMAND_RPC_GET_HASHES_FAST::request& req, COMMAND_RPC_GET_HASHES_FAST::response& res)
{
CHECK_CORE_BUSY();
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index 0c0668f3b..bce9e061f 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -76,6 +76,7 @@ namespace cryptonote
BEGIN_URI_MAP2()
MAP_URI_AUTO_JON2("/getheight", on_get_height, COMMAND_RPC_GET_HEIGHT)
MAP_URI_AUTO_BIN2("/getblocks.bin", on_get_blocks, COMMAND_RPC_GET_BLOCKS_FAST)
+ MAP_URI_AUTO_BIN2("/getblocks_by_height.bin", on_get_blocks_by_height, COMMAND_RPC_GET_BLOCKS_BY_HEIGHT)
MAP_URI_AUTO_BIN2("/gethashes.bin", on_get_hashes, COMMAND_RPC_GET_HASHES_FAST)
MAP_URI_AUTO_BIN2("/get_o_indexes.bin", on_get_indexes, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES)
MAP_URI_AUTO_BIN2("/getrandom_outs.bin", on_get_random_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS)
@@ -124,6 +125,7 @@ namespace cryptonote
bool on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res);
bool on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res);
+ bool on_get_blocks_by_height(const COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::response& res);
bool on_get_hashes(const COMMAND_RPC_GET_HASHES_FAST::request& req, COMMAND_RPC_GET_HASHES_FAST::response& res);
bool on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res);
bool on_is_key_image_spent(const COMMAND_RPC_IS_KEY_IMAGE_SPENT::request& req, COMMAND_RPC_IS_KEY_IMAGE_SPENT::response& res);
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index 2530bc06d..620a2e80d 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -49,7 +49,7 @@ namespace cryptonote
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define CORE_RPC_VERSION_MAJOR 1
-#define CORE_RPC_VERSION_MINOR 5
+#define CORE_RPC_VERSION_MINOR 6
#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)
@@ -122,6 +122,28 @@ namespace cryptonote
};
};
+ struct COMMAND_RPC_GET_BLOCKS_BY_HEIGHT
+ {
+ struct request
+ {
+ std::vector<uint64_t> heights;
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(heights)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::vector<block_complete_entry> blocks;
+ std::string status;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(blocks)
+ KV_SERIALIZE(status)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
struct COMMAND_RPC_GET_HASHES_FAST
{
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 3412617d4..ddfb7d948 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -934,22 +934,6 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
return false;
}
}
- if (!m_restore_height && m_restoring)
- {
- std::string heightstr = command_line::input_line("Restore from specific blockchain height (optional, default 0): ");
- if (std::cin.eof())
- return false;
- if (heightstr.size())
- {
- try {
- m_restore_height = boost::lexical_cast<uint64_t>(heightstr);
- }
- catch (boost::bad_lexical_cast &) {
- fail_msg_writer() << tr("bad m_restore_height parameter:") << " " << heightstr;
- return false;
- }
- }
- }
if (!m_generate_from_view_key.empty())
{
m_wallet_file = m_generate_from_view_key;
@@ -1090,6 +1074,70 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
bool r = new_wallet(vm, m_recovery_key, m_restore_deterministic_wallet, m_non_deterministic, old_language);
CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
}
+ if (!m_restore_height && m_restoring)
+ {
+ uint32_t version;
+ bool connected = try_connect_to_daemon(false, &version);
+ while (true)
+ {
+ std::string heightstr;
+ if (!connected || version < MAKE_CORE_RPC_VERSION(1, 6))
+ heightstr = command_line::input_line("Restore from specific blockchain height (optional, default 0): ");
+ else
+ heightstr = command_line::input_line("Restore from specific blockchain height (optional, default 0),\nor alternatively from specific date (YYYY-MM-DD): ");
+ if (std::cin.eof())
+ return false;
+ if (heightstr.empty())
+ {
+ m_restore_height = 0;
+ break;
+ }
+ try
+ {
+ m_restore_height = boost::lexical_cast<uint64_t>(heightstr);
+ break;
+ }
+ catch (const boost::bad_lexical_cast &)
+ {
+ if (!connected || version < MAKE_CORE_RPC_VERSION(1, 6))
+ {
+ fail_msg_writer() << tr("bad m_restore_height parameter: ") << heightstr;
+ continue;
+ }
+ if (heightstr.size() != 10 || heightstr[4] != '-' || heightstr[7] != '-')
+ {
+ fail_msg_writer() << tr("date format must be YYYY-MM-DD");
+ continue;
+ }
+ uint16_t year;
+ uint8_t month; // 1, 2, ..., 12
+ uint8_t day; // 1, 2, ..., 31
+ try
+ {
+ year = boost::lexical_cast<uint16_t>(heightstr.substr(0,4));
+ // lexical_cast<uint8_t> won't work becasue uint8_t is treated as character type
+ month = boost::lexical_cast<uint16_t>(heightstr.substr(5,2));
+ day = boost::lexical_cast<uint16_t>(heightstr.substr(8,2));
+ m_restore_height = m_wallet->get_blockchain_height_by_date(year, month, day);
+ success_msg_writer() << tr("Restore height is: ") << m_restore_height;
+ std::string confirm = command_line::input_line(tr("Is this okay? (Y/Yes/N/No): "));
+ if (std::cin.eof())
+ return false;
+ if(command_line::is_yes(confirm))
+ break;
+ }
+ catch (const boost::bad_lexical_cast &)
+ {
+ fail_msg_writer() << tr("bad m_restore_height parameter: ") << heightstr;
+ }
+ catch (const std::runtime_error& e)
+ {
+ fail_msg_writer() << e.what();
+ }
+ }
+ }
+ m_wallet->set_refresh_from_block_height(m_restore_height);
+ }
}
else
{
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 8a03b94af..7a83b883a 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -5330,6 +5330,92 @@ bool wallet2::parse_uri(const std::string &uri, std::string &address, std::strin
return true;
}
//----------------------------------------------------------------------------------------------------
+uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, uint8_t day)
+{
+ uint32_t version;
+ if (!check_connection(&version))
+ {
+ throw std::runtime_error("failed to connect to daemon: " + get_daemon_address());
+ }
+ if (version < MAKE_CORE_RPC_VERSION(1, 6))
+ {
+ throw std::runtime_error("this function requires RPC version 1.6 or higher");
+ }
+ std::tm date = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ date.tm_year = year - 1900;
+ date.tm_mon = month - 1;
+ date.tm_mday = day;
+ if (date.tm_mon < 0 || 11 < date.tm_mon || date.tm_mday < 1 || 31 < date.tm_mday)
+ {
+ throw std::runtime_error("month or day out of range");
+ }
+ uint64_t timestamp_target = std::mktime(&date);
+ std::string err;
+ uint64_t height_min = 0;
+ uint64_t height_max = get_daemon_blockchain_height(err) - 1;
+ if (!err.empty())
+ {
+ throw std::runtime_error("failed to get blockchain height");
+ }
+ while (true)
+ {
+ COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::request req;
+ COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::response res;
+ uint64_t height_mid = (height_min + height_max) / 2;
+ req.heights =
+ {
+ height_min,
+ height_mid,
+ height_max
+ };
+ bool r = net_utils::invoke_http_bin_remote_command2(get_daemon_address() + "/getblocks_by_height.bin", req, res, m_http_client);
+ if (!r || res.status != CORE_RPC_STATUS_OK)
+ {
+ std::ostringstream oss;
+ oss << "failed to get blocks by heights: ";
+ for (auto height : req.heights)
+ oss << height << ' ';
+ oss << endl << "reason: ";
+ if (!r)
+ oss << "possibly lost connection to daemon";
+ else if (res.status == CORE_RPC_STATUS_BUSY)
+ oss << "daemon is busy";
+ else
+ oss << res.status;
+ throw std::runtime_error(oss.str());
+ }
+ cryptonote::block blk_min, blk_mid, blk_max;
+ if (!parse_and_validate_block_from_blob(res.blocks[0].block, blk_min)) throw std::runtime_error("failed to parse blob at height " + height_min);
+ if (!parse_and_validate_block_from_blob(res.blocks[1].block, blk_mid)) throw std::runtime_error("failed to parse blob at height " + height_mid);
+ if (!parse_and_validate_block_from_blob(res.blocks[2].block, blk_max)) throw std::runtime_error("failed to parse blob at height " + height_max);
+ uint64_t timestamp_min = blk_min.timestamp;
+ uint64_t timestamp_mid = blk_mid.timestamp;
+ uint64_t timestamp_max = blk_max.timestamp;
+ if (!(timestamp_min <= timestamp_mid && timestamp_mid <= timestamp_max))
+ {
+ // the timestamps are not in the chronological order.
+ // assuming they're sufficiently close to each other, simply return the smallest height
+ return std::min({height_min, height_mid, height_max});
+ }
+ if (timestamp_target > timestamp_max)
+ {
+ throw std::runtime_error("specified date is in the future");
+ }
+ if (timestamp_target <= timestamp_min + 2 * 24 * 60 * 60) // two days of "buffer" period
+ {
+ return height_min;
+ }
+ if (timestamp_target <= timestamp_mid)
+ height_max = height_mid;
+ else
+ height_min = height_mid;
+ if (height_max - height_min <= 2 * 24 * 30) // don't divide the height range finer than two days
+ {
+ return height_min;
+ }
+ }
+}
+//----------------------------------------------------------------------------------------------------
void wallet2::generate_genesis(cryptonote::block& b) {
if (m_testnet)
{
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 5a569950f..21692d7b4 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -554,6 +554,8 @@ namespace tools
std::string make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error);
bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error);
+ uint64_t get_blockchain_height_by_date(uint16_t year, uint8_t month, uint8_t day); // 1<=month<=12, 1<=day<=31
+
private:
/*!
* \brief Stores wallet information to wallet file.