Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New print_sn_state_changes command #727

Merged
merged 4 commits into from
Jul 15, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion src/daemon/command_parser_executor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,39 @@ bool t_command_parser_executor::print_checkpoints(const std::vector<std::string>
return m_executor.print_checkpoints(start_height, end_height, print_json);
}

bool t_command_parser_executor::print_sn_state_changes(const std::vector<std::string> &args)
{
uint64_t start_height;
uint64_t end_height = cryptonote::COMMAND_RPC_GET_SN_STATE_CHANGES::HEIGHT_SENTINEL_VALUE;

if (args.empty()) {
std::cout << "Missing first argument start_height" << std::endl;
return false;
}

std::forward_list<std::string> args_list(args.begin(), args.end());
if (!epee::string_tools::get_xtype_from_string(start_height, args_list.front()))
{
std::cout << "start_height should be a number" << std::endl;
return false;
}

args_list.pop_front();

if (!parse_if_present(args_list, end_height, "end height"))
return false;

if (!args_list.empty())
{
std::cout << "use: print_sn_state_changes start height [end height]"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here as well

It'd be nice to lookup the usage from the command_handler, but out of scope. Gonna add it to the todo list.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed.

<< "(omit arguments to scan until the current block)"
<< std::endl;
return false;
}

return m_executor.print_sn_state_changes(start_height, end_height);
}

bool t_command_parser_executor::print_peer_list(const std::vector<std::string>& args)
{
if (args.size() > 3)
Expand Down Expand Up @@ -301,7 +334,7 @@ bool t_command_parser_executor::set_log_level(const std::vector<std::string>& ar
}
}

bool t_command_parser_executor::print_height(const std::vector<std::string>& args)
bool t_command_parser_executor::print_height(const std::vector<std::string>& args)
{
if (!args.empty()) return false;

Expand Down
2 changes: 2 additions & 0 deletions src/daemon/command_parser_executor.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ class t_command_parser_executor final
bool check_blockchain_pruning(const std::vector<std::string>& args);

bool print_net_stats(const std::vector<std::string>& args);

bool print_sn_state_changes(const std::vector<std::string> &args);
};

} // namespace daemonize
7 changes: 6 additions & 1 deletion src/daemon/command_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,12 @@ t_command_server::t_command_server(
, "print_checkpoints [+json] [start height] [end height]"
, "Query the available checkpoints between the range, omit arguments to print the last 60 checkpoints"
);

m_command_lookup.set_handler(
"print_sn_state_changes"
, std::bind(&t_command_parser_executor::print_sn_state_changes, &m_parser, p::_1)
, "print_sn_state_changes start height [end height]"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

print_sn_state_changes <start height> [end height]

start height is mandatory so <> notation

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed.

, "Query the state changes between the range, omit the last argument to scan until the current block"
);
#if defined(LOKI_ENABLE_INTEGRATION_TEST_HOOKS)
m_command_lookup.set_handler(
"relay_votes_and_uptime", std::bind([rpc_server](std::vector<std::string> const &args) {
Expand Down
36 changes: 36 additions & 0 deletions src/daemon/rpc_command_executor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,42 @@ bool t_rpc_command_executor::print_checkpoints(uint64_t start_height, uint64_t e
return true;
}

bool t_rpc_command_executor::print_sn_state_changes(uint64_t start_height, uint64_t end_height)
{
cryptonote::COMMAND_RPC_GET_SN_STATE_CHANGES::request req;
cryptonote::COMMAND_RPC_GET_SN_STATE_CHANGES::response res;
epee::json_rpc::error error_resp;

req.start_height = start_height;
req.end_height = end_height;

if (m_is_rpc)
{
if (!m_rpc_client->json_rpc_request(req, res, "get_sn_state_changes", "Failed to query blockchain checkpoints"))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_service_node_state_changes so it matches the declaration in core_rpc_server.h and need to update the error message there.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

return false;
}
else
{
if (!m_rpc_server->on_get_service_nodes_state_changes(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK)
{
tools::fail_msg_writer() << "Failed to query sn state changes";
return false;
}
}

std::stringstream output;

output << "Service Node state Changes (blocks " << res.start_height << "-" << res.end_height << ")" << std::endl;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Service Node State Changes

End_height here should be -1 if (res.end_height == sentinel) which by default it gets set to the chain height, so the height is the block you are mining for and not a block that already exists in the blockchain.

Otherwise if it's set, you are grabbing the state changes inclusive of the blocks specified so you don't need the -1.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed logic to set the end_height to current_height - 1 when the sentinel is set.

output << " Recommissions:\t\t" << res.total_recommission << std::endl;
output << " Unlocks:\t\t" << res.total_unlock << std::endl;
output << " Decommissions:\t\t" << res.total_decommission << std::endl;
output << " Deregistrations:\t" << res.total_deregister << std::endl;
output << " IP change penalties:\t" << res.total_ip_change_penalty << std::endl;

tools::success_msg_writer() << output.str();
return true;
}

bool t_rpc_command_executor::print_peer_list(bool white, bool gray, size_t limit) {
cryptonote::COMMAND_RPC_GET_PEER_LIST::request req;
cryptonote::COMMAND_RPC_GET_PEER_LIST::response res;
Expand Down
2 changes: 2 additions & 0 deletions src/daemon/rpc_command_executor.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ class t_rpc_command_executor final {

bool print_checkpoints(uint64_t start_height, uint64_t end_height, bool print_json);

bool print_sn_state_changes(uint64_t start_height, uint64_t end_height);

bool print_peer_list(bool white = true, bool gray = true, size_t limit = 0);

bool print_peer_list_stats();
Expand Down
84 changes: 84 additions & 0 deletions src/rpc/core_rpc_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2966,5 +2966,89 @@ namespace cryptonote
res.checkpoints = db.get_checkpoints_range(req.start_height, req.end_height);
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_service_nodes_state_changes(const COMMAND_RPC_GET_SN_STATE_CHANGES::request& req, COMMAND_RPC_GET_SN_STATE_CHANGES::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
using blob_t = cryptonote::blobdata;
using block_pair_t = std::pair<blob_t, block>;
std::vector<block_pair_t> blocks;
std::vector<blob_t> txs;

const auto& db = m_core.get_blockchain_storage();
const uint64_t current_height = db.get_current_blockchain_height();

uint64_t end_height;
if (req.end_height == COMMAND_RPC_GET_SN_STATE_CHANGES::HEIGHT_SENTINEL_VALUE) {
end_height = current_height;
} else {
end_height = req.end_height;
}

if (!db.get_blocks(req.start_height, end_height - req.start_height, blocks, txs)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should handle the case where req.start_height > end_height

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.
Also added a + 1 to ensure count is set to 1 when start == end, i.e. when we want to get events for a specific block...

MERROR("Could not query block at requested height: " << req.start_height);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of MERROR, fill out the error_resp argument so that it gets returned in the json response.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

return false;
}

res.start_height = req.start_height;
res.end_height = end_height;
res.total_deregister = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually don't need to initialise to 0 since the typedef epee::struct_init thingy sets default initialises things it can to 0.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to know, thanks! Changed.

res.total_decommission = 0;
res.total_ip_change_penalty = 0;
res.total_recommission = 0;
res.total_unlock = 0;

for (size_t i = 0; i < txs.size(); ++i)
{
const auto& blob = txs[i];
cryptonote::transaction tx;
if (!cryptonote::parse_and_validate_tx_from_blob(blob, tx))
{
MERROR("tx could not be validated from blob, possibly corrupt blockchain");
continue;
}
if (tx.type == cryptonote::txtype::state_change)
{
const uint8_t hard_fork_version = blocks[i].second.major_version;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably what's causing the get_service_node_state_change to fail a lot. The txs and blocks array aren't 1:1. The txs returned from get_blocks looks to be all the transactions spanning across all the blocks requested. So you could call get_transaction_blobs yourself per block so you use the right version for each set of transactions

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah that probably explains a lot :)
Thanks!

cryptonote::tx_extra_service_node_state_change state_change;
if (!cryptonote::get_service_node_state_change_from_tx_extra(tx.extra, state_change, hard_fork_version))
{
// TODO: This seem to be triggered quite often with hf 11 blocks
// LOG_ERROR("Could not get state change from tx, possibly corrupt tx, hf_version "<< std::to_string(hard_fork_version));
continue;
}

switch(state_change.state) {
case service_nodes::new_state::deregister:
res.total_deregister++;
break;

case service_nodes::new_state::decommission:
res.total_decommission++;
break;

case service_nodes::new_state::recommission:
res.total_recommission++;
break;

case service_nodes::new_state::ip_change_penalty:
res.total_ip_change_penalty++;
break;

default:
MERROR("Unhandled state in on_get_service_nodes_state_changes");
break;
}
}

if (tx.type == cryptonote::txtype::key_image_unlock)
{
res.total_unlock++;
}
}

res.status = CORE_RPC_STATUS_OK;
return true;
}


} // namespace cryptonote
2 changes: 2 additions & 0 deletions src/rpc/core_rpc_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ namespace cryptonote
MAP_JON_RPC_WE("get_checkpoints", on_get_checkpoints, COMMAND_RPC_GET_CHECKPOINTS)
MAP_JON_RPC_WE_IF("perform_blockchain_test", on_perform_blockchain_test, COMMAND_RPC_PERFORM_BLOCKCHAIN_TEST, !m_restricted)
MAP_JON_RPC_WE_IF("storage_server_ping", on_storage_server_ping, COMMAND_RPC_STORAGE_SERVER_PING, !m_restricted)
MAP_JON_RPC_WE("get_service_nodes_state_changes", on_get_service_nodes_state_changes, COMMAND_RPC_GET_SN_STATE_CHANGES)
END_JSON_RPC_MAP()
END_URI_MAP2()

Expand Down Expand Up @@ -277,6 +278,7 @@ namespace cryptonote
bool on_perform_blockchain_test(const COMMAND_RPC_PERFORM_BLOCKCHAIN_TEST::request& req, COMMAND_RPC_PERFORM_BLOCKCHAIN_TEST::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_storage_server_ping(const COMMAND_RPC_STORAGE_SERVER_PING::request& req, COMMAND_RPC_STORAGE_SERVER_PING::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_checkpoints(const COMMAND_RPC_GET_CHECKPOINTS::request& req, COMMAND_RPC_GET_CHECKPOINTS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_service_nodes_state_changes(const COMMAND_RPC_GET_SN_STATE_CHANGES::request& req, COMMAND_RPC_GET_SN_STATE_CHANGES::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
//-----------------------

#if defined(LOKI_ENABLE_INTEGRATION_TEST_HOOKS)
Expand Down
45 changes: 45 additions & 0 deletions src/rpc/core_rpc_server_commands_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -3132,4 +3132,49 @@ namespace cryptonote
};
typedef epee::misc_utils::struct_init<response_t> response;
};

LOKI_RPC_DOC_INTROSPECT
// Query hardcoded/service node checkpoints stored for the blockchain. Omit all arguments to retrieve the latest "count" checkpoints.
struct COMMAND_RPC_GET_SN_STATE_CHANGES
{
constexpr static uint32_t NUM_BLOCKS_TO_SCAN_BY_DEFAULT = 720;
constexpr static uint64_t HEIGHT_SENTINEL_VALUE = (UINT64_MAX - 1);
struct request_t
{
uint64_t start_height;
uint64_t end_height; // Optional: If omitted, the tally runs until the current block

BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(start_height)
KV_SERIALIZE_OPT(end_height, HEIGHT_SENTINEL_VALUE)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;

struct response_t
{
std::string status; // Generic RPC error code. "OK" is the success value.
bool untrusted; // If the result is obtained using bootstrap mode, and therefore not trusted `true`, or otherwise `false`.

uint32_t total_deregister;
uint32_t total_ip_change_penalty;
uint32_t total_decommission;
uint32_t total_recommission;
uint32_t total_unlock;
uint64_t start_height;
uint64_t end_height;

BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
KV_SERIALIZE(untrusted)
KV_SERIALIZE(total_deregister)
KV_SERIALIZE(total_ip_change_penalty)
KV_SERIALIZE(total_decommission)
KV_SERIALIZE(total_recommission)
KV_SERIALIZE(start_height)
KV_SERIALIZE(end_height)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
}