This section has been updated to Bitcoin Core @ v24.0.1 |
Adding new RPCs
When trying to expose new information to users there are generally two possible approaches for developers to consider:
-
Add a new server-side RPC which directly delivers the new data
-
Create a new function on the client-side (e.g. the cli tool
bitcoin-cli
) which calls one or more existing RPCs and manipulates the results into the required format.
If the data is not available from existing RPCs then option 1) must be taken. However if option 2) is available then this is the preferred first choice. This is for the following reasons:
-
Minimalistic approach: put client-side functionality into the client, not the server
-
Adding server-side increases maintenance burden
-
-
Client-side functionality does not have to worry about API stability (as the RPCs do)
-
Functions can more easily start client side and migrate to server-side if heavily used, than visa-versa
There may be other considerations though too:
-
If this functionality might be wanted in multiple clients, e.g.
bitcoin-cli
and the GUIbitcoin-qt
, then rather than making two implementations in two clients it may make sense to add a single server-side function -
Doing expensive computations on the client side when an inexpensive pathway is available server-side
Ultimately there is no "correct" answer for all cases, but considering some of the above before implementing one way or another
HTTP Server
Bitcoin Core’s HTTP server is responsible for handling both RPC and REST requests, but not ZMQ. Since PR#5677 the server is based on libevent2. Libevent is a general purpose event notification library, but is used in Bitcoin Core specifically for HTTP requests (which it supports natively).
Much (not all) of the libevent interface is hidden behind wrappers. For example, HTTPRequest
wraps evhttp_request
and HTTPEvent
wraps event_base
.
The relevant workflow for how (for example) an RPC request is handled is roughly as follows:
-
The HTTP server receives an RPC command from a caller, creates an
evhttp_request
object and passes its pointer tohttp_request_cb()
(this step is completely handled by libevent). -
An
HTTPWorkItem
is created, containing theevhttp_request
(wrapped inHTTPRequest hreq
) as well as the path and reference to the handler function (which contains the business logic to be executed to deal with the request).-
There are 2 handlers for RPCs.
-
There are 12 handlers for REST.
-
-
The
HTTPWorkItem
is put on the globalWorkQueue g_work_queue
, which is processed by multiple worker threads asynchronously. -
When the handler function of a
HTTPWorkItem
completes successfully, it callsHTTPRequest::WriteReply()
, which triggers the libevent functionevhttp_send_reply()
, which in turn returns a response to the caller and destroys theevhttp_request
object.
Endpoints are registered to the HTTP server by calling RegisterHTTPHandler()
, such as e.g. in StartHTTPRPC()
.
The HTTP server is initiated and started from AppInitServers()
, and stopped from Shutdown()
.
StartHTTPServer()
adds a thread for each worker to g_thread_http_workers
. These threads will keep running until WorkQueue::Interrupt()
sets running
to false
and the queue is empty.