From 43e130f215327324ba5c190a665a10560f33674e Mon Sep 17 00:00:00 2001 From: evoskuil Date: Thu, 18 Dec 2025 22:01:54 -0500 Subject: [PATCH 1/4] Add default serialization for rpc v1 error.result. --- src/rpc/model.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/rpc/model.cpp b/src/rpc/model.cpp index 5a5a63413..67be5a479 100644 --- a/src/rpc/model.cpp +++ b/src/rpc/model.cpp @@ -310,7 +310,8 @@ DEFINE_JSON_FROM_TAG(response_t) object["data"] = value_from(result.data.value()); } } - else if (instance.jsonrpc != version::v2) + else if (instance.jsonrpc == version::v1 || + instance.jsonrpc == version::undefined) { object["error"] = boost::json::value{}; } @@ -319,6 +320,11 @@ DEFINE_JSON_FROM_TAG(response_t) { object["result"] = value_from(instance.result.value()); } + else if (instance.jsonrpc == version::v1 || + instance.jsonrpc == version::undefined) + { + object["result"] = boost::json::value{}; + } value = object; } From 6abaf05620a209aef26cdbd843724f9ec98690b8 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Thu, 18 Dec 2025 22:02:38 -0500 Subject: [PATCH 2/4] Add post parse rpc validation to rpc::body template. --- src/messages/rpc_body.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/messages/rpc_body.cpp b/src/messages/rpc_body.cpp index da21e8f4f..3ad7f1048 100644 --- a/src/messages/rpc_body.cpp +++ b/src/messages/rpc_body.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -119,6 +120,31 @@ finish(boost_code& ec) NOEXCEPT // As a catch-all we blame alloc. ec = to_http_code(http_error_t::bad_alloc); } + + // Post-parse semantic validation. + + if (value_.message.jsonrpc == version::undefined) + value_.message.jsonrpc = version::v1; + + if (value_.message.method.empty() || + !value_.message.params.has_value()) + { + ec = to_boost_code(boost_error_t::bad_message); + return; + } + + if (value_.message.jsonrpc == version::v1) + { + if (!value_.message.id.has_value()) + ec = to_boost_code(boost_error_t::bad_message); + else if (!std::holds_alternative( + value_.message.params.value())) + ec = to_boost_code(boost_error_t::bad_message); + + // TODO: v1 batch is not allowed. + ////else if (value_.message.is_batch()) + //// ec = to_boost_code(boost_error_t::bad_message); + } } template <> From a5053654005b19971eb9191c037ed93f86d2be53 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Fri, 19 Dec 2025 00:18:28 -0500 Subject: [PATCH 3/4] Fix location of error.data in the rpc response serialization. --- src/rpc/model.cpp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/rpc/model.cpp b/src/rpc/model.cpp index 67be5a479..3f40d659a 100644 --- a/src/rpc/model.cpp +++ b/src/rpc/model.cpp @@ -299,15 +299,22 @@ DEFINE_JSON_FROM_TAG(response_t) { const auto& result = instance.error.value(); - object["error"] = - { - { "code", result.code }, - { "message", result.message } - }; - if (result.data.has_value()) { - object["data"] = value_from(result.data.value()); + object["error"] = + { + { "code", result.code }, + { "message", result.message }, + { "data", value_from(result.data.value()) } + }; + } + else + { + object["error"] = + { + { "code", result.code }, + { "message", result.message } + }; } } else if (instance.jsonrpc == version::v1 || From 9f6c84058959aefbe456d22002075929756f661f Mon Sep 17 00:00:00 2001 From: evoskuil Date: Fri, 19 Dec 2025 02:47:03 -0500 Subject: [PATCH 4/4] Add send_bad_request(). --- include/bitcoin/network/protocols/protocol_http.hpp | 1 + src/protocols/protocol_http.cpp | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/include/bitcoin/network/protocols/protocol_http.hpp b/include/bitcoin/network/protocols/protocol_http.hpp index 73f2330ce..ccb283a8d 100644 --- a/include/bitcoin/network/protocols/protocol_http.hpp +++ b/include/bitcoin/network/protocols/protocol_http.hpp @@ -79,6 +79,7 @@ class BCT_API protocol_http /// Senders. virtual void send_ok(const http::request& request={}) NOEXCEPT; virtual void send_bad_host(const http::request& request={}) NOEXCEPT; + virtual void send_bad_request(const http::request& request={}) NOEXCEPT; virtual void send_not_found(const http::request& request={}) NOEXCEPT; virtual void send_not_acceptable(const http::request& request={}) NOEXCEPT; virtual void send_forbidden(const http::request& request={}) NOEXCEPT; diff --git a/src/protocols/protocol_http.cpp b/src/protocols/protocol_http.cpp index 212741a37..380bf8662 100644 --- a/src/protocols/protocol_http.cpp +++ b/src/protocols/protocol_http.cpp @@ -245,6 +245,19 @@ void protocol_http::send_forbidden(const request& request) NOEXCEPT SEND(std::move(out), handle_complete, _1, error::forbidden); } +// Closes channel. +void protocol_http::send_bad_request(const request& request) NOEXCEPT +{ + BC_ASSERT(stranded()); + const auto code = status::bad_request; + const auto media = to_media_type(request[field::accept]); + response out{ code, request.version() }; + add_common_headers(out, request, true); + out.body() = string_status(code, out.reason(), media); + out.prepare_payload(); + SEND(std::move(out), handle_complete, _1, error::bad_request); +} + // Closes channel. void protocol_http::send_bad_host(const request& request) NOEXCEPT {