specify restore height by YYYY-MM-DD format
This commit is contained in:
parent
e459d8604d
commit
16b8b66adc
6 changed files with 207 additions and 17 deletions
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue