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:

  1. Add a new server-side RPC which directly delivers the new data

  2. 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 GUI bitcoin-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:

  1. The HTTP server receives an RPC command from a caller, creates an evhttp_request object and passes its pointer to http_request_cb() (this step is completely handled by libevent).

  2. An HTTPWorkItem is created, containing the evhttp_request (wrapped in HTTPRequest 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).

  3. The HTTPWorkItem is put on the global WorkQueue g_work_queue, which is processed by multiple worker threads asynchronously.

  4. When the handler function of a HTTPWorkItem completes successfully, it calls HTTPRequest::WriteReply(), which triggers the libevent function evhttp_send_reply(), which in turn returns a response to the caller and destroys the evhttp_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.