Optional "block" given to RPC "work_generate" to infer difficulty (#2754)
* Simplify block_impl () json/text retrieval * Optional block for work_generate () * Test for "work_generate" with block * Apply Guilherme review * Difficulty from previous block function for "block_create" & "work_generate"
This commit is contained in:
parent
53cf1be9ed
commit
04135d0aff
6 changed files with 275 additions and 93 deletions
|
@ -163,6 +163,12 @@ std::string nano::error_rpc_messages::message (int ev) const
|
|||
return "Representative account and previous hash required";
|
||||
case nano::error_rpc::block_create_requirements_send:
|
||||
return "Destination account, previous hash, current balance and amount required";
|
||||
case nano::error_rpc::block_root_mismatch:
|
||||
return "Root mismatch for block";
|
||||
case nano::error_rpc::block_work_enough:
|
||||
return "Provided work is already enough for given difficulty";
|
||||
case nano::error_rpc::block_work_version_mismatch:
|
||||
return "Work version mismatch for block";
|
||||
case nano::error_rpc::confirmation_height_not_processing:
|
||||
return "There are no blocks currently being processed for adding confirmation height";
|
||||
case nano::error_rpc::confirmation_not_found:
|
||||
|
|
|
@ -94,6 +94,9 @@ enum class error_rpc
|
|||
block_create_requirements_receive,
|
||||
block_create_requirements_change,
|
||||
block_create_requirements_send,
|
||||
block_root_mismatch,
|
||||
block_work_enough,
|
||||
block_work_version_mismatch,
|
||||
confirmation_height_not_processing,
|
||||
confirmation_not_found,
|
||||
difficulty_limit,
|
||||
|
|
|
@ -268,19 +268,27 @@ nano::amount nano::json_handler::amount_impl ()
|
|||
|
||||
std::shared_ptr<nano::block> nano::json_handler::block_impl (bool signature_work_required)
|
||||
{
|
||||
const bool json_block_l = request.get<bool> ("json_block", false);
|
||||
std::shared_ptr<nano::block> result{ nullptr };
|
||||
if (!ec)
|
||||
{
|
||||
std::string block_text (request.get<std::string> ("block"));
|
||||
boost::property_tree::ptree block_l;
|
||||
std::stringstream block_stream (block_text);
|
||||
try
|
||||
if (json_block_l)
|
||||
{
|
||||
boost::property_tree::read_json (block_stream, block_l);
|
||||
block_l = request.get_child ("block");
|
||||
}
|
||||
catch (...)
|
||||
else
|
||||
{
|
||||
ec = nano::error_blocks::invalid_block;
|
||||
std::string block_text (request.get<std::string> ("block"));
|
||||
std::stringstream block_stream (block_text);
|
||||
try
|
||||
{
|
||||
boost::property_tree::read_json (block_stream, block_l);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
ec = nano::error_blocks::invalid_block;
|
||||
}
|
||||
}
|
||||
if (!ec)
|
||||
{
|
||||
|
@ -299,26 +307,6 @@ std::shared_ptr<nano::block> nano::json_handler::block_impl (bool signature_work
|
|||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<nano::block> nano::json_handler::block_json_impl (bool signature_work_required)
|
||||
{
|
||||
std::shared_ptr<nano::block> result;
|
||||
if (!ec)
|
||||
{
|
||||
auto block_l (request.get_child ("block"));
|
||||
if (!signature_work_required)
|
||||
{
|
||||
block_l.put ("signature", "0");
|
||||
block_l.put ("work", "0");
|
||||
}
|
||||
result = nano::deserialize_block_json (block_l);
|
||||
if (result == nullptr)
|
||||
{
|
||||
ec = nano::error_blocks::invalid_block;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
nano::block_hash nano::json_handler::hash_impl (std::string search_text)
|
||||
{
|
||||
nano::block_hash result (0);
|
||||
|
@ -375,6 +363,43 @@ uint64_t nano::json_handler::difficulty_optional_impl (nano::work_version const
|
|||
return difficulty;
|
||||
}
|
||||
|
||||
uint64_t nano::json_handler::difficulty_ledger (nano::block const & block_a)
|
||||
{
|
||||
nano::block_details details (nano::epoch::epoch_0, false, false, false);
|
||||
bool details_found (false);
|
||||
auto transaction (node.store.tx_begin_read ());
|
||||
// Previous block find
|
||||
std::shared_ptr<nano::block> block_previous (nullptr);
|
||||
auto previous (block_a.previous ());
|
||||
if (!previous.is_zero ())
|
||||
{
|
||||
block_previous = node.store.block_get (transaction, previous);
|
||||
}
|
||||
// Send check
|
||||
if (block_previous != nullptr)
|
||||
{
|
||||
details.is_send = node.store.block_balance (transaction, previous) > block_a.balance ().number ();
|
||||
details_found = true;
|
||||
}
|
||||
// Epoch check
|
||||
if (block_previous != nullptr)
|
||||
{
|
||||
details.epoch = block_previous->sideband ().details.epoch;
|
||||
}
|
||||
auto link (block_a.link ());
|
||||
if (!link.is_zero () && !details.is_send)
|
||||
{
|
||||
auto block_link (node.store.block_get (transaction, link));
|
||||
if (block_link != nullptr && node.store.pending_exists (transaction, nano::pending_key (block_a.account (), link)))
|
||||
{
|
||||
details.epoch = std::max (details.epoch, block_link->sideband ().details.epoch);
|
||||
details.is_receive = true;
|
||||
details_found = true;
|
||||
}
|
||||
}
|
||||
return details_found ? nano::work_threshold (block_a.work_version (), details) : node.default_difficulty (block_a.work_version ());
|
||||
}
|
||||
|
||||
double nano::json_handler::multiplier_optional_impl (nano::work_version const version_a, uint64_t & difficulty)
|
||||
{
|
||||
double multiplier (1.);
|
||||
|
@ -1555,37 +1580,7 @@ void nano::json_handler::block_create ()
|
|||
// Difficulty calculation
|
||||
if (request.count ("difficulty") == 0)
|
||||
{
|
||||
nano::block_details details (nano::epoch::epoch_0, false, false, false);
|
||||
bool details_found (false);
|
||||
auto transaction (node.store.tx_begin_read ());
|
||||
// Previous block find
|
||||
std::shared_ptr<nano::block> block_previous (nullptr);
|
||||
if (!previous.is_zero ())
|
||||
{
|
||||
block_previous = node.store.block_get (transaction, previous);
|
||||
}
|
||||
// Send check
|
||||
if (block_previous != nullptr)
|
||||
{
|
||||
details.is_send = node.store.block_balance (transaction, previous) > balance.number ();
|
||||
details_found = true;
|
||||
}
|
||||
// Epoch check
|
||||
if (block_previous != nullptr)
|
||||
{
|
||||
details.epoch = block_previous->sideband ().details.epoch;
|
||||
}
|
||||
if (!link.is_zero () && !details.is_send)
|
||||
{
|
||||
auto block_link (node.store.block_get (transaction, link));
|
||||
if (block_link != nullptr && node.store.pending_exists (transaction, nano::pending_key (pub, link)))
|
||||
{
|
||||
details.epoch = std::max (details.epoch, block_link->sideband ().details.epoch);
|
||||
details.is_receive = true;
|
||||
details_found = true;
|
||||
}
|
||||
}
|
||||
difficulty_l = details_found ? nano::work_threshold (work_version, details) : node.default_difficulty (work_version);
|
||||
difficulty_l = difficulty_ledger (*block_l);
|
||||
}
|
||||
node.work_generate (work_version, root_l, difficulty_l, get_callback_l (block_l), nano::account (pub));
|
||||
}
|
||||
|
@ -1610,16 +1605,7 @@ void nano::json_handler::block_create ()
|
|||
|
||||
void nano::json_handler::block_hash ()
|
||||
{
|
||||
const bool json_block_l = request.get<bool> ("json_block", false);
|
||||
std::shared_ptr<nano::block> block;
|
||||
if (json_block_l)
|
||||
{
|
||||
block = block_json_impl (true);
|
||||
}
|
||||
else
|
||||
{
|
||||
block = block_impl (true);
|
||||
}
|
||||
auto block (block_impl (true));
|
||||
|
||||
if (!ec)
|
||||
{
|
||||
|
@ -3014,17 +3000,8 @@ void nano::json_handler::payment_wait ()
|
|||
void nano::json_handler::process ()
|
||||
{
|
||||
node.worker.push_task (create_worker_task ([](std::shared_ptr<nano::json_handler> const & rpc_l) {
|
||||
const bool json_block_l = rpc_l->request.get<bool> ("json_block", false);
|
||||
const bool watch_work_l = rpc_l->request.get<bool> ("watch_work", true);
|
||||
std::shared_ptr<nano::block> block;
|
||||
if (json_block_l)
|
||||
{
|
||||
block = rpc_l->block_json_impl (true);
|
||||
}
|
||||
else
|
||||
{
|
||||
block = rpc_l->block_impl (true);
|
||||
}
|
||||
auto block (rpc_l->block_impl (true));
|
||||
|
||||
// State blocks subtype check
|
||||
if (!rpc_l->ec && block->type () == nano::block_type::state)
|
||||
|
@ -3617,17 +3594,9 @@ void nano::json_handler::sign ()
|
|||
}
|
||||
// Retrieving block
|
||||
std::shared_ptr<nano::block> block;
|
||||
boost::optional<std::string> block_text (request.get_optional<std::string> ("block"));
|
||||
if (!ec && block_text.is_initialized ())
|
||||
if (!ec && request.count ("block"))
|
||||
{
|
||||
if (json_block_l)
|
||||
{
|
||||
block = block_json_impl (true);
|
||||
}
|
||||
else
|
||||
{
|
||||
block = block_impl (true);
|
||||
}
|
||||
block = block_impl (true);
|
||||
if (block != nullptr)
|
||||
{
|
||||
hash = block->hash ();
|
||||
|
@ -4785,7 +4754,38 @@ void nano::json_handler::work_generate ()
|
|||
{
|
||||
ec = nano::error_rpc::difficulty_limit;
|
||||
}
|
||||
if (!ec)
|
||||
// Retrieving optional block
|
||||
std::shared_ptr<nano::block> block;
|
||||
if (!ec && request.count ("block"))
|
||||
{
|
||||
block = block_impl (true);
|
||||
if (block != nullptr)
|
||||
{
|
||||
if (hash != block->root ())
|
||||
{
|
||||
ec = nano::error_rpc::block_root_mismatch;
|
||||
}
|
||||
if (request.count ("version") == 0)
|
||||
{
|
||||
work_version = block->work_version ();
|
||||
}
|
||||
else if (!ec && work_version != block->work_version ())
|
||||
{
|
||||
ec = nano::error_rpc::block_work_version_mismatch;
|
||||
}
|
||||
// Difficulty calculation
|
||||
if (!ec && request.count ("difficulty") == 0 && request.count ("multiplier") == 0)
|
||||
{
|
||||
difficulty = difficulty_ledger (*block);
|
||||
}
|
||||
// If optional block difficulty is higher than requested difficulty, send error
|
||||
if (!ec && block->difficulty () >= difficulty)
|
||||
{
|
||||
ec = nano::error_rpc::block_work_enough;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ec && response_l.empty ())
|
||||
{
|
||||
auto use_peers (request.get<bool> ("use_peers", false));
|
||||
auto rpc_l (shared_from_this ());
|
||||
|
|
|
@ -154,7 +154,6 @@ public:
|
|||
nano::account_info account_info_impl (nano::transaction const &, nano::account const &);
|
||||
nano::amount amount_impl ();
|
||||
std::shared_ptr<nano::block> block_impl (bool = true);
|
||||
std::shared_ptr<nano::block> block_json_impl (bool = true);
|
||||
nano::block_hash hash_impl (std::string = "hash");
|
||||
nano::amount threshold_optional_impl ();
|
||||
uint64_t work_optional_impl ();
|
||||
|
@ -162,6 +161,7 @@ public:
|
|||
uint64_t count_optional_impl (uint64_t = std::numeric_limits<uint64_t>::max ());
|
||||
uint64_t offset_optional_impl (uint64_t = 0);
|
||||
uint64_t difficulty_optional_impl (nano::work_version const);
|
||||
uint64_t difficulty_ledger (nano::block const &);
|
||||
double multiplier_optional_impl (nano::work_version const, uint64_t &);
|
||||
nano::work_version work_version_optional_impl (nano::work_version const default_a);
|
||||
bool enable_sign_hash{ false };
|
||||
|
|
|
@ -164,6 +164,7 @@ nano::account nano::system::account (nano::transaction const & transaction_a, si
|
|||
|
||||
uint64_t nano::system::work_generate_limited (nano::block_hash const & root_a, uint64_t min_a, uint64_t max_a)
|
||||
{
|
||||
debug_assert (min_a > 0);
|
||||
uint64_t result = 0;
|
||||
do
|
||||
{
|
||||
|
|
|
@ -3122,10 +3122,6 @@ TEST (rpc, work_generate_multiplier)
|
|||
}
|
||||
ASSERT_EQ (200, response.status);
|
||||
auto work_text (response.json.get_optional<std::string> ("work"));
|
||||
if (!work_text)
|
||||
{
|
||||
std::cout << response.json.get<std::string> ("error") << std::endl;
|
||||
}
|
||||
ASSERT_TRUE (work_text.is_initialized ());
|
||||
uint64_t work;
|
||||
ASSERT_FALSE (nano::from_string_hex (*work_text, work));
|
||||
|
@ -3225,6 +3221,182 @@ TEST (rpc, work_generate_epoch_2)
|
|||
}
|
||||
}
|
||||
|
||||
TEST (rpc, work_generate_block_high)
|
||||
{
|
||||
nano::system system;
|
||||
auto node = add_ipc_enabled_node (system);
|
||||
scoped_io_thread_name_change scoped_thread_name_io;
|
||||
nano::node_rpc_config node_rpc_config;
|
||||
nano::ipc::ipc_server ipc_server (*node, node_rpc_config);
|
||||
nano::rpc_config rpc_config (nano::get_available_port (), true);
|
||||
rpc_config.rpc_process.ipc_port = node->config.ipc_config.transport_tcp.port;
|
||||
nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config);
|
||||
nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor);
|
||||
rpc.start ();
|
||||
nano::keypair key;
|
||||
nano::state_block block (key.pub, 0, nano::test_genesis_key.pub, nano::Gxrb_ratio, 123, key.prv, key.pub, *node->work_generate_blocking (key.pub));
|
||||
nano::block_hash hash (block.root ());
|
||||
auto block_difficulty (nano::work_difficulty (nano::work_version::work_1, hash, block.block_work ()));
|
||||
boost::property_tree::ptree request;
|
||||
request.put ("action", "work_generate");
|
||||
request.put ("hash", hash.to_string ());
|
||||
request.put ("json_block", "true");
|
||||
boost::property_tree::ptree json;
|
||||
block.serialize_json (json);
|
||||
request.add_child ("block", json);
|
||||
{
|
||||
test_response response (request, rpc.config.port, system.io_ctx);
|
||||
system.deadline_set (5s);
|
||||
while (response.status == 0)
|
||||
{
|
||||
ASSERT_NO_ERROR (system.poll ());
|
||||
}
|
||||
ASSERT_EQ (200, response.status);
|
||||
ASSERT_EQ (1, response.json.count ("error"));
|
||||
ASSERT_EQ (std::error_code (nano::error_rpc::block_work_enough).message (), response.json.get<std::string> ("error"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST (rpc, work_generate_block_low)
|
||||
{
|
||||
nano::system system;
|
||||
auto node = add_ipc_enabled_node (system);
|
||||
scoped_io_thread_name_change scoped_thread_name_io;
|
||||
nano::node_rpc_config node_rpc_config;
|
||||
nano::ipc::ipc_server ipc_server (*node, node_rpc_config);
|
||||
nano::rpc_config rpc_config (nano::get_available_port (), true);
|
||||
rpc_config.rpc_process.ipc_port = node->config.ipc_config.transport_tcp.port;
|
||||
nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config);
|
||||
nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor);
|
||||
rpc.start ();
|
||||
nano::keypair key;
|
||||
nano::state_block block (key.pub, 0, nano::test_genesis_key.pub, nano::Gxrb_ratio, 123, key.prv, key.pub, 0);
|
||||
auto threshold (node->default_difficulty (block.work_version ()));
|
||||
block.block_work_set (system.work_generate_limited (block.root (), threshold, nano::difficulty::from_multiplier (node->config.max_work_generate_multiplier / 10, threshold)));
|
||||
nano::block_hash hash (block.root ());
|
||||
auto block_difficulty (block.difficulty ());
|
||||
boost::property_tree::ptree request;
|
||||
request.put ("action", "work_generate");
|
||||
request.put ("hash", hash.to_string ());
|
||||
request.put ("difficulty", nano::to_string_hex (block_difficulty + 1));
|
||||
request.put ("json_block", "false");
|
||||
std::string json;
|
||||
block.serialize_json (json);
|
||||
request.put ("block", json);
|
||||
{
|
||||
test_response response (request, rpc.config.port, system.io_ctx);
|
||||
system.deadline_set (10s);
|
||||
while (response.status == 0)
|
||||
{
|
||||
ASSERT_NO_ERROR (system.poll ());
|
||||
}
|
||||
ASSERT_EQ (200, response.status);
|
||||
auto work_text (response.json.get_optional<std::string> ("work"));
|
||||
ASSERT_TRUE (work_text.is_initialized ());
|
||||
uint64_t work;
|
||||
ASSERT_FALSE (nano::from_string_hex (*work_text, work));
|
||||
ASSERT_NE (block.block_work (), work);
|
||||
auto result_difficulty (nano::work_difficulty (nano::work_version::work_1, hash, work));
|
||||
auto response_difficulty_text (response.json.get<std::string> ("difficulty"));
|
||||
uint64_t response_difficulty;
|
||||
ASSERT_FALSE (nano::from_string_hex (response_difficulty_text, response_difficulty));
|
||||
ASSERT_EQ (result_difficulty, response_difficulty);
|
||||
ASSERT_LT (block_difficulty, result_difficulty);
|
||||
}
|
||||
}
|
||||
|
||||
TEST (rpc, work_generate_block_root_mismatch)
|
||||
{
|
||||
nano::system system;
|
||||
auto node = add_ipc_enabled_node (system);
|
||||
scoped_io_thread_name_change scoped_thread_name_io;
|
||||
nano::node_rpc_config node_rpc_config;
|
||||
nano::ipc::ipc_server ipc_server (*node, node_rpc_config);
|
||||
nano::rpc_config rpc_config (nano::get_available_port (), true);
|
||||
rpc_config.rpc_process.ipc_port = node->config.ipc_config.transport_tcp.port;
|
||||
nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config);
|
||||
nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor);
|
||||
rpc.start ();
|
||||
nano::keypair key;
|
||||
nano::state_block block (key.pub, 0, nano::test_genesis_key.pub, nano::Gxrb_ratio, 123, key.prv, key.pub, *node->work_generate_blocking (key.pub));
|
||||
nano::block_hash hash (1);
|
||||
boost::property_tree::ptree request;
|
||||
request.put ("action", "work_generate");
|
||||
request.put ("hash", hash.to_string ());
|
||||
request.put ("json_block", "false");
|
||||
std::string json;
|
||||
block.serialize_json (json);
|
||||
request.put ("block", json);
|
||||
{
|
||||
test_response response (request, rpc.config.port, system.io_ctx);
|
||||
system.deadline_set (5s);
|
||||
while (response.status == 0)
|
||||
{
|
||||
ASSERT_NO_ERROR (system.poll ());
|
||||
}
|
||||
ASSERT_EQ (200, response.status);
|
||||
ASSERT_EQ (1, response.json.count ("error"));
|
||||
ASSERT_EQ (std::error_code (nano::error_rpc::block_root_mismatch).message (), response.json.get<std::string> ("error"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST (rpc, work_generate_block_ledger_epoch_2)
|
||||
{
|
||||
nano::system system;
|
||||
auto node = add_ipc_enabled_node (system);
|
||||
auto epoch1 = system.upgrade_genesis_epoch (*node, nano::epoch::epoch_1);
|
||||
ASSERT_NE (nullptr, epoch1);
|
||||
auto epoch2 = system.upgrade_genesis_epoch (*node, nano::epoch::epoch_2);
|
||||
ASSERT_NE (nullptr, epoch2);
|
||||
nano::keypair key;
|
||||
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
|
||||
auto send_block (system.wallet (0)->send_action (nano::test_genesis_key.pub, key.pub, nano::Gxrb_ratio));
|
||||
ASSERT_NE (nullptr, send_block);
|
||||
scoped_io_thread_name_change scoped_thread_name_io;
|
||||
nano::node_rpc_config node_rpc_config;
|
||||
nano::ipc::ipc_server ipc_server (*node, node_rpc_config);
|
||||
nano::rpc_config rpc_config (nano::get_available_port (), true);
|
||||
rpc_config.rpc_process.ipc_port = node->config.ipc_config.transport_tcp.port;
|
||||
nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config);
|
||||
nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor);
|
||||
rpc.start ();
|
||||
nano::state_block block (key.pub, 0, nano::test_genesis_key.pub, nano::Gxrb_ratio, send_block->hash (), key.prv, key.pub, 0);
|
||||
auto threshold (nano::work_threshold (block.work_version (), nano::block_details (nano::epoch::epoch_2, false, true, false)));
|
||||
block.block_work_set (system.work_generate_limited (block.root (), 1, threshold - 1));
|
||||
nano::block_hash hash (block.root ());
|
||||
boost::property_tree::ptree request;
|
||||
request.put ("action", "work_generate");
|
||||
request.put ("hash", hash.to_string ());
|
||||
request.put ("json_block", "false");
|
||||
std::string json;
|
||||
block.serialize_json (json);
|
||||
request.put ("block", json);
|
||||
bool finished (false);
|
||||
auto iteration (0);
|
||||
while (!finished)
|
||||
{
|
||||
test_response response (request, rpc.config.port, system.io_ctx);
|
||||
system.deadline_set (10s);
|
||||
while (response.status == 0)
|
||||
{
|
||||
ASSERT_NO_ERROR (system.poll ());
|
||||
}
|
||||
ASSERT_EQ (200, response.status);
|
||||
auto work_text (response.json.get_optional<std::string> ("work"));
|
||||
ASSERT_TRUE (work_text.is_initialized ());
|
||||
uint64_t work;
|
||||
ASSERT_FALSE (nano::from_string_hex (*work_text, work));
|
||||
auto result_difficulty (nano::work_difficulty (nano::work_version::work_1, hash, work));
|
||||
auto response_difficulty_text (response.json.get<std::string> ("difficulty"));
|
||||
uint64_t response_difficulty;
|
||||
ASSERT_FALSE (nano::from_string_hex (response_difficulty_text, response_difficulty));
|
||||
ASSERT_EQ (result_difficulty, response_difficulty);
|
||||
ASSERT_GE (result_difficulty, node->network_params.network.publish_thresholds.epoch_2_receive);
|
||||
finished = result_difficulty < node->network_params.network.publish_thresholds.epoch_1;
|
||||
ASSERT_LT (++iteration, 200);
|
||||
}
|
||||
}
|
||||
|
||||
TEST (rpc, work_cancel)
|
||||
{
|
||||
nano::system system;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue