diff --git a/Makefile.am b/Makefile.am
index 00490a40..3b15bee8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -40,7 +40,6 @@ src_libbitcoin_server_la_SOURCES = \
src/parser.cpp \
src/server_node.cpp \
src/settings.cpp \
- src/parsers/bitcoind_query.cpp \
src/parsers/bitcoind_target.cpp \
src/parsers/electrum_version.cpp \
src/parsers/native_query.cpp \
@@ -89,7 +88,6 @@ test_libbitcoin_server_test_SOURCES = \
test/test.hpp \
test/mocks/blocks.cpp \
test/mocks/blocks.hpp \
- test/parsers/bitcoind_query.cpp \
test/parsers/bitcoind_target.cpp \
test/parsers/electrum_version.cpp \
test/parsers/native_query.cpp \
@@ -209,7 +207,6 @@ include_bitcoin_server_interfaces_HEADERS = \
include_bitcoin_server_parsersdir = ${includedir}/bitcoin/server/parsers
include_bitcoin_server_parsers_HEADERS = \
- include/bitcoin/server/parsers/bitcoind_query.hpp \
include/bitcoin/server/parsers/bitcoind_target.hpp \
include/bitcoin/server/parsers/electrum_version.hpp \
include/bitcoin/server/parsers/native_query.hpp \
diff --git a/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj b/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj
index 1dda035c..d70fcf34 100644
--- a/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj
+++ b/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj
@@ -122,7 +122,6 @@
-
$(IntDir)test_parsers_electrum_version.obj
diff --git a/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters
index 1c804ffb..329d44ac 100644
--- a/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters
+++ b/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters
@@ -42,9 +42,6 @@
src\mocks
-
- src\parsers
-
src\parsers
diff --git a/builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj b/builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj
index 402eb343..8f6729ed 100644
--- a/builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj
+++ b/builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj
@@ -124,7 +124,6 @@
-
@@ -179,7 +178,6 @@
-
diff --git a/builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj.filters
index 55bc05f6..63736a31 100644
--- a/builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj.filters
+++ b/builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj.filters
@@ -72,9 +72,6 @@
src
-
- src\parsers
-
src\parsers
@@ -233,9 +230,6 @@
include\bitcoin\server
-
- include\bitcoin\server\parsers
-
include\bitcoin\server\parsers
diff --git a/builds/msvc/vs2026/libbitcoin-server-test/libbitcoin-server-test.vcxproj b/builds/msvc/vs2026/libbitcoin-server-test/libbitcoin-server-test.vcxproj
index e8cf72c3..cdd024ce 100644
--- a/builds/msvc/vs2026/libbitcoin-server-test/libbitcoin-server-test.vcxproj
+++ b/builds/msvc/vs2026/libbitcoin-server-test/libbitcoin-server-test.vcxproj
@@ -122,7 +122,6 @@
-
$(IntDir)test_parsers_electrum_version.obj
diff --git a/builds/msvc/vs2026/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters b/builds/msvc/vs2026/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters
index 1c804ffb..329d44ac 100644
--- a/builds/msvc/vs2026/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters
+++ b/builds/msvc/vs2026/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters
@@ -42,9 +42,6 @@
src\mocks
-
- src\parsers
-
src\parsers
diff --git a/builds/msvc/vs2026/libbitcoin-server/libbitcoin-server.vcxproj b/builds/msvc/vs2026/libbitcoin-server/libbitcoin-server.vcxproj
index 6ad51e16..971b213f 100644
--- a/builds/msvc/vs2026/libbitcoin-server/libbitcoin-server.vcxproj
+++ b/builds/msvc/vs2026/libbitcoin-server/libbitcoin-server.vcxproj
@@ -124,7 +124,6 @@
-
@@ -179,7 +178,6 @@
-
diff --git a/builds/msvc/vs2026/libbitcoin-server/libbitcoin-server.vcxproj.filters b/builds/msvc/vs2026/libbitcoin-server/libbitcoin-server.vcxproj.filters
index 55bc05f6..63736a31 100644
--- a/builds/msvc/vs2026/libbitcoin-server/libbitcoin-server.vcxproj.filters
+++ b/builds/msvc/vs2026/libbitcoin-server/libbitcoin-server.vcxproj.filters
@@ -72,9 +72,6 @@
src
-
- src\parsers
-
src\parsers
@@ -233,9 +230,6 @@
include\bitcoin\server
-
- include\bitcoin\server\parsers
-
include\bitcoin\server\parsers
diff --git a/include/bitcoin/server.hpp b/include/bitcoin/server.hpp
index a5dd0928..1e1f935f 100644
--- a/include/bitcoin/server.hpp
+++ b/include/bitcoin/server.hpp
@@ -37,7 +37,6 @@
#include
#include
#include
-#include
#include
#include
#include
diff --git a/include/bitcoin/server/parsers/bitcoind_query.hpp b/include/bitcoin/server/parsers/bitcoind_query.hpp
deleted file mode 100644
index 9444c457..00000000
--- a/include/bitcoin/server/parsers/bitcoind_query.hpp
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * Copyright (c) 2011-2026 libbitcoin developers (see AUTHORS)
- *
- * This file is part of libbitcoin.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-#ifndef LIBBITCOIN_SERVER_PARSERS_BITCOIND_QUERY_HPP
-#define LIBBITCOIN_SERVER_PARSERS_BITCOIND_QUERY_HPP
-
-#include
-
-namespace libbitcoin {
-namespace server {
-
-BCS_API bool bitcoind_query(network::rpc::request_t& out,
- const network::http::request& request) NOEXCEPT;
-
-} // namespace server
-} // namespace libbitcoin
-
-#endif
diff --git a/include/bitcoin/server/parsers/parsers.hpp b/include/bitcoin/server/parsers/parsers.hpp
index a958d087..04c1de08 100644
--- a/include/bitcoin/server/parsers/parsers.hpp
+++ b/include/bitcoin/server/parsers/parsers.hpp
@@ -19,7 +19,6 @@
#ifndef LIBBITCOIN_SERVER_PARSERS_PARSERS_HPP
#define LIBBITCOIN_SERVER_PARSERS_PARSERS_HPP
-#include
#include
#include
#include
diff --git a/include/bitcoin/server/protocols/protocol_bitcoind_rpc.hpp b/include/bitcoin/server/protocols/protocol_bitcoind_rpc.hpp
index 6becf52e..6cbfbb16 100644
--- a/include/bitcoin/server/protocols/protocol_bitcoind_rpc.hpp
+++ b/include/bitcoin/server/protocols/protocol_bitcoind_rpc.hpp
@@ -24,7 +24,6 @@
#include
#include
#include
-#include
namespace libbitcoin {
namespace server {
@@ -148,6 +147,11 @@ class BCS_API protocol_bitcoind_rpc
const network::rpc::id_option& id,
const network::http::request_cptr& request) NOEXCEPT;
+ // Validate a transaction given next block context.
+ bool get_pool_context(system::chain::context& pool) const NOEXCEPT;
+ code validate_tx(const system::chain::transaction& tx) const NOEXCEPT;
+ code broadcast_tx(const system::chain::transaction::cptr& tx) NOEXCEPT;
+
// Obtain cached request and clear cache (requires strand).
network::http::request_cptr reset_rpc_request() NOEXCEPT;
diff --git a/src/parsers/bitcoind_query.cpp b/src/parsers/bitcoind_query.cpp
deleted file mode 100644
index 8e32b05a..00000000
--- a/src/parsers/bitcoind_query.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * Copyright (c) 2011-2026 libbitcoin developers (see AUTHORS)
- *
- * This file is part of libbitcoin.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-#include
-
-#include
-
-namespace libbitcoin {
-namespace server {
-
-using namespace system;
-using namespace network;
-using namespace network::http;
-
-BC_PUSH_WARNING(NO_ARRAY_INDEXING)
-BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
-
-bool bitcoind_query(rpc::request_t& , const request& ) NOEXCEPT
-{
- return false;
-}
-
-BC_POP_WARNING()
-BC_POP_WARNING()
-
-} // namespace server
-} // namespace libbitcoin
diff --git a/src/parsers/bitcoind_target.cpp b/src/parsers/bitcoind_target.cpp
index 0c4b13c8..7b91bb72 100644
--- a/src/parsers/bitcoind_target.cpp
+++ b/src/parsers/bitcoind_target.cpp
@@ -20,8 +20,6 @@
#include
#include
-#include
-#include
#include
#include
@@ -42,7 +40,7 @@ static hash_cptr to_hash(const std::string_view& token) NOEXCEPT
to_shared(std::move(out)) : hash_cptr{};
}
-// Map a Bitcoin Core REST file extension to a media value.
+// Map a bitcoind REST file extension to a media value.
static bool to_media(uint8_t& out, const std::string_view& extension) NOEXCEPT
{
if (extension == "bin")
@@ -87,7 +85,7 @@ static bool split_leaf(std::string& name, uint8_t& media,
return to_media(media, parts.back());
}
-// Parse a Bitcoin Core REST path into a json-rpc request model.
+// Parse a bitcoind REST path into a json-rpc request model.
// github.com/bitcoin/bitcoin/blob/master/doc/REST-interface.md
// Supports: block, block/notxdetails, blockhashbyheight, headers, blockpart,
// chaininfo (remaining endpoints return invalid_target until implemented).
@@ -116,7 +114,7 @@ code bitcoind_target(request_t& out, const std::string_view& path) NOEXCEPT
size_t segment{};
- // Accept an optional "rest" prefix (Core mounts endpoints under /rest/).
+ // Accept an optional "rest" prefix (bitcoind mounts endpoints under /rest/).
if (segments[segment] == "rest")
++segment;
@@ -239,7 +237,7 @@ code bitcoind_target(request_t& out, const std::string_view& path) NOEXCEPT
"block_filter_headers";
params["media"] = media;
params["hash"] = hash;
- params["type"] = uint8_t{ 0 };
+ params["type"] = 0_u8;
return error::success;
}
diff --git a/src/protocols/bitcoind/protocol_bitcoind_rest.cpp b/src/protocols/bitcoind/protocol_bitcoind_rest.cpp
index 879f1061..e56b353d 100644
--- a/src/protocols/bitcoind/protocol_bitcoind_rest.cpp
+++ b/src/protocols/bitcoind/protocol_bitcoind_rest.cpp
@@ -20,12 +20,9 @@
#include
#include
-#include
-#include
#include
#include
#include
-#include
namespace libbitcoin {
namespace server {
@@ -35,8 +32,8 @@ namespace server {
subscribe(&CLASS::method, __VA_ARGS__)
using namespace system;
+using namespace network;
using namespace network::rpc;
-using namespace network::http;
using namespace std::placeholders;
using namespace boost::json;
@@ -118,9 +115,9 @@ void protocol_bitcoind_rest::handle_receive_get(const code& ec,
// Handlers.
// ----------------------------------------------------------------------------
-constexpr auto data = to_value(media_type::application_octet_stream);
-constexpr auto json = to_value(media_type::application_json);
-constexpr auto text = to_value(media_type::text_plain);
+constexpr auto data = to_value(http::media_type::application_octet_stream);
+constexpr auto json = to_value(http::media_type::application_json);
+constexpr auto text = to_value(http::media_type::text_plain);
template
data_chunk to_data(const Object& object, size_t size, Args&&... args) NOEXCEPT
@@ -143,7 +140,7 @@ std::string to_text(const Object& object, size_t size, Args&&... args) NOEXCEPT
}
bool protocol_bitcoind_rest::handle_get_block(const code& ec,
- rest_interface::block, uint8_t media, const system::hash_cptr& hash) NOEXCEPT
+ rest_interface::block, uint8_t media, const hash_cptr& hash) NOEXCEPT
{
if (stopped(ec))
return false;
@@ -205,7 +202,7 @@ bool protocol_bitcoind_rest::handle_get_block_hash(const code& ec,
send_text(encode_base16(hash));
return true;
case json:
- send_json(boost::json::object{ { "blockhash", encode_hash(hash) } },
+ send_json(object{ { "blockhash", encode_hash(hash) } },
two * hash_size);
return true;
}
@@ -215,7 +212,7 @@ bool protocol_bitcoind_rest::handle_get_block_hash(const code& ec,
}
bool protocol_bitcoind_rest::handle_get_block_txs(const code& ec,
- rest_interface::block_txs, uint8_t media, const system::hash_cptr& hash) NOEXCEPT
+ rest_interface::block_txs, uint8_t media, const hash_cptr& hash) NOEXCEPT
{
if (stopped(ec))
return false;
@@ -255,7 +252,7 @@ bool protocol_bitcoind_rest::handle_get_block_txs(const code& ec,
}
bool protocol_bitcoind_rest::handle_get_block_headers(const code& ec,
- rest_interface::block_headers, uint8_t media, const system::hash_cptr& hash,
+ rest_interface::block_headers, uint8_t media, const hash_cptr& hash,
uint32_t count) NOEXCEPT
{
if (stopped(ec))
@@ -275,10 +272,9 @@ bool protocol_bitcoind_rest::handle_get_block_headers(const code& ec,
return true;
}
- // Core caps the header count at 2000.
- constexpr size_t maximum = 2000;
+ constexpr size_t maximum_headers = 2000;
constexpr auto header_size = chain::header::serialized_size();
- const auto limit = std::min(possible_wide_cast(count), maximum);
+ const auto limit = lesser(count, maximum_headers);
const auto links = query.get_confirmed_headers(height, limit);
if (links.empty())
{
@@ -324,7 +320,7 @@ bool protocol_bitcoind_rest::handle_get_block_headers(const code& ec,
}
case json:
{
- boost::json::array out{};
+ array out{};
out.reserve(links.size());
for (const auto& link: links)
{
@@ -348,7 +344,7 @@ bool protocol_bitcoind_rest::handle_get_block_headers(const code& ec,
}
bool protocol_bitcoind_rest::handle_get_block_part(const code& ec,
- rest_interface::block_part, uint8_t media, const system::hash_cptr& hash,
+ rest_interface::block_part, uint8_t media, const hash_cptr& hash,
uint32_t offset, uint32_t size) NOEXCEPT
{
if (stopped(ec))
@@ -376,8 +372,9 @@ bool protocol_bitcoind_rest::handle_get_block_part(const code& ec,
return true;
}
- const auto stop = std::min(ceilinged_add(offset, size), full.size());
- data_chunk part{ std::next(full.begin(), offset), std::next(full.begin(), stop) };
+ const auto begin = full.begin();
+ const auto stop = lesser(ceilinged_add(offset, size), full.size());
+ data_chunk part{ std::next(begin, offset), std::next(begin, stop) };
switch (media)
{
case data:
@@ -395,7 +392,7 @@ bool protocol_bitcoind_rest::handle_get_block_part(const code& ec,
bool protocol_bitcoind_rest::handle_get_block_spent_tx_outputs(const code& ec,
rest_interface::block_spent_tx_outputs, uint8_t media,
- const system::hash_cptr& hash) NOEXCEPT
+ const hash_cptr& hash) NOEXCEPT
{
if (stopped(ec))
return false;
@@ -418,7 +415,7 @@ bool protocol_bitcoind_rest::handle_get_block_spent_tx_outputs(const code& ec,
// Resolve every prevout spent by the block's non-coinbase transactions.
chain::output_cptrs spent{};
const auto& txs = *block->transactions_ptr();
- for (size_t tx = 1; tx < txs.size(); ++tx)
+ for (auto tx = one; tx < txs.size(); ++tx)
for (const auto& in: *txs.at(tx)->inputs_ptr())
if (const auto out = query.get_output(query.to_output(in->point())))
spent.push_back(out);
@@ -461,7 +458,7 @@ bool protocol_bitcoind_rest::handle_get_block_spent_tx_outputs(const code& ec,
}
bool protocol_bitcoind_rest::handle_get_block_filter(const code& ec,
- rest_interface::block_filter, uint8_t media, const system::hash_cptr& hash,
+ rest_interface::block_filter, uint8_t media, const hash_cptr& hash,
uint8_t) NOEXCEPT
{
if (stopped(ec))
@@ -491,7 +488,7 @@ bool protocol_bitcoind_rest::handle_get_block_filter(const code& ec,
send_text(encode_base16(filter));
return true;
case json:
- send_json(boost::json::object
+ send_json(object
{
{ "filter", encode_base16(filter) }
}, two * filter.size());
@@ -503,7 +500,7 @@ bool protocol_bitcoind_rest::handle_get_block_filter(const code& ec,
}
bool protocol_bitcoind_rest::handle_get_block_filter_headers(const code& ec,
- rest_interface::block_filter_headers, uint8_t media, const system::hash_cptr& hash,
+ rest_interface::block_filter_headers, uint8_t media, const hash_cptr& hash,
uint8_t) NOEXCEPT
{
if (stopped(ec))
@@ -532,7 +529,7 @@ bool protocol_bitcoind_rest::handle_get_block_filter_headers(const code& ec,
send_text(encode_base16(filter_head));
return true;
case json:
- send_json(boost::json::object
+ send_json(object
{
{ "filter_header", encode_hash(filter_head) }
}, two * hash_size);
@@ -559,7 +556,7 @@ bool protocol_bitcoind_rest::handle_get_chain_information(const code& ec,
return true;
}
- send_json(boost::json::object
+ send_json(object
{
{ "chain", chain_name(query) },
{ "blocks", blocks },
@@ -580,13 +577,14 @@ bool protocol_bitcoind_rest::handle_get_chain_information(const code& ec,
void protocol_bitcoind_rest::send_data(data_chunk&& bytes) NOEXCEPT
{
BC_ASSERT(stranded());
+ using namespace http;
+ static const auto data = from_media_type(
+ media_type::application_octet_stream);
const auto request = reset_request();
- network::http::response message{ network::http::status::ok,
- request->version() };
+ http::response message{ status::ok, request->version() };
add_common_headers(message, *request);
add_access_control_headers(message, *request);
- message.set(network::http::field::content_type, network::http::
- from_media_type(media_type::application_octet_stream));
+ message.set(http::field::content_type, data);
message.body() = std::move(bytes);
message.prepare_payload();
SEND(std::move(message), handle_complete, _1, error::success);
@@ -595,30 +593,30 @@ void protocol_bitcoind_rest::send_data(data_chunk&& bytes) NOEXCEPT
void protocol_bitcoind_rest::send_text(std::string&& text) NOEXCEPT
{
BC_ASSERT(stranded());
+ using namespace http;
+ static const auto plain = from_media_type(media_type::text_plain);
const auto request = reset_request();
- network::http::response message{ network::http::status::ok,
- request->version() };
+ http::response message{ status::ok, request->version() };
add_common_headers(message, *request);
add_access_control_headers(message, *request);
- message.set(network::http::field::content_type, network::http::
- from_media_type(media_type::text_plain));
+ message.set(field::content_type, plain);
message.body() = std::move(text);
message.prepare_payload();
SEND(std::move(message), handle_complete, _1, error::success);
}
-void protocol_bitcoind_rest::send_json(boost::json::value&& model,
+void protocol_bitcoind_rest::send_json(value&& model,
size_t size_hint) NOEXCEPT
{
BC_ASSERT(stranded());
+ using namespace http;
+ static const auto json = from_media_type(media_type::application_json);
const auto request = reset_request();
- network::http::response message{ network::http::status::ok,
- request->version() };
+ http::response message{ status::ok, request->version() };
add_common_headers(message, *request);
add_access_control_headers(message, *request);
- message.set(network::http::field::content_type, network::http::
- from_media_type(media_type::application_json));
- message.body() = network::http::json_value
+ message.set(field::content_type, json);
+ message.body() = json_value
{
.model = std::move(model),
.size_hint = size_hint
diff --git a/src/protocols/bitcoind/protocol_bitcoind_rpc.cpp b/src/protocols/bitcoind/protocol_bitcoind_rpc.cpp
index 31db91ba..ec1256af 100644
--- a/src/protocols/bitcoind/protocol_bitcoind_rpc.cpp
+++ b/src/protocols/bitcoind/protocol_bitcoind_rpc.cpp
@@ -18,10 +18,10 @@
*/
#include
+#include
+#include
#include
#include
-#include
-#include
namespace libbitcoin {
namespace server {
@@ -32,8 +32,10 @@ namespace server {
using namespace system;
using namespace network;
-using namespace network::json;
+using namespace network::rpc;
+using namespace network::messages;
using namespace std::placeholders;
+using namespace boost::json;
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED)
@@ -133,7 +135,7 @@ void protocol_bitcoind_rpc::handle_receive_post(const code& ec,
}
// Endpoint accepts only json-rpc posts.
- if (!post->body().contains())
+ if (!post->body().contains())
{
send_bad_request(*post);
return;
@@ -142,7 +144,7 @@ void protocol_bitcoind_rpc::handle_receive_post(const code& ec,
// Get the parsed json-rpc request object.
// v1 or v2 both supported, batch not yet supported.
// v1 null id and v2 missing id implies notification and no response.
- const auto& message = post->body().get().message;
+ const auto& message = post->body().get().message;
// The post is saved off during asynchonous handling and used in send_json
// to formulate response headers, isolating handlers from http semantics.
@@ -194,11 +196,18 @@ bool protocol_bitcoind_rpc::handle_get_block(const code& ec,
return true;
}
+ size_t level{};
+ if (!to_integer(level, verbosity))
+ {
+ send_error(error::invalid_argument);
+ return true;
+ }
+
constexpr auto witness = true;
const auto& query = archive();
const auto link = query.to_header(hash);
- if (verbosity == 0.0)
+ if (level == zero)
{
const auto block = query.get_block(link, witness);
if (!block)
@@ -211,7 +220,7 @@ bool protocol_bitcoind_rpc::handle_get_block(const code& ec,
return true;
}
- if (verbosity == 1.0 || verbosity == 2.0)
+ if (level == one || level == two)
{
const auto block = query.get_block(link, witness);
if (!block)
@@ -220,14 +229,14 @@ bool protocol_bitcoind_rpc::handle_get_block(const code& ec,
return true;
}
+ // TODO: map "level/verbosity" to enumeration and remove comments.
// verbosity 1 lists txids; verbosity 2 embeds full tx objects.
- auto model = (verbosity == 1.0) ?
+ auto model = is_one(level) ?
value_from(bitcoind_hashed(*block)) :
value_from(bitcoind_verbose(*block));
inject_block_context(model.as_object(), query, link, block->header());
- send_result(rpc::value_t(std::move(model)),
- two * block->serialized_size(witness));
+ send_result(std::move(model), two * block->serialized_size(witness));
return true;
}
@@ -252,26 +261,28 @@ bool protocol_bitcoind_rpc::handle_get_block_chain_info(const code& ec,
return true;
}
+ // TODO: make utility method and move explanation there.
// verificationprogress is approximated as confirmed/candidate height, the
// best available estimate of the chain tip during sync (1.0 once current).
const auto progress = is_zero(headers) ? 1.0 :
std::min(1.0, static_cast(blocks) /
static_cast(headers));
- send_result(rpc::object_t
+ // TODO: blocks/headers is a misnomer (off-by-one), intended?
+ using namespace chain;
+ send_result(object_t
{
{ "chain", chain_name(query) },
{ "blocks", possible_wide_cast(blocks) },
{ "headers", possible_wide_cast(headers) },
{ "bestblockhash", encode_hash(query.get_header_key(link)) },
{ "bits", encode_base16(to_big_endian(header->bits())) },
- { "target", encode_hash(from_uintx(
- chain::compact::expand(header->bits()))) },
+ { "target", encode_hash(from_uintx(compact::expand(header->bits()))) },
{ "difficulty", header->difficulty() },
{ "time", header->timestamp() },
{ "mediantime", median_time_past(query, link) },
{ "verificationprogress", progress },
- { "initialblockdownload", !is_current(true) },
+ { "initialblockdownload", !is_current_chain(true) },
{ "pruned", false },
{ "warnings", std::string{} }
}, 512);
@@ -284,8 +295,8 @@ bool protocol_bitcoind_rpc::handle_get_block_count(const code& ec,
if (stopped(ec))
return false;
- send_result(rpc::value_t(possible_wide_cast(
- archive().get_top_confirmed())), 20);
+ const auto top = archive().get_top_confirmed();
+ send_result(possible_wide_cast(top), 20);
return true;
}
@@ -320,7 +331,7 @@ bool protocol_bitcoind_rpc::handle_get_block_filter(const code& ec,
return true;
}
- send_result(rpc::object_t
+ send_result(object_t
{
{ "filter", encode_base16(filter) },
{ "header", encode_hash(filter_header) }
@@ -334,14 +345,15 @@ bool protocol_bitcoind_rpc::handle_get_block_hash(const code& ec,
if (stopped(ec))
return false;
- if (height < 0.0)
+ size_t block_height{};
+ if (!to_integer(block_height, height))
{
send_error(error::invalid_argument);
return true;
}
const auto& query = archive();
- const auto link = query.to_confirmed(static_cast(height));
+ const auto link = query.to_confirmed(block_height);
if (link.is_terminal())
{
send_error(error::not_found);
@@ -384,13 +396,12 @@ bool protocol_bitcoind_rpc::handle_get_block_header(const code& ec,
auto out = header_to_bitcoind(*header);
out["nTx"] = query.get_tx_count(link);
inject_block_context(out, query, link, *header);
- send_result(rpc::value_t(boost::json::value(std::move(out))), 512);
+ send_result(value{ std::move(out) }, 512);
return true;
}
bool protocol_bitcoind_rpc::handle_get_block_stats(const code& ec,
- rpc_interface::get_block_stats, const network::rpc::value_t&,
- const network::rpc::array_t&) NOEXCEPT
+ rpc_interface::get_block_stats, const value_t&, const array_t&) NOEXCEPT
{
if (stopped(ec)) return false;
send_error(error::not_implemented);
@@ -420,45 +431,57 @@ bool protocol_bitcoind_rpc::handle_get_tx_out(const code& ec,
if (stopped(ec))
return false;
+ uint32_t index{};
hash_digest hash{};
- if (!decode_hash(hash, txid) || n < 0.0)
+ if (!decode_hash(hash, txid) || !to_integer(index, n))
{
send_error(error::invalid_argument);
return true;
}
const auto& query = archive();
- const auto index = static_cast(n);
- const auto output_fk = query.to_output(hash, index);
+ const auto output_link = query.to_output(hash, index);
- // Core returns json null for a missing or spent output (mempool ignored).
- if (output_fk.is_terminal() || query.is_spent(output_fk))
+ // TODO: is this meant to be query.is_confirmed_spent(output_link)?
+ // bitcoind returns json null for missing or spent output (mempool ignored).
+ if (output_link.is_terminal() || query.is_spent(output_link))
{
- send_result(rpc::value_t{}, 4);
+ send_result({}, 42);
return true;
}
- const auto output = query.get_output(output_fk);
+ const auto output = query.get_output(output_link);
if (!output)
{
- send_result(rpc::value_t{}, 4);
+ send_result({}, 42);
return true;
}
- const auto tx_fk = query.to_tx(hash);
- size_t tx_height{};
- const auto have_height = query.get_tx_height(tx_height, tx_fk);
+ // Output's tx must exist.
+ const auto tx_link = query.to_tx(hash);
+ if (tx_link.is_terminal())
+ {
+ send_error(error::server_error);
+ return true;
+ }
+
+ // Derive header from top for consistent depth result (also cheaper).
const auto top = query.get_top_confirmed();
+ const auto header_link = query.to_confirmed(top);
- send_result(rpc::object_t
+ size_t height{};
+ const auto strong = query.get_tx_height(height, tx_link);
+ const auto depth = strong ? add1(floored_subtract(top, height)) : 0_size;
+ const auto coins = to_floating(output->value()) /
+ chain::satoshi_per_bitcoin;
+
+ send_result(object_t
{
- { "bestblock", encode_hash(query.get_top_confirmed_hash()) },
- { "confirmations", have_height ?
- possible_sign_cast(add1(floored_subtract(top, tx_height))) :
- 0_i64 },
- { "value", static_cast(output->value()) / 100000000.0 },
+ { "bestblock", encode_hash(query.get_header_key(header_link)) },
+ { "confirmations", depth },
+ { "value", coins },
{ "scriptPubKey", value_from(bitcoind(output->script())) },
- { "coinbase", query.is_coinbase(tx_fk) }
+ { "coinbase", query.is_coinbase(tx_link) }
}, 256);
return true;
}
@@ -489,7 +512,7 @@ bool protocol_bitcoind_rpc::handle_save_mem_pool(const code& ec,
bool protocol_bitcoind_rpc::handle_scan_tx_out_set(const code& ec,
rpc_interface::scan_tx_out_set, const std::string&,
- const network::rpc::array_t&) NOEXCEPT
+ const array_t&) NOEXCEPT
{
if (stopped(ec)) return false;
send_error(error::not_implemented);
@@ -518,10 +541,11 @@ bool protocol_bitcoind_rpc::handle_get_network_info(const code& ec,
if (stopped(ec))
return false;
+ // TODO: get most of these values from either config or network/node props.
+
// libbitcoin-server is a node, not a wallet/peer-introspection service;
// peer-dependent fields (connections, addresses) are reported as empty.
- // TODO: surface live connection count and relay fee from node settings.
- send_result(rpc::object_t
+ send_result(object_t
{
{ "version", 0 },
{ "subversion", std::string{ "/libbitcoin:server/" } },
@@ -530,10 +554,10 @@ bool protocol_bitcoind_rpc::handle_get_network_info(const code& ec,
{ "timeoffset", 0 },
{ "connections", 0 },
{ "networkactive", true },
- { "networks", rpc::array_t{} },
+ { "networks", array_t{} },
{ "relayfee", 0.00001 },
{ "incrementalfee", 0.00001 },
- { "localaddresses", rpc::array_t{} },
+ { "localaddresses", array_t{} },
{ "warnings", std::string{} }
}, 256);
return true;
@@ -567,19 +591,19 @@ bool protocol_bitcoind_rpc::handle_get_raw_transaction(const code& ec,
return true;
}
+ // TODO: can verbose be validated, to_integer()?
if (verbose == 0.0)
{
send_text(to_text(*tx, tx->serialized_size(witness), witness));
return true;
}
- // bitcoind() (not bitcoind_verbose) yields Core's tx fields: txid/hash/
+ // bitcoind() (not bitcoind_verbose) yields bitcoind's tx fields: txid/hash/
// size/vsize/weight/vin/vout/hex (bitcoind_verbose on a standalone tx
// falls back to libbitcoin's plain inputs/outputs form).
auto model = value_from(bitcoind(*tx));
inject_tx_context(model.as_object(), query, link);
- send_result(rpc::value_t(std::move(model)),
- two * tx->serialized_size(witness));
+ send_result(std::move(model), two * tx->serialized_size(witness));
return true;
}
@@ -597,39 +621,47 @@ bool protocol_bitcoind_rpc::handle_send_raw_transaction(const code& ec,
return true;
}
- const auto tx = std::make_shared(data, true);
+ const auto tx = to_shared(data, true);
if (!tx->is_valid())
{
send_error(error::invalid_argument);
return true;
}
- auto& query = archive();
- const auto txid = tx->hash(false);
-
- // Archive (so the out-relay can serve getdata) only if not already known.
- // TODO: contextual validation (populate_with_metadata + connect) for policy.
- if (query.to_tx(txid).is_terminal())
- {
- if (tx->check())
- {
- send_error(error::invalid_argument);
- return true;
- }
-
- if (query.set_code(*tx))
- {
- send_error(database::error::integrity);
- return true;
- }
+ // Tx archive not allowed in in v4, must move through node::tx_chaser (v5).
+ ////auto& query = archive();
+ ////const auto hash = tx->hash(false);
+ ////
+ ////// Archive (so the out-relay can serve getdata) only if not already known.
+ ////// TODO: contextual validation (populate_with_metadata + connect) for policy.
+ ////if (query.to_tx(hash).is_terminal())
+ ////{
+ //// if (tx->check())
+ //// {
+ //// send_error(error::invalid_argument);
+ //// return true;
+ //// }
+ ////
+ //// if (query.set_code(*tx))
+ //// {
+ //// send_error(database::error::integrity);
+ //// return true;
+ //// }
+ ////}
+
+ // Full validation (TODO above) handled in broadcast_tx() below.
+ ////// Announce to peers; protocol_transaction_out_106 serves the tx on getdata.
+ ////broadcast(
+ //// std::make_shared(
+ //// messages::peer::transaction{ tx }));
+
+ if (const auto fault = broadcast_tx(tx); fault)
+ {
+ send_error(fault);
+ return true;
}
- // Announce to peers; protocol_transaction_out_106 serves the tx on getdata.
- broadcast(
- std::make_shared(
- messages::peer::transaction{ tx }));
-
- send_result(encode_hash(txid), two * system::hash_size);
+ send_result(encode_hash(tx->hash(false)), two * system::hash_size);
return true;
}
@@ -648,14 +680,14 @@ void protocol_bitcoind_rpc::send_error(const code& ec,
}
void protocol_bitcoind_rpc::send_error(const code& ec,
- rpc::value_option&& error, size_t size_hint) NOEXCEPT
+ value_option&& error, size_t size_hint) NOEXCEPT
{
BC_ASSERT(stranded());
send_rpc(
{
.jsonrpc = version_,
.id = id_,
- .error = rpc::result_t
+ .error = result_t
{
.code = ec.value(),
.message = ec.message(),
@@ -670,7 +702,7 @@ void protocol_bitcoind_rpc::send_text(std::string&& hexidecimal) NOEXCEPT
send_result(hexidecimal, hexidecimal.size());
}
-void protocol_bitcoind_rpc::send_result(rpc::value_option&& result,
+void protocol_bitcoind_rpc::send_result(value_option&& result,
size_t size_hint) NOEXCEPT
{
BC_ASSERT(stranded());
@@ -683,7 +715,7 @@ void protocol_bitcoind_rpc::send_result(rpc::value_option&& result,
}
// private
-void protocol_bitcoind_rpc::send_rpc(rpc::response_t&& model,
+void protocol_bitcoind_rpc::send_rpc(response_t&& model,
size_t size_hint) NOEXCEPT
{
BC_ASSERT(stranded());
@@ -703,8 +735,8 @@ void protocol_bitcoind_rpc::send_rpc(rpc::response_t&& model,
}
// private
-void protocol_bitcoind_rpc::set_rpc_request(rpc::version version,
- const rpc::id_option& id, const http::request_cptr& request) NOEXCEPT
+void protocol_bitcoind_rpc::set_rpc_request(version version,
+ const id_option& id, const http::request_cptr& request) NOEXCEPT
{
BC_ASSERT(stranded());
id_ = id;
@@ -717,10 +749,62 @@ http::request_cptr protocol_bitcoind_rpc::reset_rpc_request() NOEXCEPT
{
BC_ASSERT(stranded());
id_.reset();
- version_ = rpc::version::undefined;
+ version_ = version::undefined;
return reset_request();
}
+// utility (redundant with protocol_electrum)
+// ----------------------------------------------------------------------------
+// TODO: move this to node utility and pass through.
+
+bool protocol_bitcoind_rpc::get_pool_context(chain::context& pool) const NOEXCEPT
+{
+ const auto& query = archive();
+ const auto& settings = system_settings();
+ const auto top = query.get_top_confirmed();
+ const auto link = query.to_confirmed(top);
+ const auto hash = query.get_header_key(link);
+ const auto state = query.get_chain_state(settings, hash);
+ if (!state) return false;
+ pool = chain::chain_state(*state, settings).context();
+ return true;
+}
+
+code protocol_bitcoind_rpc::validate_tx(
+ const chain::transaction& tx) const NOEXCEPT
+{
+ chain::context ctx{};
+ if (!get_pool_context(ctx))
+ return error::server_error;
+
+ code ec{};
+
+ // Ensure tx does not violate tx consensus rules.
+ if (!ec) ec = tx.check();
+ if (!ec) ec = tx.check(ctx);
+ if (!ec) archive().populate_with_metadata(tx, true);
+ if (!ec) ec = tx.accept(ctx);
+ if (!ec) ec = tx.confirm(ctx);
+ if (!ec) ec = tx.connect(ctx);
+
+ // Ensure tx does not violate presumed block consensus rules.
+ // This is a DoS guard when validating a tx outside of a block.
+ if (!ec) ec = tx.check_guard();
+ if (!ec) ec = tx.check_guard(ctx);
+ if (!ec) ec = tx.accept_guard(ctx);
+ return ec;
+}
+
+code protocol_bitcoind_rpc::broadcast_tx(
+ const chain::transaction::cptr& tx) NOEXCEPT
+{
+ if (const auto ec = validate_tx(*tx))
+ return ec;
+
+ BROADCAST(peer::transaction, to_shared(tx));
+ return {};
+}
+
BC_POP_WARNING()
BC_POP_WARNING()
BC_POP_WARNING()
diff --git a/src/protocols/bitcoind/protocol_bitcoind_rpc_json.cpp b/src/protocols/bitcoind/protocol_bitcoind_rpc_json.cpp
index b9065dcc..df9085f1 100644
--- a/src/protocols/bitcoind/protocol_bitcoind_rpc_json.cpp
+++ b/src/protocols/bitcoind/protocol_bitcoind_rpc_json.cpp
@@ -18,9 +18,7 @@
*/
#include
-#include
#include
-#include
namespace libbitcoin {
namespace server {
@@ -31,7 +29,7 @@ uint32_t protocol_bitcoind_rpc::median_time_past(const node::query& query,
const database::header_link& link) NOEXCEPT
{
chain::context ctx{};
- return query.get_context(ctx, link) ? ctx.median_time_past : 0;
+ return query.get_context(ctx, link) ? ctx.median_time_past : 0_u32;
}
void protocol_bitcoind_rpc::inject_block_context(boost::json::object& out,
@@ -97,8 +95,9 @@ boost::json::object protocol_bitcoind_rpc::header_to_bitcoind(
std::string protocol_bitcoind_rpc::chain_name(const node::query& query) NOEXCEPT
{
- const auto genesis = query.get_header_key(query.to_confirmed(0));
+ const auto genesis = query.get_header_key(query.to_confirmed(zero));
+ // TODO: create signet chain selector.
using selection = chain::selection;
constexpr auto signet = base16_hash(
"00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6");
diff --git a/src/protocols/electrum/protocol_electrum_transactions.cpp b/src/protocols/electrum/protocol_electrum_transactions.cpp
index 303abd1a..2b3ebf31 100644
--- a/src/protocols/electrum/protocol_electrum_transactions.cpp
+++ b/src/protocols/electrum/protocol_electrum_transactions.cpp
@@ -385,7 +385,7 @@ void protocol_electrum::handle_blockchain_transaction_id_from_position(
}, two * hash_size * add1(branch.size()));
}
-// utility
+// utility (redundant with protocol_bitcoind_rpc)
// ----------------------------------------------------------------------------
// TODO: move this to node utility and pass through.
diff --git a/test/parsers/bitcoind_query.cpp b/test/parsers/bitcoind_query.cpp
deleted file mode 100644
index 0685560d..00000000
--- a/test/parsers/bitcoind_query.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * Copyright (c) 2011-2026 libbitcoin developers (see AUTHORS)
- *
- * This file is part of libbitcoin.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-#include "../test.hpp"
-
-BOOST_AUTO_TEST_SUITE(bitcoind_query_tests)
-
-BOOST_AUTO_TEST_CASE(bitcoind_query_test)
-{
- BOOST_REQUIRE(true);
-}
-
-BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/protocols/bitcoind/bitcoind_setup_fixture.hpp b/test/protocols/bitcoind/bitcoind_setup_fixture.hpp
index a346cf75..1cf03471 100644
--- a/test/protocols/bitcoind/bitcoind_setup_fixture.hpp
+++ b/test/protocols/bitcoind/bitcoind_setup_fixture.hpp
@@ -37,7 +37,7 @@ struct bitcoind_setup_fixture
// object). Returns the parsed json-rpc response object (with result/error).
boost::json::value rpc(std::string_view method, std::string_view params="[]");
- // Bitcoin Core REST over HTTP GET (target under "/rest/...").
+ // bitcoind REST over HTTP GET (target under "/rest/...").
status rest_status(std::string_view target);
boost::json::value rest_json(std::string_view target);
std::string rest_text(std::string_view target);