Add GET_HASHES_FAST rpc, use it in wallet

When m_refresh_from_block_height has been set, only hashes will be
retrieved up to that height, instead of full blocks. The same will
be done for "refresh <height>" when the specified height is beyond
the current local blockchain.
This commit is contained in:
Howard Chu 2016-04-13 23:45:02 +01:00
parent 1c66fe04bc
commit b7140daea2
6 changed files with 145 additions and 0 deletions

View file

@ -166,6 +166,25 @@ namespace cryptonote
return true; 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();
NOTIFY_RESPONSE_CHAIN_ENTRY::request resp;
resp.start_height = req.start_height;
if(!m_core.find_blockchain_supplement(req.block_ids, resp))
{
res.status = "Failed";
return false;
}
res.current_height = resp.total_height;
res.start_height = resp.start_height;
res.m_block_ids = std::move(resp.m_block_ids);
res.status = CORE_RPC_STATUS_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) bool core_rpc_server::on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res)
{ {
CHECK_CORE_BUSY(); CHECK_CORE_BUSY();

View file

@ -75,6 +75,7 @@ namespace cryptonote
BEGIN_URI_MAP2() BEGIN_URI_MAP2()
MAP_URI_AUTO_JON2("/getheight", on_get_height, COMMAND_RPC_GET_HEIGHT) 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.bin", on_get_blocks, COMMAND_RPC_GET_BLOCKS_FAST)
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("/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) MAP_URI_AUTO_BIN2("/getrandom_outs.bin", on_get_random_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS)
MAP_URI_AUTO_JON2("/gettransactions", on_get_transactions, COMMAND_RPC_GET_TRANSACTIONS) MAP_URI_AUTO_JON2("/gettransactions", on_get_transactions, COMMAND_RPC_GET_TRANSACTIONS)
@ -115,6 +116,7 @@ namespace cryptonote
bool on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res); 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(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::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_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); bool on_is_key_image_spent(const COMMAND_RPC_IS_KEY_IMAGE_SPENT::request& req, COMMAND_RPC_IS_KEY_IMAGE_SPENT::response& res);
bool on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res); bool on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res);

View file

@ -89,6 +89,36 @@ namespace cryptonote
END_KV_SERIALIZE_MAP() END_KV_SERIALIZE_MAP()
}; };
}; };
struct COMMAND_RPC_GET_HASHES_FAST
{
struct request
{
std::list<crypto::hash> block_ids; //*first 10 blocks id goes sequential, next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */
uint64_t start_height;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids)
KV_SERIALIZE(start_height)
END_KV_SERIALIZE_MAP()
};
struct response
{
std::list<crypto::hash> m_block_ids;
uint64_t start_height;
uint64_t current_height;
std::string status;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_block_ids)
KV_SERIALIZE(start_height)
KV_SERIALIZE(current_height)
KV_SERIALIZE(status)
END_KV_SERIALIZE_MAP()
};
};
//----------------------------------------------- //-----------------------------------------------
struct COMMAND_RPC_GET_TRANSACTIONS struct COMMAND_RPC_GET_TRANSACTIONS
{ {

View file

@ -578,6 +578,24 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height,
blocks = res.blocks; blocks = res.blocks;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::list<crypto::hash> &hashes)
{
cryptonote::COMMAND_RPC_GET_HASHES_FAST::request req = AUTO_VAL_INIT(req);
cryptonote::COMMAND_RPC_GET_HASHES_FAST::response res = AUTO_VAL_INIT(res);
req.block_ids = short_chain_history;
req.start_height = start_height;
m_daemon_rpc_mutex.lock();
bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/gethashes.bin", req, res, m_http_client, WALLET_RCP_CONNECTION_TIMEOUT);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gethashes.bin");
THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gethashes.bin");
THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_hashes_error, res.status);
blocks_start_height = res.start_height;
hashes = res.m_block_ids;
}
//----------------------------------------------------------------------------------------------------
void wallet2::process_blocks(uint64_t start_height, const std::list<cryptonote::block_complete_entry> &blocks, uint64_t& blocks_added) void wallet2::process_blocks(uint64_t start_height, const std::list<cryptonote::block_complete_entry> &blocks, uint64_t& blocks_added)
{ {
size_t current_index = start_height; size_t current_index = start_height;
@ -771,6 +789,60 @@ void wallet2::check_pending_txes()
} }
} }
} }
//----------------------------------------------------------------------------------------------------
void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history)
{
std::list<crypto::hash> hashes;
size_t current_index = m_blockchain.size();
while(current_index < stop_height)
{
pull_hashes(0, blocks_start_height, short_chain_history, hashes);
if (hashes.size() < 3)
return;
if (hashes.size() + current_index < stop_height) {
std::list<crypto::hash>::iterator right;
// drop early 3 off, skipping the genesis block
if (short_chain_history.size() > 3) {
right = short_chain_history.end();
std::advance(right,-1);
std::list<crypto::hash>::iterator left = right;
std::advance(left, -3);
short_chain_history.erase(left, right);
}
right = hashes.end();
// prepend 3 more
for (int i = 0; i<3; i++) {
right--;
short_chain_history.push_front(*right);
}
}
current_index = blocks_start_height;
BOOST_FOREACH(auto& bl_id, hashes)
{
if(current_index >= m_blockchain.size())
{
LOG_PRINT_L2( "Skipped block by height: " << current_index);
m_blockchain.push_back(bl_id);
++m_local_bc_height;
if (0 != m_callback)
{ // FIXME: this isn't right, but simplewallet just logs that we got a block.
cryptonote::block dummy;
m_callback->on_new_block(current_index, dummy);
}
}
else if(bl_id != m_blockchain[current_index])
{
//split detected here !!!
return;
}
++current_index;
if (current_index >= stop_height)
return;
}
}
}
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& received_money) void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& received_money)
{ {
@ -786,7 +858,22 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
// pull the first set of blocks // pull the first set of blocks
get_short_chain_history(short_chain_history); get_short_chain_history(short_chain_history);
if (start_height > m_blockchain.size() || m_refresh_from_block_height > m_blockchain.size()) {
if (!start_height)
start_height = m_refresh_from_block_height;
// we can shortcut by only pulling hashes up to the start_height
fast_refresh(start_height, blocks_start_height, short_chain_history);
// regenerate the history now that we've got a full set of hashes
short_chain_history.clear();
get_short_chain_history(short_chain_history);
start_height = 0;
// and then fall through to regular refresh processing
}
pull_blocks(start_height, blocks_start_height, short_chain_history, blocks); pull_blocks(start_height, blocks_start_height, short_chain_history, blocks);
// always reset start_height to 0 to force short_chain_ history to be used on
// subsequent pulls in this refresh.
start_height = 0;
m_run.store(true, std::memory_order_relaxed); m_run.store(true, std::memory_order_relaxed);
while(m_run.load(std::memory_order_relaxed)) while(m_run.load(std::memory_order_relaxed))

View file

@ -387,6 +387,8 @@ namespace tools
bool is_transfer_unlocked(const transfer_details& td) const; bool is_transfer_unlocked(const transfer_details& td) const;
bool clear(); bool clear();
void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::list<cryptonote::block_complete_entry> &blocks); void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::list<cryptonote::block_complete_entry> &blocks);
void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::list<crypto::hash> &hashes);
void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history);
void pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::list<cryptonote::block_complete_entry> &prev_blocks, std::list<cryptonote::block_complete_entry> &blocks, bool &error); void pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::list<cryptonote::block_complete_entry> &prev_blocks, std::list<cryptonote::block_complete_entry> &blocks, bool &error);
void process_blocks(uint64_t start_height, const std::list<cryptonote::block_complete_entry> &blocks, uint64_t& blocks_added); void process_blocks(uint64_t start_height, const std::list<cryptonote::block_complete_entry> &blocks, uint64_t& blocks_added);
uint64_t select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::list<transfer_container::iterator>& selected_transfers, bool trusted_daemon); uint64_t select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::list<transfer_container::iterator>& selected_transfers, bool trusted_daemon);

View file

@ -60,6 +60,7 @@ namespace tools
// acc_outs_lookup_error // acc_outs_lookup_error
// block_parse_error // block_parse_error
// get_blocks_error // get_blocks_error
// get_hashes_error
// get_out_indexes_error // get_out_indexes_error
// tx_parse_error // tx_parse_error
// get_tx_pool_error // get_tx_pool_error
@ -107,12 +108,14 @@ namespace tools
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
const char* const failed_rpc_request_messages[] = { const char* const failed_rpc_request_messages[] = {
"failed to get blocks", "failed to get blocks",
"failed to get hashes",
"failed to get out indices", "failed to get out indices",
"failed to get random outs" "failed to get random outs"
}; };
enum failed_rpc_request_message_indices enum failed_rpc_request_message_indices
{ {
get_blocks_error_message_index, get_blocks_error_message_index,
get_hashes_error_message_index,
get_out_indices_error_message_index, get_out_indices_error_message_index,
get_random_outs_error_message_index get_random_outs_error_message_index
}; };
@ -291,6 +294,8 @@ namespace tools
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
typedef failed_rpc_request<refresh_error, get_blocks_error_message_index> get_blocks_error; typedef failed_rpc_request<refresh_error, get_blocks_error_message_index> get_blocks_error;
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
typedef failed_rpc_request<refresh_error, get_hashes_error_message_index> get_hashes_error;
//----------------------------------------------------------------------------------------------------
typedef failed_rpc_request<refresh_error, get_out_indices_error_message_index> get_out_indices_error; typedef failed_rpc_request<refresh_error, get_out_indices_error_message_index> get_out_indices_error;
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
struct tx_parse_error : public refresh_error struct tx_parse_error : public refresh_error