Bitcoin full node and query server

Oct 28, 2025

View in context in the libbitcoin Slack #general mirror.

evoskuil21:24
The http::json_request and http::json_response are new. These are defined using a custom network::http::body class that populates the boost::beast::http::request<> and ::response<> templates. The body is built over the boost::json parse/serialize annotations on the custom objects. One might wonder why boost::beast doesn't include this OOTB, but alas. The json body template impl looks like this (note that this is generic to any json, not limited to json-rpc:
namespace libbitcoin {
namespace network {
namespace json {

// reader
// ----------------------------------------------------------------------------

TEMPLATE
void CLASS::reader::init(const http::length_type& length,
    error_code& ec) NOEXCEPT
{
    const auto value = length.get_value_or(zero);
    if (system::is_limited<size_t>(value))
    {
        ec = boost::system::errc::protocol_error;
        return;
    }

    expected_ = system::possible_narrow_cast<size_t>(value);
    parser_.reset();
    total_ = zero;
    ec.clear();
}

TEMPLATE
size_t CLASS::reader::put(const buffer_type& buffer, error_code& ec) NOEXCEPT
{
    try
    {
        const auto data = system::pointer_cast<const char>(buffer.data());
        const size_t parsed = parser_.write_some(data, buffer.size(), ec);

        total_ = system::ceilinged_add(total_, parsed);
        if (!ec && total_ > expected_.value_or(max_size_t))
            ec = boost::system::errc::protocol_error;

        return parsed;
    }
    catch (...)
    {
        ec = boost::system::errc::protocol_error;
        return {};
    }
}

TEMPLATE
void CLASS::reader::finish(error_code& ec) NOEXCEPT
{
    parser_.finish(ec);
    if (!ec)
        payload_.model = parser_.release();
}

// writer
// ----------------------------------------------------------------------------

TEMPLATE
void CLASS::writer::init(error_code& ec) NOEXCEPT
{
    if (!payload_.buffer)
    {
        // Caller controls max_size and other buffer behavior by assigning it.
        payload_.buffer = std::make_shared<http::flat_buffer>(4096);
    }
    else
    {
        // Caller has assigned the buffer (or just reused the response).
        payload_.buffer->consume(payload_.buffer->size());
    }

    ec.clear();
    serializer_.reset(&payload_.model);
}

TEMPLATE
CLASS::writer::out_buffer CLASS::writer::get(error_code& ec) NOEXCEPT
{
    ec.clear();
    if (serializer_.done())
        return {};

    try
    {
        // Always prepares the configured max_size.
        const auto size = payload_.buffer->max_size();
        const auto buff = payload_.buffer->prepare(size);
        const auto data = system::pointer_cast<char>(buff.data());
        const auto view = serializer_.read(data, buff.size());
        const auto done = serializer_.done();
        payload_.buffer->commit(view.size());
        return out_buffer{ std::make_pair(boost::asio::buffer(view), !done) };
    }
    catch (...)
    {
        ec = boost::system::errc::protocol_error;
        return boost::none;
    }
}

} // namespace json
} // namespace network
} // namespace libbitcoin