diff --git a/include/bitcoin/node/protocols/protocol_explore.hpp b/include/bitcoin/node/protocols/protocol_explore.hpp index 92529301..55f557b2 100644 --- a/include/bitcoin/node/protocols/protocol_explore.hpp +++ b/include/bitcoin/node/protocols/protocol_explore.hpp @@ -60,25 +60,47 @@ class BCN_API protocol_explore /// REST interface handlers. + bool handle_get_top(const code& ec, interface::top, + uint8_t version, uint8_t media) NOEXCEPT; + bool handle_get_block(const code& ec, interface::block, uint8_t version, uint8_t media, std::optional hash, std::optional height, bool witness) NOEXCEPT; - bool handle_get_header(const code& ec, interface::header, + bool handle_get_block_header(const code& ec, interface::block_header, uint8_t version, uint8_t media, std::optional hash, std::optional height) NOEXCEPT; bool handle_get_block_txs(const code& ec, interface::block_txs, uint8_t version, uint8_t media, std::optional hash, std::optional height) NOEXCEPT; + bool handle_get_block_fees(const code& ec, interface::block_fees, + uint8_t version, uint8_t media, std::optional hash, + std::optional height) NOEXCEPT; + bool handle_get_block_filter(const code& ec, interface::block_filter, + uint8_t version, uint8_t media, uint8_t type, + std::optional hash, + std::optional height) NOEXCEPT; + bool handle_get_block_filter_hash(const code& ec, + interface::block_filter_hash, uint8_t version, uint8_t media, + uint8_t type, std::optional hash, + std::optional height) NOEXCEPT; + bool handle_get_block_filter_header(const code& ec, + interface::block_filter_header, uint8_t version, uint8_t media, + uint8_t type, std::optional hash, + std::optional height) NOEXCEPT; bool handle_get_block_tx(const code& ec, interface::block_tx, uint8_t version, uint8_t media, uint32_t position, std::optional hash, std::optional height, bool witness) NOEXCEPT; - bool handle_get_transaction(const code& ec, interface::transaction, + + bool handle_get_tx(const code& ec, interface::tx, uint8_t version, uint8_t media, const system::hash_cptr& hash, bool witness) NOEXCEPT; bool handle_get_tx_block(const code& ec, interface::tx_block, uint8_t version, uint8_t media, const system::hash_cptr& hash) NOEXCEPT; + bool handle_get_tx_fee(const code& ec, interface::tx_fee, + uint8_t version, uint8_t media, + const system::hash_cptr& hash) NOEXCEPT; bool handle_get_inputs(const code& ec, interface::inputs, uint8_t version, uint8_t media, const system::hash_cptr& hash, @@ -112,19 +134,20 @@ class BCN_API protocol_explore bool handle_get_address(const code& ec, interface::address, uint8_t version, uint8_t media, const system::hash_cptr& hash) NOEXCEPT; - bool handle_get_filter(const code& ec, interface::filter, uint8_t version, - uint8_t media, uint8_t type, std::optional hash, - std::optional height) NOEXCEPT; - bool handle_get_filter_hash(const code& ec, interface::filter_hash, - uint8_t version, uint8_t media, uint8_t type, - std::optional hash, - std::optional height) NOEXCEPT; - bool handle_get_filter_header(const code& ec, interface::filter_header, - uint8_t version, uint8_t media, uint8_t type, - std::optional hash, - std::optional height) NOEXCEPT; + bool handle_get_address_confirmed(const code& ec, + interface::address_confirmed, uint8_t version, uint8_t media, + const system::hash_cptr& hash) NOEXCEPT; + bool handle_get_address_unconfirmed(const code& ec, + interface::address_unconfirmed, uint8_t version, uint8_t media, + const system::hash_cptr& hash) NOEXCEPT; + bool handle_get_address_balance(const code& ec, + interface::address_balance, uint8_t version, uint8_t media, + const system::hash_cptr& hash) NOEXCEPT; private: + void inject(boost::json::value& out, std::optional height, + const database::header_link& link) const NOEXCEPT; + database::header_link to_header(const std::optional& height, const std::optional& hash) NOEXCEPT; diff --git a/src/parse/target.cpp b/src/parse/target.cpp index 62745128..b6e8af4b 100644 --- a/src/parse/target.cpp +++ b/src/parse/target.cpp @@ -46,6 +46,13 @@ static hash_cptr to_hash(const std::string_view& token) NOEXCEPT emplace_shared(std::move(out)) : hash_cptr{}; } +static hash_cptr to_base16(const std::string_view& token) NOEXCEPT +{ + hash_digest out{}; + return decode_base16(out, token) ? + emplace_shared(std::move(out)) : hash_cptr{}; +} + code parse_target(request_t& out, const std::string_view& path) NOEXCEPT { const auto clean = split(path, "?", false, false).front(); @@ -84,38 +91,21 @@ code parse_target(request_t& out, const std::string_view& path) NOEXCEPT // transaction, address, inputs, and outputs are identical excluding names; // input and output are identical excluding names; block is unique. const auto target = segments[segment++]; - if (target == "address") + if (target == "top") { - if (segment == segments.size()) - return error::missing_hash; - - const auto hash = to_hash(segments[segment++]); - if (!hash) return error::invalid_hash; - - method = "address"; - params["hash"] = hash; + method = "top"; } - else if (target == "inputs") + else if (target == "address") { if (segment == segments.size()) return error::missing_hash; - const auto hash = to_hash(segments[segment++]); - if (!hash) return error::invalid_hash; - - method = "inputs"; - params["hash"] = hash; - } - else if (target == "outputs") - { - if (segment == segments.size()) - return error::missing_hash; - - const auto hash = to_hash(segments[segment++]); - if (!hash) return error::invalid_hash; + // address hash is a single sha256, and conventionally not reversed. + const auto base16 = to_base16(segments[segment++]); + if (!base16) return error::invalid_hash; - method = "outputs"; - params["hash"] = hash; + method = "address"; + params["hash"] = base16; } else if (target == "input") { @@ -126,28 +116,32 @@ code parse_target(request_t& out, const std::string_view& path) NOEXCEPT if (!hash) return error::invalid_hash; params["hash"] = hash; - if (segment == segments.size()) - return error::missing_component; - - const auto component = segments[segment++]; - uint32_t index{}; - if (!to_number(index, component)) - return error::invalid_number; - - params["index"] = index; if (segment == segments.size()) { - method = "input"; + method = "inputs"; } else { - const auto subcomponent = segments[segment++]; - if (subcomponent == "script") - method = "input_script"; - else if (subcomponent == "witness") - method = "input_witness"; + const auto component = segments[segment++]; + uint32_t index{}; + if (!to_number(index, component)) + return error::invalid_number; + + params["index"] = index; + if (segment == segments.size()) + { + method = "input"; + } else - return error::invalid_subcomponent; + { + const auto subcomponent = segments[segment++]; + if (subcomponent == "script") + method = "input_script"; + else if (subcomponent == "witness") + method = "input_witness"; + else + return error::invalid_subcomponent; + } } } else if (target == "output") @@ -159,33 +153,37 @@ code parse_target(request_t& out, const std::string_view& path) NOEXCEPT if (!hash) return error::invalid_hash; params["hash"] = hash; - if (segment == segments.size()) - return error::missing_component; - - const auto component = segments[segment++]; - uint32_t index{}; - if (!to_number(index, component)) - return error::invalid_number; - - params["index"] = index; if (segment == segments.size()) { - method = "output"; + method = "outputs"; } else { - const auto subcomponent = segments[segment++]; - if (subcomponent == "script") - method = "output_script"; - else if (subcomponent == "spender") - method = "output_spender"; - else if (subcomponent == "spenders") - method = "output_spenders"; + const auto component = segments[segment++]; + uint32_t index{}; + if (!to_number(index, component)) + return error::invalid_number; + + params["index"] = index; + if (segment == segments.size()) + { + method = "output"; + } else - return error::invalid_subcomponent; + { + const auto subcomponent = segments[segment++]; + if (subcomponent == "script") + method = "output_script"; + else if (subcomponent == "spender") + method = "output_spender"; + else if (subcomponent == "spenders") + method = "output_spenders"; + else + return error::invalid_subcomponent; + } } } - else if (target == "transaction") + else if (target == "tx") { if (segment == segments.size()) return error::missing_hash; @@ -196,13 +194,15 @@ code parse_target(request_t& out, const std::string_view& path) NOEXCEPT params["hash"] = hash; if (segment == segments.size()) { - method = "transaction"; + method = "tx"; } else { const auto component = segments[segment++]; if (component == "block") method = "tx_block"; + else if (component == "fee") + method = "tx_fee"; else return error::invalid_component; } @@ -221,8 +221,6 @@ code parse_target(request_t& out, const std::string_view& path) NOEXCEPT const auto hash = to_hash(segments[segment++]); if (!hash) return error::invalid_hash; - // nullables can be implicit. - ////params["height"] = null_t{}; params["hash"] = hash; } else if (by == "height") @@ -234,8 +232,6 @@ code parse_target(request_t& out, const std::string_view& path) NOEXCEPT if (!to_number(height, segments[segment++])) return error::invalid_number; - // nullables can be implicit. - ////params["hash"] = null_t{}; params["height"] = height; } else @@ -250,7 +246,7 @@ code parse_target(request_t& out, const std::string_view& path) NOEXCEPT else { const auto component = segments[segment++]; - if (component == "transaction") + if (component == "tx") { if (segment == segments.size()) return error::missing_position; @@ -263,9 +259,11 @@ code parse_target(request_t& out, const std::string_view& path) NOEXCEPT method = "block_tx"; } else if (component == "header") - method = "header"; - else if (component == "transactions") + method = "block_header"; + else if (component == "txs") method = "block_txs"; + else if (component == "fees") + method = "block_fees"; else if (component == "filter") { if (segment == segments.size()) @@ -278,15 +276,15 @@ code parse_target(request_t& out, const std::string_view& path) NOEXCEPT params["type"] = type; if (segment == segments.size()) { - method = "filter"; + method = "block_filter"; } else { const auto subcomponent = segments[segment++]; if (subcomponent == "hash") - method = "filter_hash"; + method = "block_filter_hash"; else if (subcomponent == "header") - method = "filter_header"; + method = "block_filter_header"; else return error::invalid_subcomponent; } diff --git a/src/protocols/protocol_explore.cpp b/src/protocols/protocol_explore.cpp index 645e94c7..5bdc1c17 100644 --- a/src/protocols/protocol_explore.cpp +++ b/src/protocols/protocol_explore.cpp @@ -58,12 +58,20 @@ void protocol_explore::start() NOEXCEPT if (started()) return; + SUBSCRIBE_EXPLORE(handle_get_top, _1, _2, _3, _4); + SUBSCRIBE_EXPLORE(handle_get_block, _1, _2, _3, _4, _5, _6, _7); - SUBSCRIBE_EXPLORE(handle_get_header, _1, _2, _3, _4, _5, _6); + SUBSCRIBE_EXPLORE(handle_get_block_header, _1, _2, _3, _4, _5, _6); SUBSCRIBE_EXPLORE(handle_get_block_txs, _1, _2, _3, _4, _5, _6); + SUBSCRIBE_EXPLORE(handle_get_block_fees, _1, _2, _3, _4, _5, _6); + SUBSCRIBE_EXPLORE(handle_get_block_filter, _1, _2, _3, _4, _5, _6, _7); + SUBSCRIBE_EXPLORE(handle_get_block_filter_hash, _1, _2, _3, _4, _5, _6, _7); + SUBSCRIBE_EXPLORE(handle_get_block_filter_header, _1, _2, _3, _4, _5, _6, _7); SUBSCRIBE_EXPLORE(handle_get_block_tx, _1, _2, _3, _4, _5, _6, _7, _8); - SUBSCRIBE_EXPLORE(handle_get_transaction, _1, _2, _3, _4, _5, _6); + + SUBSCRIBE_EXPLORE(handle_get_tx, _1, _2, _3, _4, _5, _6); SUBSCRIBE_EXPLORE(handle_get_tx_block, _1, _2, _3, _4, _5); + SUBSCRIBE_EXPLORE(handle_get_tx_fee, _1, _2, _3, _4, _5); SUBSCRIBE_EXPLORE(handle_get_inputs, _1, _2, _3, _4, _5, _6); SUBSCRIBE_EXPLORE(handle_get_input, _1, _2, _3, _4, _5, _6, _7); @@ -77,9 +85,9 @@ void protocol_explore::start() NOEXCEPT SUBSCRIBE_EXPLORE(handle_get_output_spenders, _1, _2, _3, _4, _5, _6); SUBSCRIBE_EXPLORE(handle_get_address, _1, _2, _3, _4, _5); - SUBSCRIBE_EXPLORE(handle_get_filter, _1, _2, _3, _4, _5, _6, _7); - SUBSCRIBE_EXPLORE(handle_get_filter_hash, _1, _2, _3, _4, _5, _6, _7); - SUBSCRIBE_EXPLORE(handle_get_filter_header, _1, _2, _3, _4, _5, _6, _7); + SUBSCRIBE_EXPLORE(handle_get_address_confirmed, _1, _2, _3, _4, _5); + SUBSCRIBE_EXPLORE(handle_get_address_unconfirmed, _1, _2, _3, _4, _5); + SUBSCRIBE_EXPLORE(handle_get_address_balance, _1, _2, _3, _4, _5); protocol_html::start(); } @@ -130,16 +138,18 @@ data_chunk to_bin(const Object& object, size_t size, Args&&... args) NOEXCEPT stream::out::fast sink{ out }; write::bytes::fast writer{ sink }; object.to_data(writer, std::forward(args)...); + BC_ASSERT(writer); return out; } template std::string to_hex(const Object& object, size_t size, Args&&... args) NOEXCEPT { - std::string out(size, '\0'); + std::string out(two * size, '\0'); stream::out::fast sink{ out }; write::base16::fast writer{ sink }; object.to_data(writer, std::forward(args)...); + BC_ASSERT(writer); return out; } @@ -153,6 +163,7 @@ data_chunk to_bin_array(const Collection& collection, size_t size, for (const auto& element: collection) element.to_data(writer, std::forward(args)...); + BC_ASSERT(writer); return out; } @@ -160,12 +171,13 @@ template std::string to_hex_array(const Collection& collection, size_t size, Args&&... args) NOEXCEPT { - std::string out(size, '\0'); + std::string out(two * size, '\0'); stream::out::fast sink{ out }; write::base16::fast writer{ sink }; for (const auto& element: collection) element.to_data(writer, std::forward(args)...); + BC_ASSERT(writer); return out; } @@ -179,6 +191,7 @@ data_chunk to_bin_ptr_array(const Collection& collection, size_t size, for (const auto& ptr: collection) ptr->to_data(writer, std::forward(args)...); + BC_ASSERT(writer); return out; } @@ -186,15 +199,40 @@ template std::string to_hex_ptr_array(const Collection& collection, size_t size, Args&&... args) NOEXCEPT { - std::string out(size, '\0'); + std::string out(two * size, '\0'); stream::out::fast sink{ out }; write::base16::fast writer{ sink }; for (const auto& ptr: collection) ptr->to_data(writer, std::forward(args)...); + BC_ASSERT(writer); return out; } +bool protocol_explore::handle_get_top(const code& ec, interface::top, + uint8_t, uint8_t media) NOEXCEPT +{ + if (stopped(ec)) + return false; + + const auto height = archive().get_top_confirmed(); + switch (media) + { + case data: + send_chunk(to_little_endian_size(height)); + return true; + case text: + send_text(encode_base16(to_little_endian_size(height))); + return true; + case json: + send_json(height, two * sizeof(height)); + return true; + } + + send_not_found(); + return true; +} + bool protocol_explore::handle_get_block(const code& ec, interface::block, uint8_t, uint8_t media, std::optional hash, std::optional height, bool witness) NOEXCEPT @@ -202,8 +240,8 @@ bool protocol_explore::handle_get_block(const code& ec, interface::block, if (stopped(ec)) return false; - if (const auto block = archive().get_block(to_header(height, hash), - witness)) + const auto link = to_header(height, hash); + if (const auto block = archive().get_block(link, witness)) { const auto size = block->serialized_size(witness); switch (media) @@ -215,7 +253,9 @@ bool protocol_explore::handle_get_block(const code& ec, interface::block, send_text(to_hex(*block, size, witness)); return true; case json: - send_json(value_from(block), two * size); + auto model = value_from(block); + inject(model.at("header"), height, link); + send_json(std::move(model), two * size); return true; } } @@ -224,14 +264,15 @@ bool protocol_explore::handle_get_block(const code& ec, interface::block, return true; } -bool protocol_explore::handle_get_header(const code& ec, interface::header, - uint8_t, uint8_t media, std::optional hash, - std::optional height) NOEXCEPT +bool protocol_explore::handle_get_block_header(const code& ec, + interface::block_header, uint8_t, uint8_t media, + std::optional hash, std::optional height) NOEXCEPT { if (stopped(ec)) return false; - if (const auto header = archive().get_header(to_header(height, hash))) + const auto link = to_header(height, hash); + if (const auto header = archive().get_header(link)) { constexpr auto size = chain::header::serialized_size(); switch (media) @@ -243,7 +284,9 @@ bool protocol_explore::handle_get_header(const code& ec, interface::header, send_text(to_hex(*header, size)); return true; case json: - send_json(value_from(header), two * size); + auto model = value_from(header); + inject(model, height, link); + send_json(std::move(model), two * size); return true; } } @@ -293,6 +336,144 @@ bool protocol_explore::handle_get_block_txs(const code& ec, return true; } +bool protocol_explore::handle_get_block_fees(const code& ec, + interface::block_fees, uint8_t, uint8_t media, + std::optional hash, std::optional height) NOEXCEPT +{ + if (stopped(ec)) + return false; + + if (const auto fees = archive().get_block_fees(to_header(height, hash)); + fees != max_uint64) + { + switch (media) + { + case data: + send_chunk(to_little_endian_size(fees)); + return true; + case text: + send_text(encode_base16(to_little_endian_size(fees))); + return true; + case json: + send_json(fees, two * sizeof(fees)); + return true; + } + } + + send_not_found(); + return true; +} + +bool protocol_explore::handle_get_block_filter(const code& ec, + interface::block_filter, uint8_t, uint8_t media, uint8_t type, + std::optional hash, std::optional height) NOEXCEPT +{ + if (stopped(ec)) + return false; + + const auto& query = archive(); + if (!query.filter_enabled() || type != client_filter::type_id::neutrino) + { + send_not_implemented(); + return true; + } + + data_chunk filter{}; + if (query.get_filter_body(filter, to_header(height, hash))) + { + switch (media) + { + case data: + send_chunk(std::move(filter)); + return true; + case text: + send_text(encode_base16(filter)); + return true; + case json: + send_json(value_from(encode_base16(filter)), + two * filter.size()); + return true; + } + } + + send_not_found(); + return true; +} + +bool protocol_explore::handle_get_block_filter_hash(const code& ec, + interface::block_filter_hash, uint8_t, uint8_t media, uint8_t type, + std::optional hash, std::optional height) NOEXCEPT +{ + if (stopped(ec)) + return false; + + const auto& query = archive(); + if (!query.filter_enabled() || type != client_filter::type_id::neutrino) + { + send_not_implemented(); + return true; + } + + hash_digest filter_hash{ hash_size }; + if (query.get_filter_hash(filter_hash, to_header(height, hash))) + { + switch (media) + { + case data: + send_chunk(to_chunk(filter_hash)); + return true; + case text: + send_text(encode_base16(filter_hash)); + return true; + case json: + send_json(value_from(encode_hash(filter_hash)), + two * hash_size); + return true; + } + } + + send_not_found(); + return true; +} + +bool protocol_explore::handle_get_block_filter_header(const code& ec, + interface::block_filter_header, uint8_t, uint8_t media, uint8_t type, + std::optional hash, std::optional height) NOEXCEPT +{ + if (stopped(ec)) + return false; + + const auto& query = archive(); + if (!query.filter_enabled() || type != client_filter::type_id::neutrino) + { + send_not_implemented(); + return true; + } + + hash_digest filter_head{ hash_size }; + if (query.get_filter_head(filter_head, to_header(height, hash))) + { + switch (media) + { + case data: + { + send_chunk(to_chunk(filter_head)); + return true; + } + case text: + send_text(encode_base16(filter_head)); + return true; + case json: + send_json(value_from(encode_hash(filter_head)), + two * hash_size); + return true; + } + } + + send_not_found(); + return true; +} + bool protocol_explore::handle_get_block_tx(const code& ec, interface::block_tx, uint8_t, uint8_t media, uint32_t position, std::optional hash, std::optional height, bool witness) NOEXCEPT @@ -323,9 +504,8 @@ bool protocol_explore::handle_get_block_tx(const code& ec, interface::block_tx, return true; } -bool protocol_explore::handle_get_transaction(const code& ec, - interface::transaction, uint8_t, uint8_t media, const hash_cptr& hash, - bool witness) NOEXCEPT +bool protocol_explore::handle_get_tx(const code& ec, interface::tx, uint8_t, + uint8_t media, const hash_cptr& hash, bool witness) NOEXCEPT { if (stopped(ec)) return false; @@ -390,6 +570,34 @@ bool protocol_explore::handle_get_tx_block(const code& ec, interface::tx_block, return true; } +bool protocol_explore::handle_get_tx_fee(const code& ec, interface::tx_fee, + uint8_t, uint8_t media, const hash_cptr& hash) NOEXCEPT +{ + if (stopped(ec)) + return false; + + const auto& query = archive(); + if (const auto fee = query.get_tx_fee(query.to_tx(*hash)); + fee != max_uint64) + { + switch (media) + { + case data: + send_chunk(to_little_endian_size(fee)); + return true; + case text: + send_text(encode_base16(to_little_endian_size(fee))); + return true; + case json: + send_json(fee, two * sizeof(fee)); + return true; + } + } + + send_not_found(); + return true; +} + bool protocol_explore::handle_get_inputs(const code& ec, interface::inputs, uint8_t, uint8_t media, const hash_cptr& hash, bool witness) NOEXCEPT { @@ -685,12 +893,14 @@ bool protocol_explore::handle_get_output_spenders(const code& ec, return true; } - const auto size = points.size() * chain::point::serialized_size(); + // TODO: dedup and lexical sort. chain::points out(points.size()); - std::ranges::transform(points, out.begin(), - [&](const auto& link) NOEXCEPT { return query.get_point(link); }); + std::ranges::transform(points, out.begin(), [&](const auto& link) NOEXCEPT + { + return query.get_spender(link); + }); - // TODO: dedup and sort by height/position/index in query. + const auto size = out.size() * chain::point::serialized_size(); switch (media) { case data: @@ -734,142 +944,80 @@ bool protocol_explore::handle_get_address(const code& ec, interface::address, return true; } - // TODO: dedup and sort by height/position/index in query. - if (const auto ptr = query.get_output(outputs.front())) + // TODO: dedup and lexical sort. + chain::points out(outputs.size()); + std::ranges::transform(outputs, out.begin(), [&](const auto& link) NOEXCEPT { - switch (media) - { - case data: - { - send_chunk({}); - return true; - } - case text: - send_text(encode_base16({})); - return true; - case json: - send_json(value_from(*ptr), {}); - return true; - } + return query.get_spent(link); + }); + + const auto size = out.size() * chain::point::serialized_size(); + switch (media) + { + case data: + send_chunk(to_bin_array(out, size)); + return true; + case text: + send_text(to_hex_array(out, size)); + return true; + case json: + send_json(value_from(out), two * size); + return true; } send_not_found(); return true; } -bool protocol_explore::handle_get_filter(const code& ec, interface::filter, - uint8_t, uint8_t media, uint8_t type, std::optional hash, - std::optional height) NOEXCEPT +bool protocol_explore::handle_get_address_confirmed(const code& ec, + interface::address_confirmed, uint8_t, uint8_t , + const hash_cptr& ) NOEXCEPT { if (stopped(ec)) return false; - const auto& query = archive(); - if (!query.filter_enabled() || type != client_filter::type_id::neutrino) - { - send_not_implemented(); - return true; - } - - data_chunk filter{}; - if (query.get_filter_body(filter, to_header(height, hash))) - { - switch (media) - { - case data: - send_chunk(std::move(filter)); - return true; - case text: - send_text(encode_base16(filter)); - return true; - case json: - send_json(value_from(encode_base16(filter)), - two * filter.size()); - return true; - } - } + // TODO. - send_not_found(); + send_not_implemented(); return true; } -bool protocol_explore::handle_get_filter_hash(const code& ec, - interface::filter_hash, uint8_t, uint8_t media, uint8_t type, - std::optional hash, std::optional height) NOEXCEPT +bool protocol_explore::handle_get_address_unconfirmed(const code& ec, + interface::address_unconfirmed, uint8_t, uint8_t , + const hash_cptr& ) NOEXCEPT { if (stopped(ec)) return false; - const auto& query = archive(); - if (!query.filter_enabled() || type != client_filter::type_id::neutrino) - { - send_not_implemented(); - return true; - } + // TODO. - hash_digest filter_hash{ hash_size }; - if (query.get_filter_hash(filter_hash, to_header(height, hash))) - { - switch (media) - { - case data: - send_chunk(to_chunk(filter_hash)); - return true; - case text: - send_text(encode_base16(filter_hash)); - return true; - case json: - send_json(value_from(encode_hash(filter_hash)), - two * hash_size); - return true; - } - } - - send_not_found(); + send_not_implemented(); return true; } -bool protocol_explore::handle_get_filter_header(const code& ec, - interface::filter_header, uint8_t, uint8_t media, uint8_t type, - std::optional hash, std::optional height) NOEXCEPT +bool protocol_explore::handle_get_address_balance(const code& ec, + interface::address_balance, uint8_t, uint8_t, + const hash_cptr&) NOEXCEPT { if (stopped(ec)) return false; - const auto& query = archive(); - if (!query.filter_enabled() || type != client_filter::type_id::neutrino) - { - send_not_implemented(); - return true; - } - - hash_digest filter_head{ hash_size }; - if (query.get_filter_head(filter_head, to_header(height, hash))) - { - switch (media) - { - case data: - { - send_chunk(to_chunk(filter_head)); - return true; - } - case text: - send_text(encode_base16(filter_head)); - return true; - case json: - send_json(value_from(encode_hash(filter_head)), - two * hash_size); - return true; - } - } + // TODO. - send_not_found(); + send_not_implemented(); return true; } // private // ---------------------------------------------------------------------------- +void protocol_explore::inject(value& out, std::optional height, + const database::header_link& link) const NOEXCEPT +{ + out.as_object().emplace("height", height.has_value() ? height.value() : + archive().get_height(link).value); +} + database::header_link protocol_explore::to_header( const std::optional& height, const std::optional& hash) NOEXCEPT diff --git a/test/parse/target.cpp b/test/parse/target.cpp index ad4a7bc9..078e36ba 100644 --- a/test/parse/target.cpp +++ b/test/parse/target.cpp @@ -158,13 +158,13 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__block_invalid_id_type__invalid_id_type BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/block/invalid/123"), node::error::invalid_id_type); } -// header/height +// block_header/height BOOST_AUTO_TEST_CASE(parse__parse_target__header_height_valid__expected) { request_t request{}; BOOST_REQUIRE(!parse_target(request, "/v42/block/height/123456/header/")); - BOOST_REQUIRE_EQUAL(request.method, "header"); + BOOST_REQUIRE_EQUAL(request.method, "block_header"); BOOST_REQUIRE(request.params.has_value()); const auto& params = request.params.value(); @@ -186,7 +186,7 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__header_height_extra_segment__extra_seg BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/block/height/123/header/extra"), node::error::extra_segment); } -// header/hash +// block_header/hash BOOST_AUTO_TEST_CASE(parse__parse_target__header_hash_valid__expected) { @@ -194,7 +194,7 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__header_hash_valid__expected) request_t request{}; BOOST_REQUIRE(!parse_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "header"); + BOOST_REQUIRE_EQUAL(request.method, "block_header"); BOOST_REQUIRE(request.params.has_value()); const auto& params = request.params.value(); @@ -226,7 +226,7 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__header_hash_extra_segment__extra_segme BOOST_AUTO_TEST_CASE(parse__parse_target__block_txs_height_valid__expected) { request_t request{}; - BOOST_REQUIRE(!parse_target(request, "/v42/block/height/123456/transactions")); + BOOST_REQUIRE(!parse_target(request, "/v42/block/height/123456/txs")); BOOST_REQUIRE_EQUAL(request.method, "block_txs"); BOOST_REQUIRE(request.params.has_value()); @@ -246,14 +246,14 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__block_txs_height_valid__expected) BOOST_AUTO_TEST_CASE(parse__parse_target__block_txs_height_extra_segment__extra_segment) { request_t out{}; - BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/block/height/123/transactions/extra"), node::error::extra_segment); + BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/block/height/123/txs/extra"), node::error::extra_segment); } // block_txs/hash BOOST_AUTO_TEST_CASE(parse__parse_target__block_txs_hash_valid__expected) { - const std::string path = "/v42/block/hash/0000000000000000000000000000000000000000000000000000000000000042/transactions"; + const std::string path = "/v42/block/hash/0000000000000000000000000000000000000000000000000000000000000042/txs"; request_t request{}; BOOST_REQUIRE(!parse_target(request, path)); @@ -279,7 +279,7 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__block_txs_hash_valid__expected) BOOST_AUTO_TEST_CASE(parse__parse_target__block_txs_hash_extra_segment__extra_segment) { - const std::string path = "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/transactions/extra"; + const std::string path = "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/txs/extra"; request_t out{}; BOOST_REQUIRE_EQUAL(parse_target(out, path), node::error::extra_segment); } @@ -289,7 +289,7 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__block_txs_hash_extra_segment__extra_se BOOST_AUTO_TEST_CASE(parse__parse_target__block_tx_height_valid__expected) { request_t request{}; - BOOST_REQUIRE(!parse_target(request, "/v42/block/height/123456/transaction/7")); + BOOST_REQUIRE(!parse_target(request, "/v42/block/height/123456/tx/7")); BOOST_REQUIRE_EQUAL(request.method, "block_tx"); BOOST_REQUIRE(request.params.has_value()); @@ -312,26 +312,26 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__block_tx_height_valid__expected) BOOST_AUTO_TEST_CASE(parse__parse_target__block_tx_height_missing_position__missing_position) { request_t out{}; - BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/block/height/123/transaction"), node::error::missing_position); + BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/block/height/123/tx"), node::error::missing_position); } BOOST_AUTO_TEST_CASE(parse__parse_target__block_tx_height_invalid_position__invalid_number) { request_t out{}; - BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/block/height/123/transaction/invalid"), node::error::invalid_number); + BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/block/height/123/tx/invalid"), node::error::invalid_number); } BOOST_AUTO_TEST_CASE(parse__parse_target__block_tx_height_extra_segment__extra_segment) { request_t out{}; - BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/block/height/123/transaction/7/extra"), node::error::extra_segment); + BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/block/height/123/tx/7/extra"), node::error::extra_segment); } // block_tx/hash BOOST_AUTO_TEST_CASE(parse__parse_target__block_tx_hash_valid__expected) { - const std::string path = "/v42/block/hash/0000000000000000000000000000000000000000000000000000000000000042/transaction/7"; + const std::string path = "/v42/block/hash/0000000000000000000000000000000000000000000000000000000000000042/tx/7"; request_t request{}; BOOST_REQUIRE(!parse_target(request, path)); @@ -361,31 +361,31 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__block_tx_hash_valid__expected) BOOST_AUTO_TEST_CASE(parse__parse_target__block_tx_hash_missing_position__missing_position) { request_t out{}; - BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/transaction"), node::error::missing_position); + BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/tx"), node::error::missing_position); } BOOST_AUTO_TEST_CASE(parse__parse_target__block_tx_hash_invalid_position__invalid_number) { request_t out{}; - BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/transaction/invalid"), node::error::invalid_number); + BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/tx/invalid"), node::error::invalid_number); } BOOST_AUTO_TEST_CASE(parse__parse_target__block_tx_hash_extra_segment__extra_segment) { - const std::string path = "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/transaction/7/extra"; + const std::string path = "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/tx/7/extra"; request_t out{}; BOOST_REQUIRE_EQUAL(parse_target(out, path), node::error::extra_segment); } -// transaction +// tx -BOOST_AUTO_TEST_CASE(parse__parse_target__transaction_valid__expected) +BOOST_AUTO_TEST_CASE(parse__parse_target__tx_valid__expected) { - const std::string path = "/v42/transaction/0000000000000000000000000000000000000000000000000000000000000042"; + const std::string path = "/v42/tx/0000000000000000000000000000000000000000000000000000000000000042"; request_t request{}; BOOST_REQUIRE(!parse_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "transaction"); + BOOST_REQUIRE_EQUAL(request.method, "tx"); BOOST_REQUIRE(request.params.has_value()); const auto& params = request.params.value(); @@ -405,21 +405,21 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__transaction_valid__expected) BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); } -BOOST_AUTO_TEST_CASE(parse__parse_target__transaction_missing_hash__missing_hash) +BOOST_AUTO_TEST_CASE(parse__parse_target__tx_missing_hash__missing_hash) { request_t out{}; - BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/transaction"), node::error::missing_hash); + BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/tx"), node::error::missing_hash); } BOOST_AUTO_TEST_CASE(parse__parse_target__transaction_invalid_hash__invalid_hash) { request_t out{}; - BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/transaction/invalidhex"), node::error::invalid_hash); + BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/tx/invalidhex"), node::error::invalid_hash); } -BOOST_AUTO_TEST_CASE(parse__parse_target__transaction_invalid_component__invalid_component) +BOOST_AUTO_TEST_CASE(parse__parse_target__tx_invalid_component__invalid_component) { - const std::string path = "/v3/transaction/0000000000000000000000000000000000000000000000000000000000000000/extra"; + const std::string path = "/v3/tx/0000000000000000000000000000000000000000000000000000000000000000/extra"; request_t out{}; BOOST_REQUIRE_EQUAL(parse_target(out, path), node::error::invalid_component); } @@ -428,7 +428,7 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__transaction_invalid_component__invalid BOOST_AUTO_TEST_CASE(parse__parse_target__tx_block_valid__expected) { - const std::string path = "/v42/transaction/0000000000000000000000000000000000000000000000000000000000000042/block"; + const std::string path = "/v42/tx/0000000000000000000000000000000000000000000000000000000000000042/block"; request_t request{}; BOOST_REQUIRE(!parse_target(request, path)); @@ -454,14 +454,14 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__tx_block_valid__expected) BOOST_AUTO_TEST_CASE(parse__parse_target__tx_block_invalid_component__invalid_component) { - const std::string path = "/v3/transaction/0000000000000000000000000000000000000000000000000000000000000000/invalid"; + const std::string path = "/v3/tx/0000000000000000000000000000000000000000000000000000000000000000/invalid"; request_t out{}; BOOST_REQUIRE_EQUAL(parse_target(out, path), node::error::invalid_component); } BOOST_AUTO_TEST_CASE(parse__parse_target__tx_block_extra_segment__extra_segment) { - const std::string path = "/v3/transaction/0000000000000000000000000000000000000000000000000000000000000000/block/extra"; + const std::string path = "/v3/tx/0000000000000000000000000000000000000000000000000000000000000000/block/extra"; request_t out{}; BOOST_REQUIRE_EQUAL(parse_target(out, path), node::error::extra_segment); } @@ -470,7 +470,7 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__tx_block_extra_segment__extra_segment) BOOST_AUTO_TEST_CASE(parse__parse_target__inputs_valid__expected) { - const std::string path = "/v255/inputs/0000000000000000000000000000000000000000000000000000000000000042"; + const std::string path = "/v255/input/0000000000000000000000000000000000000000000000000000000000000042"; request_t request{}; BOOST_REQUIRE(!parse_target(request, path)); @@ -497,20 +497,20 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__inputs_valid__expected) BOOST_AUTO_TEST_CASE(parse__parse_target__inputs_missing_hash__missing_hash) { request_t out{}; - BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/inputs"), node::error::missing_hash); + BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/input"), node::error::missing_hash); } BOOST_AUTO_TEST_CASE(parse__parse_target__inputs_invalid_hash__invalid_hash) { request_t out{}; - BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/inputs/invalidhex"), node::error::invalid_hash); + BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/input/invalidhex"), node::error::invalid_hash); } -BOOST_AUTO_TEST_CASE(parse__parse_target__inputs_extra_segment__extra_segment) +BOOST_AUTO_TEST_CASE(parse__parse_target__inputs_invalid_number__invalid_number) { - const std::string path = "/v3/inputs/0000000000000000000000000000000000000000000000000000000000000000/extra"; + const std::string path = "/v3/input/0000000000000000000000000000000000000000000000000000000000000000/invalid"; request_t out{}; - BOOST_REQUIRE_EQUAL(parse_target(out, path), node::error::extra_segment); + BOOST_REQUIRE_EQUAL(parse_target(out, path), node::error::invalid_number); } // input @@ -556,14 +556,7 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__input_invalid_hash__invalid_hash) BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/input/invalidhex/3"), node::error::invalid_hash); } -BOOST_AUTO_TEST_CASE(parse__parse_target__input_missing_component__missing_component) -{ - const std::string path = "/v3/input/0000000000000000000000000000000000000000000000000000000000000000"; - request_t out{}; - BOOST_REQUIRE_EQUAL(parse_target(out, path), node::error::missing_component); -} - -BOOST_AUTO_TEST_CASE(parse__parse_target__input_invalid_index__invalid_number) +BOOST_AUTO_TEST_CASE(parse__parse_target__input_invalid_number__invalid_number) { const std::string path = "/v3/input/0000000000000000000000000000000000000000000000000000000000000000/invalid"; request_t out{}; @@ -601,13 +594,6 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__input_script_valid__expected) BOOST_REQUIRE_EQUAL(index, 3u); } -BOOST_AUTO_TEST_CASE(parse__parse_target__input_script_invalid_subcomponent__invalid_subcomponent) -{ - const std::string path = "/v3/input/0000000000000000000000000000000000000000000000000000000000000000/3/invalid"; - request_t out{}; - BOOST_REQUIRE_EQUAL(parse_target(out, path), node::error::invalid_subcomponent); -} - BOOST_AUTO_TEST_CASE(parse__parse_target__input_script_extra_segment__extra_segment) { const std::string path = "/v3/input/0000000000000000000000000000000000000000000000000000000000000000/3/script/extra"; @@ -615,41 +601,6 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__input_script_extra_segment__extra_segm BOOST_REQUIRE_EQUAL(parse_target(out, path), node::error::extra_segment); } -// input_scripts - -////BOOST_AUTO_TEST_CASE(parse__parse_target__input_scripts_valid__expected) -////{ -//// const std::string path = "/v255/input/0000000000000000000000000000000000000000000000000000000000000042/scripts"; -//// -//// request_t request{}; -//// BOOST_REQUIRE(!parse_target(request, path)); -//// BOOST_REQUIRE_EQUAL(request.method, "input_scripts"); -//// BOOST_REQUIRE(request.params.has_value()); -//// -//// const auto& params = request.params.value(); -//// BOOST_REQUIRE(std::holds_alternative(params)); -//// -//// const auto& object = std::get(request.params.value()); -//// BOOST_REQUIRE_EQUAL(object.size(), 2u); -//// -//// const auto version = std::get(object.at("version").value()); -//// BOOST_REQUIRE_EQUAL(version, 255u); -//// -//// const auto& any = std::get(object.at("hash").value()); -//// BOOST_REQUIRE(any.holds_alternative()); -//// -//// const auto& hash_cptr = any.get(); -//// BOOST_REQUIRE(hash_cptr); -//// BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); -////} -//// -////BOOST_AUTO_TEST_CASE(parse__parse_target__input_scripts_extra_segment__extra_segment) -////{ -//// const std::string path = "/v3/input/0000000000000000000000000000000000000000000000000000000000000000/scripts/extra"; -//// request_t out{}; -//// BOOST_REQUIRE_EQUAL(parse_target(out, path), node::error::extra_segment); -////} - // input_witness BOOST_AUTO_TEST_CASE(parse__parse_target__input_witness_valid__expected) @@ -688,46 +639,11 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__input_witness_extra_segment__extra_seg BOOST_REQUIRE_EQUAL(parse_target(out, path), node::error::extra_segment); } -// input_witnesses - -////BOOST_AUTO_TEST_CASE(parse__parse_target__input_witnesses_valid__expected) -////{ -//// const std::string path = "/v255/input/0000000000000000000000000000000000000000000000000000000000000042/witnesses"; -//// -//// request_t request{}; -//// BOOST_REQUIRE(!parse_target(request, path)); -//// BOOST_REQUIRE_EQUAL(request.method, "input_witnesses"); -//// BOOST_REQUIRE(request.params.has_value()); -//// -//// const auto& params = request.params.value(); -//// BOOST_REQUIRE(std::holds_alternative(params)); -//// -//// const auto& object = std::get(request.params.value()); -//// BOOST_REQUIRE_EQUAL(object.size(), 2u); -//// -//// const auto version = std::get(object.at("version").value()); -//// BOOST_REQUIRE_EQUAL(version, 255u); -//// -//// const auto& any = std::get(object.at("hash").value()); -//// BOOST_REQUIRE(any.holds_alternative()); -//// -//// const auto& hash_cptr = any.get(); -//// BOOST_REQUIRE(hash_cptr); -//// BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); -////} -//// -////BOOST_AUTO_TEST_CASE(parse__parse_target__input_witnesses_extra_segment__extra_segment) -////{ -//// const std::string path = "/v3/input/0000000000000000000000000000000000000000000000000000000000000000/witnesses/extra"; -//// request_t out{}; -//// BOOST_REQUIRE_EQUAL(parse_target(out, path), node::error::extra_segment); -////} - // outputs BOOST_AUTO_TEST_CASE(parse__parse_target__outputs_valid__expected) { - const std::string path = "/v255/outputs/0000000000000000000000000000000000000000000000000000000000000042"; + const std::string path = "/v255/output/0000000000000000000000000000000000000000000000000000000000000042"; request_t request{}; BOOST_REQUIRE(!parse_target(request, path)); @@ -754,20 +670,20 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__outputs_valid__expected) BOOST_AUTO_TEST_CASE(parse__parse_target__outputs_missing_hash__missing_hash) { request_t out{}; - BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/outputs"), node::error::missing_hash); + BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/output"), node::error::missing_hash); } BOOST_AUTO_TEST_CASE(parse__parse_target__outputs_invalid_hash__invalid_hash) { request_t out{}; - BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/outputs/invalidhex"), node::error::invalid_hash); + BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/output/invalidhex"), node::error::invalid_hash); } -BOOST_AUTO_TEST_CASE(parse__parse_target__outputs_extra_segment__extra_segment) +BOOST_AUTO_TEST_CASE(parse__parse_target__outputs_invalid_number__invalid_number) { - const std::string path = "/v3/outputs/0000000000000000000000000000000000000000000000000000000000000000/extra"; + const std::string path = "/v3/output/0000000000000000000000000000000000000000000000000000000000000000/invalid"; request_t out{}; - BOOST_REQUIRE_EQUAL(parse_target(out, path), node::error::extra_segment); + BOOST_REQUIRE_EQUAL(parse_target(out, path), node::error::invalid_number); } // output @@ -801,14 +717,7 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__output_valid__expected) BOOST_REQUIRE_EQUAL(index, 3u); } -BOOST_AUTO_TEST_CASE(parse__parse_target__output_missing_component__missing_component) -{ - const std::string path = "/v3/output/0000000000000000000000000000000000000000000000000000000000000000"; - request_t out{}; - BOOST_REQUIRE_EQUAL(parse_target(out, path), node::error::missing_component); -} - -BOOST_AUTO_TEST_CASE(parse__parse_target__output_invalid_index__invalid_number) +BOOST_AUTO_TEST_CASE(parse__parse_target__output_invalid_number__invalid_number) { const std::string path = "/v3/output/0000000000000000000000000000000000000000000000000000000000000000/invalid"; request_t out{}; @@ -848,7 +757,7 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__output_script_valid__expected) BOOST_AUTO_TEST_CASE(parse__parse_target__output_script_invalid_subcomponent__invalid_subcomponent) { - const std::string path = "/v3/output/0000000000000000000000000000000000000000000000000000000000000000/3/invalid"; + const std::string path = "/v3/output/0000000000000000000000000000000000000000000000000000000000000000/3/extra"; request_t out{}; BOOST_REQUIRE_EQUAL(parse_target(out, path), node::error::invalid_subcomponent); } @@ -860,41 +769,6 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__output_script_extra_segment__extra_seg BOOST_REQUIRE_EQUAL(parse_target(out, path), node::error::extra_segment); } -// output_scripts - -////BOOST_AUTO_TEST_CASE(parse__parse_target__output_scripts_valid__expected) -////{ -//// const std::string path = "/v255/output/0000000000000000000000000000000000000000000000000000000000000042/scripts"; -//// -//// request_t request{}; -//// BOOST_REQUIRE(!parse_target(request, path)); -//// BOOST_REQUIRE_EQUAL(request.method, "output_scripts"); -//// BOOST_REQUIRE(request.params.has_value()); -//// -//// const auto& params = request.params.value(); -//// BOOST_REQUIRE(std::holds_alternative(params)); -//// -//// const auto& object = std::get(request.params.value()); -//// BOOST_REQUIRE_EQUAL(object.size(), 2u); -//// -//// const auto version = std::get(object.at("version").value()); -//// BOOST_REQUIRE_EQUAL(version, 255u); -//// -//// const auto& any = std::get(object.at("hash").value()); -//// BOOST_REQUIRE(any.holds_alternative()); -//// -//// const auto& hash_cptr = any.get(); -//// BOOST_REQUIRE(hash_cptr); -//// BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); -////} -//// -////BOOST_AUTO_TEST_CASE(parse__parse_target__output_scripts_extra_segment__extra_segment) -////{ -//// const std::string path = "/v3/output/0000000000000000000000000000000000000000000000000000000000000000/scripts/extra"; -//// request_t out{}; -//// BOOST_REQUIRE_EQUAL(parse_target(out, path), node::error::extra_segment); -////} - // output_spender BOOST_AUTO_TEST_CASE(parse__parse_target__output_spender_valid__expected) @@ -973,9 +847,9 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__output_spenders_extra_segment__extra_s // address -BOOST_AUTO_TEST_CASE(parse__parse_target__address_valid__expected) +BOOST_AUTO_TEST_CASE(parse__parse_target__address_valid__unreversed_expected) { - const std::string path = "/v255/address/0000000000000000000000000000000000000000000000000000000000000042"; + const std::string path = "/v255/address/4200000000000000000000000000000000000000000000000000000000000000"; request_t request{}; BOOST_REQUIRE(!parse_target(request, path)); @@ -1018,13 +892,13 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__address_extra_segment__extra_segment) BOOST_REQUIRE_EQUAL(parse_target(out, path), node::error::extra_segment); } -// filter/height +// block_filter/height -BOOST_AUTO_TEST_CASE(parse__parse_target__filter_height_valid__expected) +BOOST_AUTO_TEST_CASE(parse__parse_target__block_filter_height_valid__expected) { request_t request{}; BOOST_REQUIRE(!parse_target(request, "v42/block/height/123456/filter/255")); - BOOST_REQUIRE_EQUAL(request.method, "filter"); + BOOST_REQUIRE_EQUAL(request.method, "block_filter"); BOOST_REQUIRE(request.params.has_value()); const auto& params = request.params.value(); @@ -1043,21 +917,21 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__filter_height_valid__expected) BOOST_REQUIRE_EQUAL(type, 255u); } -BOOST_AUTO_TEST_CASE(parse__parse_target__filter_height_invalid_subcomponent__invalid_subcomponent) +BOOST_AUTO_TEST_CASE(parse__parse_target__block_filter_height_invalid_subcomponent__invalid_subcomponent) { request_t out{}; BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/block/height/123/filter/42/extra"), node::error::invalid_subcomponent); } -// filter/hash +// block_filter/hash -BOOST_AUTO_TEST_CASE(parse__parse_target__filter_hash_valid__expected) +BOOST_AUTO_TEST_CASE(parse__parse_target__block_filter_hash_valid__expected) { const std::string path = "/v42/block/hash/0000000000000000000000000000000000000000000000000000000000000042/filter/255"; request_t request{}; BOOST_REQUIRE(!parse_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "filter"); + BOOST_REQUIRE_EQUAL(request.method, "block_filter"); BOOST_REQUIRE(request.params.has_value()); const auto& params = request.params.value(); @@ -1080,20 +954,20 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__filter_hash_valid__expected) BOOST_REQUIRE_EQUAL(type, 255u); } -BOOST_AUTO_TEST_CASE(parse__parse_target__filter_hash_invalid_subcomponent__invalid_subcomponent) +BOOST_AUTO_TEST_CASE(parse__parse_target__block_filter_hash_invalid_subcomponent__invalid_subcomponent) { const std::string path = "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/filter/42/extra"; request_t out{}; BOOST_REQUIRE_EQUAL(parse_target(out, path), node::error::invalid_subcomponent); } -// filter_hash/height +// block_filter_hash/height -BOOST_AUTO_TEST_CASE(parse__parse_target__filter_hash_height_valid__expected) +BOOST_AUTO_TEST_CASE(parse__parse_target__block_filter_hash_height_valid__expected) { request_t request{}; BOOST_REQUIRE(!parse_target(request, "/v42/block/height/123456/filter/255/hash")); - BOOST_REQUIRE_EQUAL(request.method, "filter_hash"); + BOOST_REQUIRE_EQUAL(request.method, "block_filter_hash"); BOOST_REQUIRE(request.params.has_value()); const auto& params = request.params.value(); @@ -1112,21 +986,21 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__filter_hash_height_valid__expected) BOOST_REQUIRE_EQUAL(type, 255u); } -BOOST_AUTO_TEST_CASE(parse__parse_target__filter_hash_height_extra_segment__extra_segment) +BOOST_AUTO_TEST_CASE(parse__parse_target__block_filter_hash_height_extra_segment__extra_segment) { request_t out{}; BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/block/height/123/filter/42/hash/extra"), node::error::extra_segment); } -// filter_hash/hash +// block_filter_hash/hash -BOOST_AUTO_TEST_CASE(parse__parse_target__filter_hash_hash_valid__expected) +BOOST_AUTO_TEST_CASE(parse__parse_target__block_filter_hash_hash_valid__expected) { const std::string path = "/v42/block/hash/0000000000000000000000000000000000000000000000000000000000000042/filter/255/hash"; request_t request{}; BOOST_REQUIRE(!parse_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "filter_hash"); + BOOST_REQUIRE_EQUAL(request.method, "block_filter_hash"); BOOST_REQUIRE(request.params.has_value()); const auto& params = request.params.value(); @@ -1149,20 +1023,20 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__filter_hash_hash_valid__expected) BOOST_REQUIRE_EQUAL(type, 255u); } -BOOST_AUTO_TEST_CASE(parse__parse_target__filter_hash_hash_extra_segment__extra_segment) +BOOST_AUTO_TEST_CASE(parse__parse_target__block_filter_hash_hash_extra_segment__extra_segment) { const std::string path = "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/filter/42/hash/extra"; request_t out{}; BOOST_REQUIRE_EQUAL(parse_target(out, path), node::error::extra_segment); } -// filter_header/height +// block_filter_header/height -BOOST_AUTO_TEST_CASE(parse__parse_target__filter_header_height_valid__expected) +BOOST_AUTO_TEST_CASE(parse__parse_target__block_filter_header_height_valid__expected) { request_t request{}; BOOST_REQUIRE(!parse_target(request, "/v42/block/height/123456/filter/255/header")); - BOOST_REQUIRE_EQUAL(request.method, "filter_header"); + BOOST_REQUIRE_EQUAL(request.method, "block_filter_header"); BOOST_REQUIRE(request.params.has_value()); const auto& params = request.params.value(); @@ -1181,21 +1055,21 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__filter_header_height_valid__expected) BOOST_REQUIRE_EQUAL(type, 255u); } -BOOST_AUTO_TEST_CASE(parse__parse_target__filter_header_height_extra_segment__extra_segment) +BOOST_AUTO_TEST_CASE(parse__parse_target__block_filter_header_height_extra_segment__extra_segment) { request_t out{}; BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/block/height/123/filter/42/header/extra"), node::error::extra_segment); } -// filter_header/hash +// block_filter_header/hash -BOOST_AUTO_TEST_CASE(parse__parse_target__filter_header_hash_valid__expected) +BOOST_AUTO_TEST_CASE(parse__parse_target__block_filter_header_hash_valid__expected) { const std::string path = "/v42/block/hash/0000000000000000000000000000000000000000000000000000000000000042/filter/255/header"; request_t request{}; BOOST_REQUIRE(!parse_target(request, path)); - BOOST_REQUIRE_EQUAL(request.method, "filter_header"); + BOOST_REQUIRE_EQUAL(request.method, "block_filter_header"); BOOST_REQUIRE(request.params.has_value()); const auto& params = request.params.value(); @@ -1218,32 +1092,130 @@ BOOST_AUTO_TEST_CASE(parse__parse_target__filter_header_hash_valid__expected) BOOST_REQUIRE_EQUAL(type, 255u); } -BOOST_AUTO_TEST_CASE(parse__parse_target__filter_header_hash_extra_segment__extra_segment) +BOOST_AUTO_TEST_CASE(parse__parse_target__block_filter_header_hash_extra_segment__extra_segment) { const std::string path = "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/filter/42/header/extra"; request_t out{}; BOOST_REQUIRE_EQUAL(parse_target(out, path), node::error::extra_segment); } -BOOST_AUTO_TEST_CASE(parse__parse_target__filter_missing_type_id__missing_type_id) +BOOST_AUTO_TEST_CASE(parse__parse_target__block_filter_missing_type_id__missing_type_id) { request_t out{}; BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/block/height/123/filter"), node::error::missing_type_id); BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/filter"), node::error::missing_type_id); } -BOOST_AUTO_TEST_CASE(parse__parse_target__filter_invalid_type__invalid_number) +BOOST_AUTO_TEST_CASE(parse__parse_target__block_filter_invalid_type__invalid_number) { request_t out{}; BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/block/height/123/filter/invalid"), node::error::invalid_number); BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/filter/invalid"), node::error::invalid_number); } -BOOST_AUTO_TEST_CASE(parse__parse_target__filter_invalid_subcomponent__invalid_subcomponent) +BOOST_AUTO_TEST_CASE(parse__parse_target__block_filter_invalid_subcomponent__invalid_subcomponent) { request_t out{}; BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/block/height/123/filter/42/invalid"), node::error::invalid_subcomponent); BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/filter/42/invalid"), node::error::invalid_subcomponent); } +// tx_fee + +BOOST_AUTO_TEST_CASE(parse__parse_target__tx_fee_valid__expected) +{ + const std::string path = "/v42/tx/0000000000000000000000000000000000000000000000000000000000000042/fee"; + + request_t request{}; + BOOST_REQUIRE(!parse_target(request, path)); + BOOST_REQUIRE_EQUAL(request.method, "tx_fee"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 2u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 42u); + + const auto& any = std::get(object.at("hash").value()); + BOOST_REQUIRE(any.holds_alternative()); + + const auto& hash_cptr = any.get(); + BOOST_REQUIRE(hash_cptr); + BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); +} + +BOOST_AUTO_TEST_CASE(parse__parse_target__tx_fee_extra_segment__extra_segment) +{ + const std::string path = "/v3/tx/0000000000000000000000000000000000000000000000000000000000000000/fee/extra"; + request_t out{}; + BOOST_REQUIRE_EQUAL(parse_target(out, path), node::error::extra_segment); +} + +// block_fees/height + +BOOST_AUTO_TEST_CASE(parse__parse_target__block_fees_height_valid__expected) +{ + request_t request{}; + BOOST_REQUIRE(!parse_target(request, "/v42/block/height/123456/fees")); + BOOST_REQUIRE_EQUAL(request.method, "block_fees"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 2u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 42u); + + const auto height = std::get(object.at("height").value()); + BOOST_REQUIRE_EQUAL(height, 123456u); +} + +BOOST_AUTO_TEST_CASE(parse__parse_target__block_fees_height_extra_segment__extra_segment) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(parse_target(out, "/v3/block/height/123/fees/extra"), node::error::extra_segment); +} + +// block_fees/hash + +BOOST_AUTO_TEST_CASE(parse__parse_target__block_fees_hash_valid__expected) +{ + const std::string path = "/v42/block/hash/0000000000000000000000000000000000000000000000000000000000000042/fees"; + + request_t request{}; + BOOST_REQUIRE(!parse_target(request, path)); + BOOST_REQUIRE_EQUAL(request.method, "block_fees"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 2u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 42u); + + const auto& any = std::get(object.at("hash").value()); + BOOST_REQUIRE(any.holds_alternative()); + + const auto& hash_cptr = any.get(); + BOOST_REQUIRE(hash_cptr); + BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); +} + +BOOST_AUTO_TEST_CASE(parse__parse_target__block_fees_hash_extra_segment__extra_segment) +{ + const std::string path = "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/fees/extra"; + request_t out{}; + BOOST_REQUIRE_EQUAL(parse_target(out, path), node::error::extra_segment); +} + BOOST_AUTO_TEST_SUITE_END()