diff --git a/src/ipc/daemon_ipc_handlers.cpp b/src/ipc/daemon_ipc_handlers.cpp index 2e83d7601..563d0621f 100644 --- a/src/ipc/daemon_ipc_handlers.cpp +++ b/src/ipc/daemon_ipc_handlers.cpp @@ -57,6 +57,25 @@ namespace } return check_core_busy(); } + + //------------------------------------------------------------------------------------------------------------------------------ + // equivalent of strstr, but with arbitrary bytes (ie, NULs) + // This does not differentiate between "not found" and "found at offset 0" + // (taken straight from core_rpc_server.cpp) + uint64_t slow_memmem(const void *start_buff, size_t buflen, const void *pat, size_t patlen) + { + const void *buf = start_buff; + const void *end = (const char*)buf + buflen; + if (patlen > buflen || patlen == 0) return 0; + while (buflen > 0 && (buf = memchr(buf, ((const char*)pat)[0], buflen-patlen + 1))) + { + if (memcmp(buf,pat,patlen) == 0) + return (const char*)buf - (const char*)start_buff; + buf = (const char*)buf + 1; + buflen = (const char*)end - (const char*)buf; + } + return 0; + } } namespace IPC @@ -483,5 +502,89 @@ namespace IPC p2p->set_save_graph(false); wap_proto_set_status(message, STATUS_OK); } + + void get_block_hash(wap_proto_t *message) { + if (!check_core_busy()) + { + wap_proto_set_status(message, STATUS_CORE_BUSY); + return; + } + uint64_t height = wap_proto_height(message); + if (core->get_current_blockchain_height() <= height) + { + wap_proto_set_status(message, STATUS_HEIGHT_TOO_BIG); + return; + } + std::string hash = string_tools::pod_to_hex(core->get_block_id_by_height(height)); + zchunk_t *hash_chunk = zchunk_new((void*)(hash.c_str()), hash.length()); + wap_proto_set_hash(message, &hash_chunk); + wap_proto_set_status(message, STATUS_OK); + } + + void get_block_template(wap_proto_t *message) { + if (!check_core_ready()) + { + wap_proto_set_status(message, STATUS_CORE_BUSY); + return; + } + + uint64_t reserve_size = wap_proto_reserve_size(message); + if (reserve_size > 255) + { + wap_proto_set_status(message, STATUS_RESERVE_SIZE_TOO_BIG); + return; + } + + cryptonote::account_public_address acc = AUTO_VAL_INIT(acc); + + zchunk_t *address_chunk = wap_proto_address(message); + std::string address((char*)zchunk_data(address_chunk), zchunk_size(address_chunk)); + if (!address.size() || !cryptonote::get_account_address_from_str(acc, testnet, address)) + { + wap_proto_set_status(message, STATUS_WRONG_ADDRESS); + return; + } + + cryptonote::block b = AUTO_VAL_INIT(b); + cryptonote::blobdata blob_reserve; + blob_reserve.resize(reserve_size, 0); + uint64_t difficulty = wap_proto_difficulty(message); + uint64_t height; + if (!core->get_block_template(b, acc, difficulty, height, blob_reserve)) + { + wap_proto_set_status(message, STATUS_INTERNAL_ERROR); + return; + } + cryptonote::blobdata block_blob = t_serializable_object_to_blob(b); + crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx); + if (tx_pub_key == cryptonote::null_pkey) + { + wap_proto_set_status(message, STATUS_INTERNAL_ERROR); + return; + } + uint64_t reserved_offset = slow_memmem((void*)block_blob.data(), block_blob.size(), &tx_pub_key, sizeof(tx_pub_key)); + if (!reserved_offset) + { + wap_proto_set_status(message, STATUS_INTERNAL_ERROR); + return; + } + reserved_offset += sizeof(tx_pub_key) + 3; // 3 bytes: tag for TX_EXTRA_TAG_PUBKEY(1 byte), tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte) + if (reserved_offset + reserve_size > block_blob.size()) + { + wap_proto_set_status(message, STATUS_INTERNAL_ERROR); + return; + } + wap_proto_set_height(message, height); + wap_proto_set_difficulty(message, difficulty); + wap_proto_set_reserved_offset(message, reserved_offset); + std::string prev_hash = string_tools::pod_to_hex(b.prev_id); + zchunk_t *prev_hash_chunk = zchunk_new((void*)prev_hash.c_str(), prev_hash.length()); + wap_proto_set_prev_hash(message, &prev_hash_chunk); + + cryptonote::blobdata blocktemplate_blob = string_tools::buff_to_hex_nodelimer(block_blob); + zchunk_t *blob_chunk = zchunk_new((void*)blocktemplate_blob.c_str(), blocktemplate_blob.length()); + wap_proto_set_block_template_blob(message, &blob_chunk); + wap_proto_set_status(message, STATUS_OK); + } } } diff --git a/src/ipc/include/daemon_ipc_handlers.h b/src/ipc/include/daemon_ipc_handlers.h index 545712815..231a9bae5 100644 --- a/src/ipc/include/daemon_ipc_handlers.h +++ b/src/ipc/include/daemon_ipc_handlers.h @@ -66,8 +66,10 @@ namespace IPC const uint64_t STATUS_RANDOM_OUTS_FAILED = 9; const uint64_t STATUS_MINING_NOT_STOPPED = 10; const uint64_t STATUS_NOT_MINING = 11; - const uint64_t STATUS_INVALID_LOG_LEVEL = 11; - const uint64_t STATUS_ERROR_STORING_BLOCKCHAIN = 11; + const uint64_t STATUS_INVALID_LOG_LEVEL = 12; + const uint64_t STATUS_ERROR_STORING_BLOCKCHAIN = 13; + const uint64_t STATUS_HEIGHT_TOO_BIG = 13; + const uint64_t STATUS_RESERVE_SIZE_TOO_BIG = 14; namespace Daemon { void start_mining(wap_proto_t *message); @@ -85,6 +87,8 @@ namespace IPC void set_log_level(wap_proto_t *message); void start_save_graph(wap_proto_t *message); void stop_save_graph(wap_proto_t *message); + void get_block_hash(wap_proto_t *message); + void get_block_template(wap_proto_t *message); void init(cryptonote::core &p_core, nodetool::node_server > &p_p2p, bool p_testnet); diff --git a/src/ipc/include/wap_client.h b/src/ipc/include/wap_client.h index c39d6b08d..777528e26 100644 --- a/src/ipc/include/wap_client.h +++ b/src/ipc/include/wap_client.h @@ -147,6 +147,16 @@ WAP_EXPORT int WAP_EXPORT int wap_client_stop_save_graph (wap_client_t *self); +// Get block hash +// Returns >= 0 if successful, -1 if interrupted. +WAP_EXPORT int + wap_client_get_block_hash (wap_client_t *self, uint64_t height); + +// Get block template +// Returns >= 0 if successful, -1 if interrupted. +WAP_EXPORT int + wap_client_get_block_template (wap_client_t *self, uint64_t reserve_size, zchunk_t **address_p); + // Return last received status WAP_EXPORT int wap_client_status (wap_client_t *self); @@ -243,6 +253,22 @@ WAP_EXPORT uint64_t WAP_EXPORT zchunk_t * wap_client_address (wap_client_t *self); +// Return last received hash +WAP_EXPORT zchunk_t * + wap_client_hash (wap_client_t *self); + +// Return last received reserved_offset +WAP_EXPORT uint64_t + wap_client_reserved_offset (wap_client_t *self); + +// Return last received prev_hash +WAP_EXPORT zchunk_t * + wap_client_prev_hash (wap_client_t *self); + +// Return last received block_template_blob +WAP_EXPORT zchunk_t * + wap_client_block_template_blob (wap_client_t *self); + // Self test of this class WAP_EXPORT void wap_client_test (bool verbose); diff --git a/src/ipc/include/wap_client_engine.inc b/src/ipc/include/wap_client_engine.inc index 3ec540854..621c5d295 100644 --- a/src/ipc/include/wap_client_engine.inc +++ b/src/ipc/include/wap_client_engine.inc @@ -40,10 +40,12 @@ typedef enum { expect_set_log_level_ok_state = 17, expect_start_save_graph_ok_state = 18, expect_stop_save_graph_ok_state = 19, - expect_close_ok_state = 20, - defaults_state = 21, - have_error_state = 22, - reexpect_open_ok_state = 23 + expect_get_block_hash_ok_state = 20, + expect_get_block_template_ok_state = 21, + expect_close_ok_state = 22, + defaults_state = 23, + have_error_state = 24, + reexpect_open_ok_state = 25 } state_t; typedef enum { @@ -68,29 +70,33 @@ typedef enum { set_log_level_event = 18, start_save_graph_event = 19, stop_save_graph_event = 20, - destructor_event = 21, - blocks_ok_event = 22, - get_ok_event = 23, - put_ok_event = 24, - save_bc_ok_event = 25, - start_ok_event = 26, - stop_ok_event = 27, - output_indexes_ok_event = 28, - random_outs_ok_event = 29, - get_height_ok_event = 30, - get_info_ok_event = 31, - get_peer_list_ok_event = 32, - get_mining_status_ok_event = 33, - set_log_hash_rate_ok_event = 34, - set_log_level_ok_event = 35, - start_save_graph_ok_event = 36, - stop_save_graph_ok_event = 37, - close_ok_event = 38, - ping_ok_event = 39, - error_event = 40, - exception_event = 41, - command_invalid_event = 42, - other_event = 43 + get_block_hash_event = 21, + get_block_template_event = 22, + destructor_event = 23, + blocks_ok_event = 24, + get_ok_event = 25, + put_ok_event = 26, + save_bc_ok_event = 27, + start_ok_event = 28, + stop_ok_event = 29, + output_indexes_ok_event = 30, + random_outs_ok_event = 31, + get_height_ok_event = 32, + get_info_ok_event = 33, + get_peer_list_ok_event = 34, + get_mining_status_ok_event = 35, + set_log_hash_rate_ok_event = 36, + set_log_level_ok_event = 37, + start_save_graph_ok_event = 38, + stop_save_graph_ok_event = 39, + get_block_hash_ok_event = 40, + get_block_template_ok_event = 41, + close_ok_event = 42, + ping_ok_event = 43, + error_event = 44, + exception_event = 45, + command_invalid_event = 46, + other_event = 47 } event_t; // Names for state machine logging and error reporting @@ -116,6 +122,8 @@ s_state_name [] = { "expect set log level ok", "expect start save graph ok", "expect stop save graph ok", + "expect get block hash ok", + "expect get block template ok", "expect close ok", "defaults", "have error", @@ -145,6 +153,8 @@ s_event_name [] = { "SET_LOG_LEVEL", "START_SAVE_GRAPH", "STOP_SAVE_GRAPH", + "GET_BLOCK_HASH", + "GET_BLOCK_TEMPLATE", "destructor", "BLOCKS_OK", "GET_OK", @@ -162,6 +172,8 @@ s_event_name [] = { "SET_LOG_LEVEL_OK", "START_SAVE_GRAPH_OK", "STOP_SAVE_GRAPH_OK", + "GET_BLOCK_HASH_OK", + "GET_BLOCK_TEMPLATE_OK", "CLOSE_OK", "PING_OK", "ERROR", @@ -191,6 +203,8 @@ struct _client_args_t { uint64_t thread_count; uint8_t visible; uint8_t level; + uint64_t height; + uint64_t reserve_size; }; typedef struct { @@ -258,6 +272,10 @@ static void prepare_set_log_hash_rate_command (client_t *self); static void prepare_set_log_level_command (client_t *self); +static void + prepare_get_block_hash_command (client_t *self); +static void + prepare_get_block_template_command (client_t *self); static void check_if_connection_is_dead (client_t *self); static void @@ -292,6 +310,10 @@ static void signal_have_start_save_graph_ok (client_t *self); static void signal_have_stop_save_graph_ok (client_t *self); +static void + signal_have_get_block_hash_ok (client_t *self); +static void + signal_have_get_block_template_ok (client_t *self); static void signal_failure (client_t *self); static void @@ -577,6 +599,18 @@ s_protocol_event (s_client_t *self, wap_proto_t *message) case WAP_PROTO_STOP_SAVE_GRAPH_OK: return stop_save_graph_ok_event; break; + case WAP_PROTO_GET_BLOCK_HASH: + return get_block_hash_event; + break; + case WAP_PROTO_GET_BLOCK_HASH_OK: + return get_block_hash_ok_event; + break; + case WAP_PROTO_GET_BLOCK_TEMPLATE: + return get_block_template_event; + break; + case WAP_PROTO_GET_BLOCK_TEMPLATE_OK: + return get_block_template_ok_event; + break; case WAP_PROTO_STOP: return stop_event; break; @@ -983,6 +1017,42 @@ s_client_execute (s_client_t *self, event_t event) self->state = expect_stop_save_graph_ok_state; } else + if (self->event == get_block_hash_event) { + if (!self->exception) { + // prepare get block hash command + if (wap_client_verbose) + zsys_debug ("wap_client: $ prepare get block hash command"); + prepare_get_block_hash_command (&self->client); + } + if (!self->exception) { + // send GET_BLOCK_HASH + if (wap_client_verbose) + zsys_debug ("wap_client: $ send GET_BLOCK_HASH"); + wap_proto_set_id (self->message, WAP_PROTO_GET_BLOCK_HASH); + wap_proto_send (self->message, self->dealer); + } + if (!self->exception) + self->state = expect_get_block_hash_ok_state; + } + else + if (self->event == get_block_template_event) { + if (!self->exception) { + // prepare get block template command + if (wap_client_verbose) + zsys_debug ("wap_client: $ prepare get block template command"); + prepare_get_block_template_command (&self->client); + } + if (!self->exception) { + // send GET_BLOCK_TEMPLATE + if (wap_client_verbose) + zsys_debug ("wap_client: $ send GET_BLOCK_TEMPLATE"); + wap_proto_set_id (self->message, WAP_PROTO_GET_BLOCK_TEMPLATE); + wap_proto_send (self->message, self->dealer); + } + if (!self->exception) + self->state = expect_get_block_template_ok_state; + } + else if (self->event == destructor_event) { if (!self->exception) { // send CLOSE @@ -1764,6 +1834,96 @@ s_client_execute (s_client_t *self, event_t event) } break; + case expect_get_block_hash_ok_state: + if (self->event == get_block_hash_ok_event) { + if (!self->exception) { + // signal have get block hash ok + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal have get block hash ok"); + signal_have_get_block_hash_ok (&self->client); + } + if (!self->exception) + self->state = connected_state; + } + else + if (self->event == ping_ok_event) { + if (!self->exception) { + // client is connected + if (wap_client_verbose) + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); + } + } + else + if (self->event == error_event) { + if (!self->exception) { + // check status code + if (wap_client_verbose) + zsys_debug ("wap_client: $ check status code"); + check_status_code (&self->client); + } + if (!self->exception) + self->state = have_error_state; + } + else + if (self->event == exception_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ exception"); + } + else { + // Handle unexpected protocol events + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ *"); + } + break; + + case expect_get_block_template_ok_state: + if (self->event == get_block_template_ok_event) { + if (!self->exception) { + // signal have get block template ok + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal have get block template ok"); + signal_have_get_block_template_ok (&self->client); + } + if (!self->exception) + self->state = connected_state; + } + else + if (self->event == ping_ok_event) { + if (!self->exception) { + // client is connected + if (wap_client_verbose) + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); + } + } + else + if (self->event == error_event) { + if (!self->exception) { + // check status code + if (wap_client_verbose) + zsys_debug ("wap_client: $ check status code"); + check_status_code (&self->client); + } + if (!self->exception) + self->state = have_error_state; + } + else + if (self->event == exception_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ exception"); + } + else { + // Handle unexpected protocol events + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ *"); + } + break; + case expect_close_ok_state: if (self->event == close_ok_event) { if (!self->exception) { @@ -2093,6 +2253,17 @@ s_client_handle_cmdpipe (zloop_t *loop, zsock_t *reader, void *argument) if (streq (method, "STOP SAVE GRAPH")) { s_client_execute (self, stop_save_graph_event); } + else + if (streq (method, "GET BLOCK HASH")) { + zsock_recv (self->cmdpipe, "8", &self->args.height); + s_client_execute (self, get_block_hash_event); + } + else + if (streq (method, "GET BLOCK TEMPLATE")) { + zchunk_destroy (&self->args.address); + zsock_recv (self->cmdpipe, "8p", &self->args.reserve_size, &self->args.address); + s_client_execute (self, get_block_template_event); + } // Cleanup pipe if any argument frames are still waiting to be eaten if (zsock_rcvmore (self->cmdpipe)) { zsys_error ("wap_client: trailing API command frames (%s)", method); @@ -2229,6 +2400,10 @@ struct _wap_client_t { uint64_t speed; // Returned by actor reply uint64_t thread_count; // Returned by actor reply zchunk_t *address; // Returned by actor reply + zchunk_t *hash; // Returned by actor reply + uint64_t reserved_offset; // Returned by actor reply + zchunk_t *prev_hash; // Returned by actor reply + zchunk_t *block_template_blob; // Returned by actor reply }; @@ -2284,6 +2459,9 @@ wap_client_destroy (wap_client_t **self_p) zframe_destroy (&self->white_list); zframe_destroy (&self->gray_list); zchunk_destroy (&self->address); + zchunk_destroy (&self->hash); + zchunk_destroy (&self->prev_hash); + zchunk_destroy (&self->block_template_blob); free (self); *self_p = NULL; } @@ -2432,6 +2610,17 @@ s_accept_reply (wap_client_t *self, ...) if (streq (reply, "STOP SAVE GRAPH OK")) { zsock_recv (self->actor, "8", &self->status); } + else + if (streq (reply, "GET BLOCK HASH OK")) { + zchunk_destroy (&self->hash); + zsock_recv (self->actor, "8p", &self->status, &self->hash); + } + else + if (streq (reply, "GET BLOCK TEMPLATE OK")) { + zchunk_destroy (&self->prev_hash); + zchunk_destroy (&self->block_template_blob); + zsock_recv (self->actor, "8888pp", &self->status, &self->reserved_offset, &self->height, &self->difficulty, &self->prev_hash, &self->block_template_blob); + } break; } filter = va_arg (args, char *); @@ -2745,6 +2934,39 @@ wap_client_stop_save_graph (wap_client_t *self) } +// --------------------------------------------------------------------------- +// Get block hash +// Returns >= 0 if successful, -1 if interrupted. + +int +wap_client_get_block_hash (wap_client_t *self, uint64_t height) +{ + assert (self); + + zsock_send (self->actor, "s8", "GET BLOCK HASH", height); + if (s_accept_reply (self, "GET BLOCK HASH OK", "FAILURE", NULL)) + return -1; // Interrupted or timed-out + return self->status; +} + + +// --------------------------------------------------------------------------- +// Get block template +// Returns >= 0 if successful, -1 if interrupted. + +int +wap_client_get_block_template (wap_client_t *self, uint64_t reserve_size, zchunk_t **address_p) +{ + assert (self); + + zsock_send (self->actor, "s8p", "GET BLOCK TEMPLATE", reserve_size, *address_p); + *address_p = NULL; // Take ownership of address + if (s_accept_reply (self, "GET BLOCK TEMPLATE OK", "FAILURE", NULL)) + return -1; // Interrupted or timed-out + return self->status; +} + + // --------------------------------------------------------------------------- // Return last received status @@ -3007,3 +3229,47 @@ wap_client_address (wap_client_t *self) assert (self); return self->address; } + + +// --------------------------------------------------------------------------- +// Return last received hash + +zchunk_t * +wap_client_hash (wap_client_t *self) +{ + assert (self); + return self->hash; +} + + +// --------------------------------------------------------------------------- +// Return last received reserved_offset + +uint64_t +wap_client_reserved_offset (wap_client_t *self) +{ + assert (self); + return self->reserved_offset; +} + + +// --------------------------------------------------------------------------- +// Return last received prev_hash + +zchunk_t * +wap_client_prev_hash (wap_client_t *self) +{ + assert (self); + return self->prev_hash; +} + + +// --------------------------------------------------------------------------- +// Return last received block_template_blob + +zchunk_t * +wap_client_block_template_blob (wap_client_t *self) +{ + assert (self); + return self->block_template_blob; +} diff --git a/src/ipc/include/wap_proto.h b/src/ipc/include/wap_proto.h index 15b66f468..a3ede6cdc 100644 --- a/src/ipc/include/wap_proto.h +++ b/src/ipc/include/wap_proto.h @@ -142,6 +142,25 @@ ERROR. STOP_SAVE_GRAPH_OK - This is a codec for a Bitcoin Wallet Access Protocol (RFC tbd) status number 8 Status + GET_BLOCK_HASH - get_block_hash IPC + height number 8 Height + + GET_BLOCK_HASH_OK - This is a codec for a Bitcoin Wallet Access Protocol (RFC tbd) + status number 8 Status + hash chunk Hash + + GET_BLOCK_TEMPLATE - get_block_template IPC + reserve_size number 8 Reserve size + address chunk Address + + GET_BLOCK_TEMPLATE_OK - This is a codec for a Bitcoin Wallet Access Protocol (RFC tbd) + status number 8 Status + reserved_offset number 8 Rservered Offset + height number 8 Height + difficulty number 8 Difficulty + prev_hash chunk Previous Hash + block_template_blob chunk Block template blob + STOP - Wallet asks daemon to start mining. Daemon replies with STOP-OK, or ERROR. @@ -205,13 +224,17 @@ Daemon will reply with CLOSE-OK or ERROR. #define WAP_PROTO_START_SAVE_GRAPH_OK 30 #define WAP_PROTO_STOP_SAVE_GRAPH 31 #define WAP_PROTO_STOP_SAVE_GRAPH_OK 32 -#define WAP_PROTO_STOP 33 -#define WAP_PROTO_STOP_OK 34 -#define WAP_PROTO_CLOSE 35 -#define WAP_PROTO_CLOSE_OK 36 -#define WAP_PROTO_PING 37 -#define WAP_PROTO_PING_OK 38 -#define WAP_PROTO_ERROR 39 +#define WAP_PROTO_GET_BLOCK_HASH 33 +#define WAP_PROTO_GET_BLOCK_HASH_OK 34 +#define WAP_PROTO_GET_BLOCK_TEMPLATE 35 +#define WAP_PROTO_GET_BLOCK_TEMPLATE_OK 36 +#define WAP_PROTO_STOP 37 +#define WAP_PROTO_STOP_OK 38 +#define WAP_PROTO_CLOSE 39 +#define WAP_PROTO_CLOSE_OK 40 +#define WAP_PROTO_PING 41 +#define WAP_PROTO_PING_OK 42 +#define WAP_PROTO_ERROR 43 #include @@ -491,6 +514,48 @@ byte void wap_proto_set_level (wap_proto_t *self, byte level); +// Get a copy of the hash field +zchunk_t * + wap_proto_hash (wap_proto_t *self); +// Get the hash field and transfer ownership to caller +zchunk_t * + wap_proto_get_hash (wap_proto_t *self); +// Set the hash field, transferring ownership from caller +void + wap_proto_set_hash (wap_proto_t *self, zchunk_t **chunk_p); + +// Get/set the reserve_size field +uint64_t + wap_proto_reserve_size (wap_proto_t *self); +void + wap_proto_set_reserve_size (wap_proto_t *self, uint64_t reserve_size); + +// Get/set the reserved_offset field +uint64_t + wap_proto_reserved_offset (wap_proto_t *self); +void + wap_proto_set_reserved_offset (wap_proto_t *self, uint64_t reserved_offset); + +// Get a copy of the prev_hash field +zchunk_t * + wap_proto_prev_hash (wap_proto_t *self); +// Get the prev_hash field and transfer ownership to caller +zchunk_t * + wap_proto_get_prev_hash (wap_proto_t *self); +// Set the prev_hash field, transferring ownership from caller +void + wap_proto_set_prev_hash (wap_proto_t *self, zchunk_t **chunk_p); + +// Get a copy of the block_template_blob field +zchunk_t * + wap_proto_block_template_blob (wap_proto_t *self); +// Get the block_template_blob field and transfer ownership to caller +zchunk_t * + wap_proto_get_block_template_blob (wap_proto_t *self); +// Set the block_template_blob field, transferring ownership from caller +void + wap_proto_set_block_template_blob (wap_proto_t *self, zchunk_t **chunk_p); + // Get/set the reason field const char * wap_proto_reason (wap_proto_t *self); diff --git a/src/ipc/include/wap_server_engine.inc b/src/ipc/include/wap_server_engine.inc index eaa4f37ce..58c66c6ea 100644 --- a/src/ipc/include/wap_server_engine.inc +++ b/src/ipc/include/wap_server_engine.inc @@ -47,11 +47,13 @@ typedef enum { set_log_level_event = 16, start_save_graph_event = 17, stop_save_graph_event = 18, - close_event = 19, - ping_event = 20, - expired_event = 21, - exception_event = 22, - settled_event = 23 + get_block_hash_event = 19, + get_block_template_event = 20, + close_event = 21, + ping_event = 22, + expired_event = 23, + exception_event = 24, + settled_event = 25 } event_t; // Names for state machine logging and error reporting @@ -85,6 +87,8 @@ s_event_name [] = { "SET_LOG_LEVEL", "START_SAVE_GRAPH", "STOP_SAVE_GRAPH", + "GET_BLOCK_HASH", + "GET_BLOCK_TEMPLATE", "CLOSE", "PING", "expired", @@ -186,6 +190,10 @@ static void start_save_graph (client_t *self); static void stop_save_graph (client_t *self); +static void + get_block_hash (client_t *self); +static void + get_block_template (client_t *self); static void deregister_wallet (client_t *self); static void @@ -418,6 +426,12 @@ s_protocol_event (wap_proto_t *message) case WAP_PROTO_STOP_SAVE_GRAPH: return stop_save_graph_event; break; + case WAP_PROTO_GET_BLOCK_HASH: + return get_block_hash_event; + break; + case WAP_PROTO_GET_BLOCK_TEMPLATE: + return get_block_template_event; + break; case WAP_PROTO_STOP: return stop_event; break; @@ -924,6 +938,42 @@ s_client_execute (s_client_t *self, event_t event) } } else + if (self->event == get_block_hash_event) { + if (!self->exception) { + // get block hash + if (self->server->verbose) + zsys_debug ("%s: $ get block hash", self->log_prefix); + get_block_hash (&self->client); + } + if (!self->exception) { + // send GET_BLOCK_HASH_OK + if (self->server->verbose) + zsys_debug ("%s: $ send GET_BLOCK_HASH_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_GET_BLOCK_HASH_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + } + else + if (self->event == get_block_template_event) { + if (!self->exception) { + // get block template + if (self->server->verbose) + zsys_debug ("%s: $ get block template", self->log_prefix); + get_block_template (&self->client); + } + if (!self->exception) { + // send GET_BLOCK_TEMPLATE_OK + if (self->server->verbose) + zsys_debug ("%s: $ send GET_BLOCK_TEMPLATE_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_GET_BLOCK_TEMPLATE_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + } + else if (self->event == close_event) { if (!self->exception) { // send CLOSE_OK diff --git a/src/ipc/wap_client/wap_client.c b/src/ipc/wap_client/wap_client.c index 8973abe6e..40b577691 100644 --- a/src/ipc/wap_client/wap_client.c +++ b/src/ipc/wap_client/wap_client.c @@ -16,9 +16,6 @@ */ #include "wap_classes.h" -// TODO: Change these to match your project's needs -#include "../include/wap_proto.h" -#include "../include/wap_client.h" // Forward reference to method arguments structure typedef struct _client_args_t client_args_t; @@ -98,6 +95,7 @@ use_connect_timeout (client_t *self) engine_set_timeout (self, self->args->timeout); } + // --------------------------------------------------------------------------- // client_is_connected // @@ -110,6 +108,7 @@ client_is_connected (client_t *self) engine_set_timeout (self, self->heartbeat_timer); } + // --------------------------------------------------------------------------- // check_if_connection_is_dead // @@ -125,17 +124,6 @@ check_if_connection_is_dead (client_t *self) } } -// --------------------------------------------------------------------------- -// use_heartbeat_timer -// - -static void -use_heartbeat_timer (client_t *self) -{ - engine_set_timeout (self, self->heartbeat_timer); -} - - // --------------------------------------------------------------------------- // prepare_blocks_command @@ -148,15 +136,6 @@ prepare_blocks_command (client_t *self) wap_proto_set_start_height (self->message, self->args->start_height); } -// --------------------------------------------------------------------------- -// prepare_get_output_indexes_command -// - -static void -prepare_get_output_indexes_command (client_t *self) -{ - wap_proto_set_tx_id (self->message, &self->args->tx_id); -} // --------------------------------------------------------------------------- // signal_have_blocks_ok @@ -165,10 +144,13 @@ prepare_get_output_indexes_command (client_t *self) static void signal_have_blocks_ok (client_t *self) { + zmsg_t *msg = wap_proto_get_block_data (self->message); + assert(msg != 0); + printf("%p <--\n", (void*)msg); zsock_send (self->cmdpipe, "s888p", "BLOCKS OK", wap_proto_status(self->message), wap_proto_start_height (self->message), wap_proto_curr_height (self->message), - wap_proto_get_block_data (self->message)); + msg); } @@ -201,8 +183,8 @@ prepare_put_command (client_t *self) static void signal_have_put_ok (client_t *self) { - zsock_send (self->cmdpipe, "s8s", "PUT OK", wap_proto_status(self->message), - wap_proto_tx_id (self->message)); + zsock_send (self->cmdpipe, "s8", "PUT OK", + wap_proto_status (self->message)); } @@ -224,10 +206,21 @@ prepare_get_command (client_t *self) static void signal_have_get_ok (client_t *self) { - zsock_send (self->cmdpipe, "s8p", "GET OK", 0, + zsock_send (self->cmdpipe, "sip", "GET OK", 0, wap_proto_get_tx_data (self->message)); } +// --------------------------------------------------------------------------- +// signal_have_get_height_ok +// + +static void +signal_have_get_height_ok (client_t *self) +{ + zsock_send (self->cmdpipe, "si8", "GET HEIGHT OK", 0, + wap_proto_height (self->message)); +} + // --------------------------------------------------------------------------- // signal_have_save_ok @@ -258,31 +251,9 @@ signal_have_start_ok (client_t *self) static void signal_have_stop_ok (client_t *self) { - zsock_send (self->cmdpipe, "s8", "STOP OK", 0); + zsock_send (self->cmdpipe, "si", "STOP OK", 0); } -// --------------------------------------------------------------------------- -// signal_have_get_height_ok -// - -static void -signal_have_get_height_ok (client_t *self) -{ - zsock_send (self->cmdpipe, "si8", "GET HEIGHT OK", 0, - wap_proto_height (self->message)); -} - -// --------------------------------------------------------------------------- -// signal_have_output_indexes_ok -// - -static void -signal_have_output_indexes_ok (client_t *self) -{ - zsock_send (self->cmdpipe, "s8p", "OUTPUT INDEXES OK", - wap_proto_status (self->message), - wap_proto_get_o_indexes (self->message)); -} // --------------------------------------------------------------------------- // signal_success @@ -352,6 +323,37 @@ signal_server_not_present (client_t *self) zsock_send (self->cmdpipe, "sis", "FAILURE", -1, "Server is not reachable"); } + + + +// --------------------------------------------------------------------------- +// prepare_get_output_indexes_command +// + +static void +prepare_get_output_indexes_command (client_t *self) +{ + wap_proto_set_tx_id (self->message, &self->args->tx_id); +} + +// --------------------------------------------------------------------------- +// signal_have_output_indexes_ok +// + +static void +signal_have_output_indexes_ok (client_t *self) +{ + zsock_send (self->cmdpipe, "s8p", "OUTPUT INDEXES OK", + wap_proto_status (self->message), + wap_proto_get_o_indexes (self->message)); +} + +void wap_client_test(bool verbose) { +} + + + + // --------------------------------------------------------------------------- // prepare_get_random_outs_command // @@ -359,7 +361,6 @@ signal_server_not_present (client_t *self) static void prepare_get_random_outs_command (client_t *self) { - wap_proto_set_outs_count(self->message, self->args->outs_count); wap_proto_set_amounts (self->message, &self->args->amounts); } @@ -376,6 +377,8 @@ signal_have_random_outs_ok (client_t *self) wap_proto_get_random_outputs (self->message)); } + + // --------------------------------------------------------------------------- // signal_have_get_info_ok // @@ -397,8 +400,9 @@ signal_have_get_info_ok (client_t *self) wap_proto_grey_peerlist_size (self->message)); } + // --------------------------------------------------------------------------- -// signal_have_get_get_peer_list_ok +// signal_have_get_peer_list_ok // static void @@ -432,7 +436,7 @@ signal_have_get_mining_status_ok (client_t *self) static void prepare_set_log_hash_rate_command (client_t *self) { - wap_proto_set_visible (self->message, self->args->visible); + wap_proto_set_visible (self->message, self->args->visible); } // --------------------------------------------------------------------------- @@ -453,7 +457,7 @@ signal_have_set_log_hash_rate_ok (client_t *self) static void prepare_set_log_level_command (client_t *self) { - wap_proto_set_level (self->message, self->args->level); + wap_proto_set_level (self->message, self->args->level); } // --------------------------------------------------------------------------- @@ -488,3 +492,52 @@ signal_have_stop_save_graph_ok (client_t *self) zsock_send (self->cmdpipe, "s8", "STOP SAVE GRAPH OK", wap_proto_status (self->message)); } + +// --------------------------------------------------------------------------- +// prepare_get_block_hash_command +// + +static void +prepare_get_block_hash_command (client_t *self) +{ + wap_proto_set_height (self->message, self->args->height); +} + +// --------------------------------------------------------------------------- +// signal_have_get_block_hash_ok +// + +static void +signal_have_get_block_hash_ok (client_t *self) +{ + zsock_send (self->cmdpipe, "s8p", "GET BLOCK HASH OK", + wap_proto_status (self->message), wap_proto_get_hash (self->message)); +} + +// --------------------------------------------------------------------------- +// prepare_get_block_template_command +// + +static void +prepare_get_block_template_command (client_t *self) +{ + wap_proto_set_reserve_size (self->message, self->args->reserve_size); + wap_proto_set_address (self->message, &self->args->address); +} + +// --------------------------------------------------------------------------- +// signal_have_get_block_template_ok +// + +static void +signal_have_get_block_template_ok (client_t *self) +{ + zsock_send (self->cmdpipe, "s8888pp", "GET BLOCK TEMPLATE OK", + wap_proto_status (self->message), + wap_proto_reserved_offset (self->message), + wap_proto_height (self->message), + wap_proto_difficulty (self->message), + wap_proto_get_prev_hash (self->message), + wap_proto_get_block_template_blob (self->message)); +} + diff --git a/src/ipc/wap_proto.c b/src/ipc/wap_proto.c index 6a6f0af26..bc4522d7c 100644 --- a/src/ipc/wap_proto.c +++ b/src/ipc/wap_proto.c @@ -65,6 +65,11 @@ struct _wap_proto_t { uint64_t speed; // Speed byte visible; // Visible byte level; // Level + zchunk_t *hash; // Hash + uint64_t reserve_size; // Reserve size + uint64_t reserved_offset; // Rservered Offset + zchunk_t *prev_hash; // Previous Hash + zchunk_t *block_template_blob; // Block template blob char reason [256]; // Printable explanation }; @@ -253,6 +258,9 @@ wap_proto_destroy (wap_proto_t **self_p) zchunk_destroy (&self->address); zframe_destroy (&self->white_list); zframe_destroy (&self->gray_list); + zchunk_destroy (&self->hash); + zchunk_destroy (&self->prev_hash); + zchunk_destroy (&self->block_template_blob); // Free object itself free (self); @@ -568,6 +576,69 @@ wap_proto_recv (wap_proto_t *self, zsock_t *input) GET_NUMBER8 (self->status); break; + case WAP_PROTO_GET_BLOCK_HASH: + GET_NUMBER8 (self->height); + break; + + case WAP_PROTO_GET_BLOCK_HASH_OK: + GET_NUMBER8 (self->status); + { + size_t chunk_size; + GET_NUMBER4 (chunk_size); + if (self->needle + chunk_size > (self->ceiling)) { + zsys_warning ("wap_proto: hash is missing data"); + goto malformed; + } + zchunk_destroy (&self->hash); + self->hash = zchunk_new (self->needle, chunk_size); + self->needle += chunk_size; + } + break; + + case WAP_PROTO_GET_BLOCK_TEMPLATE: + GET_NUMBER8 (self->reserve_size); + { + size_t chunk_size; + GET_NUMBER4 (chunk_size); + if (self->needle + chunk_size > (self->ceiling)) { + zsys_warning ("wap_proto: address is missing data"); + goto malformed; + } + zchunk_destroy (&self->address); + self->address = zchunk_new (self->needle, chunk_size); + self->needle += chunk_size; + } + break; + + case WAP_PROTO_GET_BLOCK_TEMPLATE_OK: + GET_NUMBER8 (self->status); + GET_NUMBER8 (self->reserved_offset); + GET_NUMBER8 (self->height); + GET_NUMBER8 (self->difficulty); + { + size_t chunk_size; + GET_NUMBER4 (chunk_size); + if (self->needle + chunk_size > (self->ceiling)) { + zsys_warning ("wap_proto: prev_hash is missing data"); + goto malformed; + } + zchunk_destroy (&self->prev_hash); + self->prev_hash = zchunk_new (self->needle, chunk_size); + self->needle += chunk_size; + } + { + size_t chunk_size; + GET_NUMBER4 (chunk_size); + if (self->needle + chunk_size > (self->ceiling)) { + zsys_warning ("wap_proto: block_template_blob is missing data"); + goto malformed; + } + zchunk_destroy (&self->block_template_blob); + self->block_template_blob = zchunk_new (self->needle, chunk_size); + self->needle += chunk_size; + } + break; + case WAP_PROTO_STOP: break; @@ -734,6 +805,33 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) case WAP_PROTO_STOP_SAVE_GRAPH_OK: frame_size += 8; // status break; + case WAP_PROTO_GET_BLOCK_HASH: + frame_size += 8; // height + break; + case WAP_PROTO_GET_BLOCK_HASH_OK: + frame_size += 8; // status + frame_size += 4; // Size is 4 octets + if (self->hash) + frame_size += zchunk_size (self->hash); + break; + case WAP_PROTO_GET_BLOCK_TEMPLATE: + frame_size += 8; // reserve_size + frame_size += 4; // Size is 4 octets + if (self->address) + frame_size += zchunk_size (self->address); + break; + case WAP_PROTO_GET_BLOCK_TEMPLATE_OK: + frame_size += 8; // status + frame_size += 8; // reserved_offset + frame_size += 8; // height + frame_size += 8; // difficulty + frame_size += 4; // Size is 4 octets + if (self->prev_hash) + frame_size += zchunk_size (self->prev_hash); + frame_size += 4; // Size is 4 octets + if (self->block_template_blob) + frame_size += zchunk_size (self->block_template_blob); + break; case WAP_PROTO_ERROR: frame_size += 2; // status frame_size += 1 + strlen (self->reason); @@ -930,6 +1028,61 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) PUT_NUMBER8 (self->status); break; + case WAP_PROTO_GET_BLOCK_HASH: + PUT_NUMBER8 (self->height); + break; + + case WAP_PROTO_GET_BLOCK_HASH_OK: + PUT_NUMBER8 (self->status); + if (self->hash) { + PUT_NUMBER4 (zchunk_size (self->hash)); + memcpy (self->needle, + zchunk_data (self->hash), + zchunk_size (self->hash)); + self->needle += zchunk_size (self->hash); + } + else + PUT_NUMBER4 (0); // Empty chunk + break; + + case WAP_PROTO_GET_BLOCK_TEMPLATE: + PUT_NUMBER8 (self->reserve_size); + if (self->address) { + PUT_NUMBER4 (zchunk_size (self->address)); + memcpy (self->needle, + zchunk_data (self->address), + zchunk_size (self->address)); + self->needle += zchunk_size (self->address); + } + else + PUT_NUMBER4 (0); // Empty chunk + break; + + case WAP_PROTO_GET_BLOCK_TEMPLATE_OK: + PUT_NUMBER8 (self->status); + PUT_NUMBER8 (self->reserved_offset); + PUT_NUMBER8 (self->height); + PUT_NUMBER8 (self->difficulty); + if (self->prev_hash) { + PUT_NUMBER4 (zchunk_size (self->prev_hash)); + memcpy (self->needle, + zchunk_data (self->prev_hash), + zchunk_size (self->prev_hash)); + self->needle += zchunk_size (self->prev_hash); + } + else + PUT_NUMBER4 (0); // Empty chunk + if (self->block_template_blob) { + PUT_NUMBER4 (zchunk_size (self->block_template_blob)); + memcpy (self->needle, + zchunk_data (self->block_template_blob), + zchunk_size (self->block_template_blob)); + self->needle += zchunk_size (self->block_template_blob); + } + else + PUT_NUMBER4 (0); // Empty chunk + break; + case WAP_PROTO_ERROR: PUT_NUMBER2 (self->status); PUT_STRING (self->reason); @@ -1210,6 +1363,33 @@ wap_proto_print (wap_proto_t *self) zsys_debug (" status=%ld", (long) self->status); break; + case WAP_PROTO_GET_BLOCK_HASH: + zsys_debug ("WAP_PROTO_GET_BLOCK_HASH:"); + zsys_debug (" height=%ld", (long) self->height); + break; + + case WAP_PROTO_GET_BLOCK_HASH_OK: + zsys_debug ("WAP_PROTO_GET_BLOCK_HASH_OK:"); + zsys_debug (" status=%ld", (long) self->status); + zsys_debug (" hash=[ ... ]"); + break; + + case WAP_PROTO_GET_BLOCK_TEMPLATE: + zsys_debug ("WAP_PROTO_GET_BLOCK_TEMPLATE:"); + zsys_debug (" reserve_size=%ld", (long) self->reserve_size); + zsys_debug (" address=[ ... ]"); + break; + + case WAP_PROTO_GET_BLOCK_TEMPLATE_OK: + zsys_debug ("WAP_PROTO_GET_BLOCK_TEMPLATE_OK:"); + zsys_debug (" status=%ld", (long) self->status); + zsys_debug (" reserved_offset=%ld", (long) self->reserved_offset); + zsys_debug (" height=%ld", (long) self->height); + zsys_debug (" difficulty=%ld", (long) self->difficulty); + zsys_debug (" prev_hash=[ ... ]"); + zsys_debug (" block_template_blob=[ ... ]"); + break; + case WAP_PROTO_STOP: zsys_debug ("WAP_PROTO_STOP:"); break; @@ -1383,6 +1563,18 @@ wap_proto_command (wap_proto_t *self) case WAP_PROTO_STOP_SAVE_GRAPH_OK: return ("STOP_SAVE_GRAPH_OK"); break; + case WAP_PROTO_GET_BLOCK_HASH: + return ("GET_BLOCK_HASH"); + break; + case WAP_PROTO_GET_BLOCK_HASH_OK: + return ("GET_BLOCK_HASH_OK"); + break; + case WAP_PROTO_GET_BLOCK_TEMPLATE: + return ("GET_BLOCK_TEMPLATE"); + break; + case WAP_PROTO_GET_BLOCK_TEMPLATE_OK: + return ("GET_BLOCK_TEMPLATE_OK"); + break; case WAP_PROTO_STOP: return ("STOP"); break; @@ -2137,6 +2329,141 @@ wap_proto_set_level (wap_proto_t *self, byte level) } +// -------------------------------------------------------------------------- +// Get the hash field without transferring ownership + +zchunk_t * +wap_proto_hash (wap_proto_t *self) +{ + assert (self); + return self->hash; +} + +// Get the hash field and transfer ownership to caller + +zchunk_t * +wap_proto_get_hash (wap_proto_t *self) +{ + zchunk_t *hash = self->hash; + self->hash = NULL; + return hash; +} + +// Set the hash field, transferring ownership from caller + +void +wap_proto_set_hash (wap_proto_t *self, zchunk_t **chunk_p) +{ + assert (self); + assert (chunk_p); + zchunk_destroy (&self->hash); + self->hash = *chunk_p; + *chunk_p = NULL; +} + + +// -------------------------------------------------------------------------- +// Get/set the reserve_size field + +uint64_t +wap_proto_reserve_size (wap_proto_t *self) +{ + assert (self); + return self->reserve_size; +} + +void +wap_proto_set_reserve_size (wap_proto_t *self, uint64_t reserve_size) +{ + assert (self); + self->reserve_size = reserve_size; +} + + +// -------------------------------------------------------------------------- +// Get/set the reserved_offset field + +uint64_t +wap_proto_reserved_offset (wap_proto_t *self) +{ + assert (self); + return self->reserved_offset; +} + +void +wap_proto_set_reserved_offset (wap_proto_t *self, uint64_t reserved_offset) +{ + assert (self); + self->reserved_offset = reserved_offset; +} + + +// -------------------------------------------------------------------------- +// Get the prev_hash field without transferring ownership + +zchunk_t * +wap_proto_prev_hash (wap_proto_t *self) +{ + assert (self); + return self->prev_hash; +} + +// Get the prev_hash field and transfer ownership to caller + +zchunk_t * +wap_proto_get_prev_hash (wap_proto_t *self) +{ + zchunk_t *prev_hash = self->prev_hash; + self->prev_hash = NULL; + return prev_hash; +} + +// Set the prev_hash field, transferring ownership from caller + +void +wap_proto_set_prev_hash (wap_proto_t *self, zchunk_t **chunk_p) +{ + assert (self); + assert (chunk_p); + zchunk_destroy (&self->prev_hash); + self->prev_hash = *chunk_p; + *chunk_p = NULL; +} + + +// -------------------------------------------------------------------------- +// Get the block_template_blob field without transferring ownership + +zchunk_t * +wap_proto_block_template_blob (wap_proto_t *self) +{ + assert (self); + return self->block_template_blob; +} + +// Get the block_template_blob field and transfer ownership to caller + +zchunk_t * +wap_proto_get_block_template_blob (wap_proto_t *self) +{ + zchunk_t *block_template_blob = self->block_template_blob; + self->block_template_blob = NULL; + return block_template_blob; +} + +// Set the block_template_blob field, transferring ownership from caller + +void +wap_proto_set_block_template_blob (wap_proto_t *self, zchunk_t **chunk_p) +{ + assert (self); + assert (chunk_p); + zchunk_destroy (&self->block_template_blob); + self->block_template_blob = *chunk_p; + *chunk_p = NULL; +} + + // -------------------------------------------------------------------------- // Get/set the reason field @@ -2646,6 +2973,76 @@ wap_proto_test (bool verbose) assert (wap_proto_routing_id (self)); assert (wap_proto_status (self) == 123); } + wap_proto_set_id (self, WAP_PROTO_GET_BLOCK_HASH); + + wap_proto_set_height (self, 123); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_height (self) == 123); + } + wap_proto_set_id (self, WAP_PROTO_GET_BLOCK_HASH_OK); + + wap_proto_set_status (self, 123); + zchunk_t *get_block_hash_ok_hash = zchunk_new ("Captcha Diem", 12); + wap_proto_set_hash (self, &get_block_hash_ok_hash); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_status (self) == 123); + assert (memcmp (zchunk_data (wap_proto_hash (self)), "Captcha Diem", 12) == 0); + zchunk_destroy (&get_block_hash_ok_hash); + } + wap_proto_set_id (self, WAP_PROTO_GET_BLOCK_TEMPLATE); + + wap_proto_set_reserve_size (self, 123); + zchunk_t *get_block_template_address = zchunk_new ("Captcha Diem", 12); + wap_proto_set_address (self, &get_block_template_address); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_reserve_size (self) == 123); + assert (memcmp (zchunk_data (wap_proto_address (self)), "Captcha Diem", 12) == 0); + zchunk_destroy (&get_block_template_address); + } + wap_proto_set_id (self, WAP_PROTO_GET_BLOCK_TEMPLATE_OK); + + wap_proto_set_status (self, 123); + wap_proto_set_reserved_offset (self, 123); + wap_proto_set_height (self, 123); + wap_proto_set_difficulty (self, 123); + zchunk_t *get_block_template_ok_prev_hash = zchunk_new ("Captcha Diem", 12); + wap_proto_set_prev_hash (self, &get_block_template_ok_prev_hash); + zchunk_t *get_block_template_ok_block_template_blob = zchunk_new ("Captcha Diem", 12); + wap_proto_set_block_template_blob (self, &get_block_template_ok_block_template_blob); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_status (self) == 123); + assert (wap_proto_reserved_offset (self) == 123); + assert (wap_proto_height (self) == 123); + assert (wap_proto_difficulty (self) == 123); + assert (memcmp (zchunk_data (wap_proto_prev_hash (self)), "Captcha Diem", 12) == 0); + zchunk_destroy (&get_block_template_ok_prev_hash); + assert (memcmp (zchunk_data (wap_proto_block_template_blob (self)), "Captcha Diem", 12) == 0); + zchunk_destroy (&get_block_template_ok_block_template_blob); + } wap_proto_set_id (self, WAP_PROTO_STOP); // Send twice diff --git a/src/ipc/wap_server/wap_server.c b/src/ipc/wap_server/wap_server.c index 699a9c11a..98b2a3fa5 100644 --- a/src/ipc/wap_server/wap_server.c +++ b/src/ipc/wap_server/wap_server.c @@ -362,3 +362,23 @@ stop_save_graph (client_t *self) { IPC::Daemon::stop_save_graph(self->message); } + +// --------------------------------------------------------------------------- +// get_block_hash +// + +static void +get_block_hash (client_t *self) +{ + IPC::Daemon::get_block_hash(self->message); +} + +// --------------------------------------------------------------------------- +// get_block_template +// + +static void +get_block_template (client_t *self) +{ + IPC::Daemon::get_block_template(self->message); +} diff --git a/src/rpc/daemon_deprecated_rpc.cpp b/src/rpc/daemon_deprecated_rpc.cpp index 338ef2015..9d50fc97a 100644 --- a/src/rpc/daemon_deprecated_rpc.cpp +++ b/src/rpc/daemon_deprecated_rpc.cpp @@ -15,6 +15,8 @@ // Trivial and error responses may be returned with ns_create_rpc_reply and ns_create_rpc_error // respectively. +// TODO: Add core busy checks to all methods here + #include "daemon_deprecated_rpc.h" #include @@ -26,6 +28,7 @@ */ namespace { + // TODO: put right error codes here int daemon_connection_error = -326701; int parse_error = -32700; int invalid_request = -32600; @@ -43,26 +46,21 @@ namespace return ipc_client && wap_client_connected(ipc_client); } - void connect_to_daemon() { + bool connect_to_daemon() { if (check_connection_to_daemon()) { - return; + return true; } ipc_client = wap_client_new(); wap_client_connect(ipc_client, "ipc://@/monero", 200, "wallet identity"); + return check_connection_to_daemon(); } /*! - * \brief Constructs a response string given a result JSON object. - * - * It also adds boilerplate properties like id, method. + * \brief Initializes a rapidjson response object * \param req net_skeleton request object - * \param result_json rapidjson result object - * \param response_json "Root" rapidjson document that will eventually have the whole response - * \param response Response as a string gets written here. + * \param response_json net_skeleton request object to fill */ - void construct_response_string(struct ns_rpc_request *req, rapidjson::Value &result_json, - rapidjson::Document &response_json, std::string &response) - { + void init_response_object(struct ns_rpc_request *req, rapidjson::Document &response_json) { response_json.SetObject(); response_json.AddMember("jsonrpc", "2.0" , response_json.GetAllocator()); rapidjson::Value string_value(rapidjson::kStringType); @@ -78,6 +76,21 @@ namespace response_json.AddMember("id", string_value, response_json.GetAllocator()); string_value.SetString(req->method[0].ptr, req->method[0].len); response_json.AddMember("method", string_value, response_json.GetAllocator()); + } + + /*! + * \brief Constructs a response string given a result JSON object. + * + * It also adds boilerplate properties like id, method. + * \param req net_skeleton request object + * \param result_json rapidjson result object + * \param response_json "Root" rapidjson document that will eventually have the whole response + * \param response Response as a string gets written here. + */ + void construct_response_string(struct ns_rpc_request *req, rapidjson::Value &result_json, + rapidjson::Document &response_json, std::string &response) + { + init_response_object(req, response_json); response_json.AddMember("result", result_json, response_json.GetAllocator()); rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); @@ -85,6 +98,30 @@ namespace // Write string to `response`. response = buffer.GetString(); } + + /*! + * \brief Constructs a response string given a result string. + * + * It also adds boilerplate properties like id, method. + * \param req net_skeleton request object + * \param result rapidjson result object + * \param response_json "Root" rapidjson document that will eventually have the whole response + * \param response Response as a string gets written here. + */ + void construct_response_string(struct ns_rpc_request *req, const std::string &result, + rapidjson::Document &response_json, std::string &response) + { + init_response_object(req, response_json); + rapidjson::Value string_value(rapidjson::kStringType); + string_value.SetString(result.c_str(), result.length()); + response_json.AddMember("result", string_value, response_json.GetAllocator()); + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + response_json.Accept(writer); + // Write string to `response`. + response = buffer.GetString(); + } + /*! * \brief Implementation of 'getheight' method. * \param buf Buffer to fill in response. @@ -94,12 +131,20 @@ namespace */ int getheight(char *buf, int len, struct ns_rpc_request *req) { - connect_to_daemon(); + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } int rc = wap_client_get_height(ipc_client); if (rc < 0) { return ns_rpc_create_error(buf, len, req, daemon_connection_error, "Couldn't connect to daemon.", "{}"); } + uint64_t status = wap_client_status(ipc_client); + if (status == IPC::STATUS_CORE_BUSY) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Core busy.", "{}"); + } uint64_t height = wap_client_height(ipc_client); rapidjson::Document response_json; rapidjson::Value result_json; @@ -122,7 +167,10 @@ namespace */ int startmining(char *buf, int len, struct ns_rpc_request *req) { - connect_to_daemon(); + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } if (req->params == NULL) { return ns_rpc_create_error(buf, len, req, invalid_params, @@ -160,7 +208,10 @@ namespace "Couldn't connect to daemon.", "{}"); } uint64_t status = wap_client_status(ipc_client); - + if (status == IPC::STATUS_CORE_BUSY) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Core busy.", "{}"); + } if (status == IPC::STATUS_WRONG_ADDRESS) { return ns_rpc_create_error(buf, len, req, invalid_params, @@ -183,12 +234,20 @@ namespace */ int stopmining(char *buf, int len, struct ns_rpc_request *req) { - connect_to_daemon(); + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } int rc = wap_client_stop(ipc_client); if (rc < 0) { return ns_rpc_create_error(buf, len, req, daemon_connection_error, "Couldn't connect to daemon.", "{}"); } + uint64_t status = wap_client_status(ipc_client); + if (status == IPC::STATUS_CORE_BUSY) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Core busy.", "{}"); + } if (wap_client_status(ipc_client) != IPC::STATUS_OK) { return ns_rpc_create_error(buf, len, req, invalid_request, @@ -206,13 +265,21 @@ namespace */ int getinfo(char *buf, int len, struct ns_rpc_request *req) { - connect_to_daemon(); + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } int rc = wap_client_get_info(ipc_client); if (rc < 0) { return ns_rpc_create_error(buf, len, req, daemon_connection_error, "Couldn't connect to daemon.", "{}"); } - if (wap_client_status(ipc_client) != IPC::STATUS_OK) + uint64_t status = wap_client_status(ipc_client); + if (status == IPC::STATUS_CORE_BUSY) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Core busy.", "{}"); + } + if (status != IPC::STATUS_OK) { return ns_rpc_create_error(buf, len, req, invalid_request, "Failed to get info", "{}"); @@ -257,7 +324,10 @@ namespace */ int getpeerlist(char *buf, int len, struct ns_rpc_request *req) { - connect_to_daemon(); + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } int rc = wap_client_get_peer_list(ipc_client); if (rc < 0) { return ns_rpc_create_error(buf, len, req, daemon_connection_error, @@ -309,13 +379,20 @@ namespace */ int getminingstatus(char *buf, int len, struct ns_rpc_request *req) { - connect_to_daemon(); + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } int rc = wap_client_get_mining_status(ipc_client); if (rc < 0) { return ns_rpc_create_error(buf, len, req, daemon_connection_error, "Couldn't connect to daemon.", "{}"); } - + uint64_t status = wap_client_status(ipc_client); + if (status == IPC::STATUS_CORE_BUSY) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Core busy.", "{}"); + } rapidjson::Document response_json; rapidjson::Document::AllocatorType &allocator = response_json.GetAllocator(); rapidjson::Value result_json; @@ -346,7 +423,10 @@ namespace */ int setloghashrate(char *buf, int len, struct ns_rpc_request *req) { - connect_to_daemon(); + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } if (req->params == NULL) { return ns_rpc_create_error(buf, len, req, invalid_params, @@ -376,8 +456,12 @@ namespace return ns_rpc_create_error(buf, len, req, daemon_connection_error, "Couldn't connect to daemon.", "{}"); } - - if (wap_client_status(ipc_client) == IPC::STATUS_NOT_MINING) { + uint64_t status = wap_client_status(ipc_client); + if (status == IPC::STATUS_CORE_BUSY) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Core busy.", "{}"); + } + if (status == IPC::STATUS_NOT_MINING) { return ns_rpc_create_error(buf, len, req, not_mining_error, "Not mining", "{}"); } @@ -394,7 +478,10 @@ namespace */ int setloglevel(char *buf, int len, struct ns_rpc_request *req) { - connect_to_daemon(); + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } if (req->params == NULL) { return ns_rpc_create_error(buf, len, req, invalid_params, @@ -423,8 +510,12 @@ namespace return ns_rpc_create_error(buf, len, req, daemon_connection_error, "Couldn't connect to daemon.", "{}"); } - - if (wap_client_status(ipc_client) == IPC::STATUS_INVALID_LOG_LEVEL) { + uint64_t status = wap_client_status(ipc_client); + if (status == IPC::STATUS_CORE_BUSY) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Core busy.", "{}"); + } + if (status == IPC::STATUS_INVALID_LOG_LEVEL) { return ns_rpc_create_error(buf, len, req, invalid_params, "Invalid log level", "{}"); } @@ -441,12 +532,20 @@ namespace */ int getblockcount(char *buf, int len, struct ns_rpc_request *req) { - connect_to_daemon(); + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } int rc = wap_client_get_height(ipc_client); if (rc < 0) { return ns_rpc_create_error(buf, len, req, daemon_connection_error, "Couldn't connect to daemon.", "{}"); } + uint64_t status = wap_client_status(ipc_client); + if (status == IPC::STATUS_CORE_BUSY) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Core busy.", "{}"); + } uint64_t count = wap_client_height(ipc_client); rapidjson::Document response_json; rapidjson::Value result_json; @@ -469,7 +568,10 @@ namespace */ int startsavegraph(char *buf, int len, struct ns_rpc_request *req) { - connect_to_daemon(); + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } int rc = wap_client_start_save_graph(ipc_client); if (rc < 0) { return ns_rpc_create_error(buf, len, req, daemon_connection_error, @@ -487,7 +589,10 @@ namespace */ int stopsavegraph(char *buf, int len, struct ns_rpc_request *req) { - connect_to_daemon(); + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } int rc = wap_client_stop_save_graph(ipc_client); if (rc < 0) { return ns_rpc_create_error(buf, len, req, daemon_connection_error, @@ -495,6 +600,152 @@ namespace } return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", STATUS_OK); } + + /*! + * \brief Implementation of 'getblockhash' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ + int getblockhash(char *buf, int len, struct ns_rpc_request *req) + { + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } + if (req->params == NULL) + { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Parameters missing.", "{}"); + } + + rapidjson::Document request_json; + char request_buf[1000]; + strncpy(request_buf, req->params[0].ptr, req->params[0].len); + request_buf[req->params[0].len] = '\0'; + if (request_json.Parse(request_buf).HasParseError()) + { + return ns_rpc_create_error(buf, len, req, parse_error, + "Invalid JSON passed", "{}"); + } + + if (!request_json.IsArray() || request_json.Size() < 1 || !request_json[(unsigned int)0].IsNumber()) + { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Incorrect 'height' field", "{}"); + } + + + int height = request_json[(unsigned int)0].GetUint(); + int rc = wap_client_get_block_hash(ipc_client, height); + if (rc < 0) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } + uint64_t status = wap_client_status(ipc_client); + if (status == IPC::STATUS_CORE_BUSY) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Core busy.", "{}"); + } + if (status == IPC::STATUS_HEIGHT_TOO_BIG) { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Height too big.", "{}"); + } + zchunk_t *hash_chunk = wap_client_hash(ipc_client); + std::string hash((char*)zchunk_data(hash_chunk), zchunk_size(hash_chunk)); + std::string response; + rapidjson::Document response_json; + construct_response_string(req, hash, response_json, response); + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + + /*! + * \brief Implementation of 'getblocktemplate' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ + int getblocktemplate(char *buf, int len, struct ns_rpc_request *req) + { + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } + if (req->params == NULL) + { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Parameters missing.", "{}"); + } + + rapidjson::Document request_json; + char request_buf[1000]; + strncpy(request_buf, req->params[0].ptr, req->params[0].len); + request_buf[req->params[0].len] = '\0'; + if (request_json.Parse(request_buf).HasParseError()) + { + return ns_rpc_create_error(buf, len, req, parse_error, + "Invalid JSON passed", "{}"); + } + + if (!request_json.HasMember("reserve_size") || !request_json["reserve_size"].IsNumber()) + { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Incorrect 'reserve_size' field", "{}"); + } + if (!request_json.HasMember("wallet_address") || !request_json["wallet_address"].IsString()) + { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Incorrect 'wallet_address' field", "{}"); + } + + uint64_t reserve_size = request_json["reserve_size"].GetUint(); + std::string wallet_address = request_json["wallet_address"].GetString(); + zchunk_t *address_chunk = zchunk_new((void*)wallet_address.c_str(), wallet_address.length()); + int rc = wap_client_get_block_template(ipc_client, reserve_size, &address_chunk); + if (rc < 0) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } + + uint64_t status = wap_client_status(ipc_client); + if (status == IPC::STATUS_CORE_BUSY) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Core busy.", "{}"); + } + if (status == IPC::STATUS_RESERVE_SIZE_TOO_BIG) { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Reserve size too big.", "{}"); + } + if (status == IPC::STATUS_WRONG_ADDRESS) { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Wrong address.", "{}"); + } + + rapidjson::Document response_json; + rapidjson::Document::AllocatorType &allocator = response_json.GetAllocator(); + rapidjson::Value result_json; + result_json.SetObject(); + result_json.AddMember("difficulty", wap_client_difficulty(ipc_client), allocator); + result_json.AddMember("height", wap_client_height(ipc_client), allocator); + result_json.AddMember("reserved_offset", wap_client_reserved_offset(ipc_client), allocator); + zchunk_t *prev_hash_chunk = wap_client_prev_hash(ipc_client); + rapidjson::Value string_value(rapidjson::kStringType); + string_value.SetString((char*)zchunk_data(prev_hash_chunk), zchunk_size(prev_hash_chunk)); + result_json.AddMember("prev_hash", string_value, allocator); + zchunk_t *block_template_chunk = wap_client_prev_hash(ipc_client); + string_value.SetString((char*)zchunk_data(block_template_chunk), zchunk_size(block_template_chunk)); + result_json.AddMember("blocktemplate_blob", string_value, allocator); + std::string response; + construct_response_string(req, result_json, response_json, response); + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + // Contains a list of method names. const char *method_names[] = { "getheight", @@ -508,6 +759,8 @@ namespace "getblockcount", "startsavegraph", "stopsavegraph", + "getblockhash", + "getblocktemplate", NULL }; @@ -524,6 +777,8 @@ namespace getblockcount, startsavegraph, stopsavegraph, + getblockhash, + getblocktemplate, NULL };