| Crates.io | bitcoin-remote |
| lib.rs | bitcoin-remote |
| version | 0.1.22 |
| created_at | 2023-01-19 19:09:55.504341+00 |
| updated_at | 2025-12-01 04:11:10.91534+00 |
| description | Low-level, Bitcoin Core–compatible JSON-RPC helper crate providing HTTP status mapping, RPC error codes, auth-cookie management, parameter-conversion tables, and UniValue-based request/reply utilities. |
| homepage | |
| repository | https://github.com/klebs6/bitcoin-rs |
| max_upload_size | |
| id | 762829 |
| size | 278,602 |
Low-level, zero-magic utilities for speaking Bitcoin Core's JSON‑RPC over HTTP, faithfully ported from upstream C++ with strongly typed Rust interfaces.
This crate does not try to be a full Bitcoin wallet or a high-level client library. Instead, it focuses on being a precise, observable, and testable substrate for:
rpcauth/cookie-based authentication,If you want exact behavioral parity with bitcoind's RPC interface—down to error codes, HTTP status behavior, and batch semantics—this crate is intended for you.
HTTP / JSON‑RPC fidelity
HTTPStatusCode enum for mapping internal failures to HTTP status codes.Bitcoin Core RPC error codes
RPCErrorCode is a bitflags! set of all Bitcoin Core RPC error codes, including wallet, P2P, chain, and general application errors.RPC_TRANSACTION_ERROR) closely track upstream.RPC parameter conversion metadata
RPCConvertParam and RPCConvertTable encode which RPC parameters must be treated as JSON even when passed as textual CLI arguments.(method, index) or (method, name) gives you a boolean indicating whether a parameter must be JSON‑parsed before being passed to Core.Authentication cookie helpers
generate_auth_cookie, get_auth_cookie, and delete_auth_cookie implement the same cookie‑file strategy as Bitcoin Core:
-rpccookiefile from global argsJSON‑RPC request and response composition
JSONRPCRequest encapsulates method, params, id, and metadata like uri, auth_user, and peer_addr.jsonrpc_request_obj, jsonrpc_reply_obj, jsonrpc_reply, and jsonrpc_error are low-level helpers over UniValue.Instrumentation
tracing crate at trace/debug/info levels, intended for production observability without polluting the hot path.The crate assumes you are comfortable working close to the protocol: you will handle HTTP transport concerns, lifetimes, and concurrency at your own abstraction layer.
The design mirrors Bitcoin Core's internals:
UniValue (from uni_value), not serde_json, to follow Core's semantics exactly.The goal is to allow you to reconstruct, inspect, and control your interaction with a Bitcoin node with maximal transparency.
#[repr(i32)]
pub enum HTTPStatusCode {
HTTP_OK,
HTTP_BAD_REQUEST,
HTTP_UNAUTHORIZED,
HTTP_FORBIDDEN,
HTTP_NOT_FOUND,
HTTP_BAD_METHOD,
HTTP_INTERNAL_SERVER_ERROR,
HTTP_SERVICE_UNAVAILABLE,
}
HTTPStatusCode encodes the subset of HTTP codes relevant to JSON‑RPC error mapping. Upstream Core maps certain RPC errors to specific HTTP codes (e.g. RPC_INVALID_REQUEST → HTTP_BAD_REQUEST). Use this when designing your HTTP layer.
pub enum JSONRPCRequestMode {
EXECUTE,
GET_HELP,
GET_ARGS,
}
JSONRPCRequestMode allows you to model whether an incoming request is meant to be executed or used for help/argument introspection. This is primarily useful for CLI frontends or meta‑RPC layers.
RPCErrorCode is defined using bitflags! over i32. It enumerates the entire Bitcoin Core RPC error space:
RPC_INVALID_REQUEST, RPC_METHOD_NOT_FOUND, RPC_INVALID_PARAMS, RPC_INTERNAL_ERROR, RPC_PARSE_ERROR.RPC_MISC_ERROR, RPC_TYPE_ERROR, RPC_INVALID_PARAMETER, etc.RPC_CLIENT_NOT_CONNECTED, RPC_CLIENT_IN_INITIAL_DOWNLOAD, RPC_CLIENT_NODE_CAPACITY_REACHED, etc.RPC_WALLET_ERROR, RPC_WALLET_INSUFFICIENT_FUNDS, RPC_WALLET_UNLOCK_NEEDED, RPC_WALLET_ALREADY_LOADED, etc.Each error is a bitflag with a specific negative code matching Bitcoin Core's public RPC interface. Example:
use bitcoin_remote::RPCErrorCode;
fn classify(err_code: i32) {
if err_code == RPCErrorCode::RPC_WALLET_INSUFFICIENT_FUNDS.bits() {
eprintln!("insufficient funds: {err_code}");
}
}
Using a bitflag type instead of raw i32 clarifies intent and allows consistent code completion.
Bitcoin Core distinguishes between positional and named RPC parameters that must be treated as JSON instead of simple strings. For example, sendrawtransaction expects a hex string, whereas createrawtransaction may expect complex JSON objects.
This crate mirrors Core's table‑driven approach using:
#[derive(Debug, Getters, MutGetters, Setters, Default, Builder)]
pub struct RPCConvertParam {
method_name: &'static str,
param_idx: i32,
param_name: &'static str,
}
#[derive(Debug, Getters, MutGetters, Setters, Default, Builder)]
pub struct RPCConvertTable {
members: HashSet<(String, i32)>,
members_by_name: HashSet<(String, String)>,
}
and the global instance:
lazy_static! {
pub static ref RPC_CVT_TABLE: std::sync::Mutex<RPCConvertTable> =
std::sync::Mutex::new(RPCConvertTable::new());
}
RPCConvertTable::new() populates members and members_by_name from a static vRPCConvertParams list emitted from upstream definitions. You can query it as follows:
use bitcoin_remote::RPC_CVT_TABLE;
fn param_requires_json(method: &str, index: i32) -> bool {
let mut tbl = RPC_CVT_TABLE.lock().expect("RPC_CVT_TABLE poisoned");
tbl.convert_with_method_and_idx(method, index)
}
fn param_requires_json_by_name(method: &str, name: &str) -> bool {
let mut tbl = RPC_CVT_TABLE.lock().expect("RPC_CVT_TABLE poisoned");
tbl.convert_with_method_and_name(method, name)
}
These APIs are intentionally simple: they answer "must this parameter be JSON‑parsed?" without hiding the table's structure.
pub struct JSONRPCRequest {
id: UniValue,
str_method: String,
params: UniValue,
mode: JSONRPCRequestMode,
uri: String,
auth_user: String,
peer_addr: String,
context: Box<dyn Any>,
}
The parse method implements JSON‑RPC request parsing consistent with Bitcoin Core:
id early so subsequent error reports can include it.method is present and a string.params as either an array or object; null becomes an empty array; all other types panic as invalid.Example:
use bitcoin_remote::{JSONRPCRequest, jsonrpc_error, RPCErrorCode};
use uni_value::{UniValue, VType};
fn parse_single_request(val_request: &UniValue) -> JSONRPCRequest {
let mut req = JSONRPCRequest {
// reasonable defaults; context populated by caller
id: UniValue::null(),
str_method: String::new(),
params: UniValue::empty_array(),
mode: bitcoin_remote::JSONRPCRequestMode::EXECUTE,
uri: String::new(),
auth_user: String::new(),
peer_addr: String::new(),
context: Box::new(()),
};
// May panic with a JSON‑encoded error, per Core behavior
req.parse(val_request);
req
}
Because the implementation intentionally panics on structural violations (mirroring C++ exceptions), you should either sanitize input at your HTTP boundary or wrap parsing in higher‑level error handling if you need a non‑panic path.
pub fn jsonrpc_request_obj(str_method: &str, params: &UniValue, id: &UniValue) -> UniValue
Creates a minimal JSON‑RPC request object:
{ "method": <str_method>, "params": <params>, "id": <id> }
Example:
use bitcoin_remote::jsonrpc_request_obj;
use uni_value::{UniValue, VType};
let mut params = UniValue::new(VType::VARR, None);
params.push_back("getblockchaininfo");
let id = UniValue::from(1_i64);
let req = jsonrpc_request_obj("getblockchaininfo", ¶ms, &id);
let wire = req.write(None, None);
pub fn jsonrpc_reply_obj(result: &UniValue, error: &UniValue, id: &UniValue) -> UniValue
pub fn jsonrpc_reply(result: &UniValue, error: &UniValue, id: &UniValue) -> String
pub fn jsonrpc_error(code: i32, message: &str) -> UniValue
Semantics:
error is not null, the reply's "result" field is forced to null.jsonrpc_reply serializes the reply to a compact string and appends a newline (matching Core's write() + "\n").Example:
use bitcoin_remote::{jsonrpc_reply, jsonrpc_error, RPCErrorCode};
use uni_value::UniValue;
// success reply
let result = UniValue::from("ok");
let error = UniValue::null();
let id = UniValue::from(1_i64);
let success_wire = jsonrpc_reply(&result, &error, &id);
// error reply
let err_obj = jsonrpc_error(
RPCErrorCode::RPC_INVALID_PARAMS.bits(),
"invalid parameters supplied",
);
let error_wire = jsonrpc_reply(&UniValue::null(), &err_obj, &id);
pub fn jsonrpc_process_batch_reply(in_: &UniValue) -> Vec<UniValue>
Parses a batch reply into a Vec<UniValue> indexed by numeric "id":
in_ must be an array; otherwise the function panics with "Batch must be an array"."id" between 0 and batch_size - 1; otherwise, a panic with "Batch member id is larger than batch size" occurs.This deterministic behavior makes it straightforward to implement batch clients that rely strictly on index‑based mapping.
Example:
use bitcoin_remote::jsonrpc_process_batch_reply;
use uni_value::{UniValue, VType};
fn handle_batch(batch_uv: &UniValue) {
let replies = jsonrpc_process_batch_reply(batch_uv);
for (idx, reply) in replies.iter().enumerate() {
println!("reply {idx}: {reply:?}");
}
}
Bitcoin Core supports a cookie‑file based authentication mechanism. This crate provides low-level helpers that implement the same behavior, including path resolution and atomic writes.
pub fn get_auth_cookie_file(temp: Option<bool>) -> Box<Path>
temp == Some(true), appends .tmp and returns the path for the temporary file location.-rpccookiefile=<path> from global args if present; otherwise it falls back to the default (typically something like $DATADIR/.cookie).pub fn generate_auth_cookie(cookie_out: &mut String) -> bool
Steps:
"<COOKIEAUTH_USER>:<hex_password>"..tmp), creating parent directories as needed.On success, cookie_out is populated and true is returned. Errors are logged via tracing::error! and false is returned; best‑effort clean‑up is attempted.
Example:
use bitcoin_remote::generate_auth_cookie;
let mut cookie = String::new();
if generate_auth_cookie(&mut cookie) {
println!("Generated cookie: {cookie}");
}
pub fn get_auth_cookie(cookie_out: &mut String) -> bool
cookie_out.\n and optional \r to match C++ getline behavior.false if the file cannot be opened or read, logging at debug or warn levels.pub fn delete_auth_cookie()
Attempts to delete the cookie file. Any I/O error other than file not found is logged but otherwise ignored.
Example integration pattern:
use bitcoin_remote::{generate_auth_cookie, get_auth_cookie, delete_auth_cookie};
fn rotate_cookie() {
let mut new_cookie = String::new();
if !generate_auth_cookie(&mut new_cookie) {
eprintln!("failed to generate new cookie");
return;
}
let mut read_back = String::new();
if get_auth_cookie(&mut read_back) {
assert_eq!(new_cookie, read_back);
}
delete_auth_cookie();
}
JSONRPCRequest::parse, jsonrpc_process_batch_reply) intentionally use panic! on structural violations, to mimic Bitcoin Core's use of exceptions for programmer / protocol errors rather than expected runtime failures.trace!, debug!, info!, warn!, and error! invocations align with logical phases—construction, parsing, and I/O. In production, you can adjust your tracing subscriber level to tune verbosity.RPCErrorCode values are kept numerically identical to upstream. Where upstream deprecates or repurposes codes, the crate favors backward compatibility.If you are building public APIs or user‑facing tooling on top of this crate, you may want to wrap these primitives into a non‑panicking facade that translates panics into structured error values.
Below is a sketch of how you might integrate bitcoin-remote into a higher-level HTTP client. This example omits error‑handling and TLS for brevity.
use bitcoin_remote::{
jsonrpc_request_obj,
jsonrpc_reply,
jsonrpc_process_batch_reply,
};
use uni_value::{UniValue, VType};
fn build_getblockchaininfo_request() -> String {
let params = UniValue::empty_array();
let id = UniValue::from(0_i64);
let req_obj = jsonrpc_request_obj("getblockchaininfo", ¶ms, &id);
let mut s = req_obj.write(None, None);
s.push('\n');
s
}
fn main() {
let body = build_getblockchaininfo_request();
// send `body` over HTTP POST to bitcoind's RPC endpoint using your
// HTTP client of choice (e.g., reqwest). Parse the response into
// `UniValue` using the `uni_value` crate, then handle it.
}
bitcoin-remote0.1.22klebs <none>