Clients and servers communicate using JSON RPC over an unspecified underlying stream transport. Examples include TCP, SSL, WS and WSS.
A client or server should only indicate JSON RPC 2.0 by
setting the jsonrpc member of
its messages to
"2.0" if it supports the version 2.0 protocol in
its entirety. ElectrumX does and will expect clients advertizing so
to function correctly. Those that do not will be disconnected and
Clients making batch requests should limit their size depending on the nature of their query, because servers will limit response size as an anti-DoS mechanism.
Over TCP and SSL raw sockets each RPC call, and each response, MUST be terminated by a single newline to delimit messages. Websocket messages are already framed so they MUST NOT be newline terminated. The JSON specification does not permit control characters within strings, so no confusion is possible there. However it does permit newlines as extraneous whitespace between elements; client and server MUST NOT use newlines in such a way.
If using JSON RPC 2.0’s feature of parameter passing by name, the names shown in the description of the method or notification in question MUST be used.
A server advertising support for a particular protocol version MUST support each method documented for that protocol version, unless the method is explicitly marked optional. It may support other methods or additional parameters with unspecified behaviour. Use of additional parameters is discouraged as it may conflict with future versions of the protocol.
Some RPC calls are subscriptions which, after the initial response, will send a JSON RPC notification each time the thing subscribed to changes. The method of the notification is the same as the method of the subscription, and the params of the notification (and their names) are given in the documentation of the method.
It is desirable to have a way to enhance and improve the protocol without forcing servers and clients to upgrade at the same time.
Protocol versions are denoted by dotted number strings with at least one dot. Examples: “1.5”, “1.4.1”, “2.0”. In “a.b.c” a is the major version number, b the minor version number, and c the revision number.
A party to a connection will speak all protocol versions in a range, say from protocol_min to protocol_max, which may be the same. When a connection is made, both client and server must initially assume the protocol to use is their own protocol_min.
The client should send a
server.version() RPC call as early as
possible in order to negotiate the precise protocol version; see its
description for more detail. All responses received in the stream
from and including the server’s response to this call will use its
negotiated protocol version.
A script hash is the hash of the binary bytes of the locking
script (ScriptPubKey), expressed as a hexadecimal string. The hash
function to use is given by the “hash_function” member of
sha256() only). Like for
block and transaction hashes, when converting the big-endian binary
hash to a hexadecimal string the least-significant byte appears first,
and the most-significant byte last.
For example, the legacy Bitcoin address from the genesis block:
has P2PKH script:
with SHA256 hash:
which is sent to the server reversed as:
By subscribing to this hash you can find P2PKH payments to that address.
One public key, the genesis block public key, among the trillions for that address is:
which has P2PK script:
with SHA256 hash:
which is sent to the server reversed as:
By subscribing to this hash you can find P2PK payments to the genesis block public key.
The Genesis block coinbase is uniquely unspendable and therefore not indexed. It will not show with the above P2PK script hash subscription.
To calculate the status of a script hash (or address):
1. order confirmed transactions to the script hash by increasing height (and position in the block if there are more than one in a block)
2. form a string that is the concatenation of strings
"tx_hash:height:" for each transaction in order, where:
tx_hashis the transaction hash in hexadecimal
heightis the height of the block it is in.
3. Next, with mempool transactions in any order, append a similar
string for those transactions, but where height is
-1 if the
transaction has at least one unconfirmed input, and
0 if all
inputs are confirmed.
4. The status of the script hash is the
sha256() hash of the
full string expressed as a hexadecimal string, or
null if the
string is empty because there are no transactions.
To calculate the status of an asset:
Convert the number of satoshis in circulation into a string (e.g. 100,000,000 -> “100000000”)
Convert the number of divisions into a string (e.g. 1 -> “1”)
3. Convert the reissuability of an asset into a string. The boolean types match as follows: true -> “True”, false -> “False” (The default python implementation)
4. Convert the boolean value of whether an asset has associated ipfs data into string form.
Concatenate these strings (e.g. “1000000001FalseTrue”)
6. If an asset has an associated ipfs hash, append this as-is. (e.g. “1000000001FalseTrueQmeGgd16sWq6TNfXy8xzwQWRhv1vZUjP1LBxVnfaHaoV25”) otherwise, do nothing. (e.g. “1000000001FalseFalse”)
Convert this string into a byte array of respective ascii values.
8. The status of the asset is the
sha256() hash of this byte array
expressed as a hexadecimal string, or
null if the
asset does not exist.
Originally Electrum clients would download all block headers and verify the chain of hashes and header difficulty in order to confirm the merkle roots with which to check transaction inclusion.
With the Bitcoin chain now past height 500,000, the headers form over 40MB of raw data which becomes 80MB if downloaded as text from Electrum servers. The situation is worse for testnet and coins with more frequent blocks. Downloading and verifying all this data on initial use would take several minutes, during which Electrum was non-responsive.
To facilitate a better experience for SPV clients, particularly on
mobile, protocol version 1.4 introduces an
optional cp_height argument to the
blockchain.block.headers() RPC calls.
This requests the server provide a merkle proof, to a single 32-byte checkpoint hard-coded in the client, that the header(s) provided are valid in the same way the server proves a transaction is included in a block. If several consecutive headers are requested, the proof is provided for the final header - the prev_hash links in the headers are sufficient to prove the others valid.
Using this feature client software only needs to download the headers it is interested in up to the checkpoint. Headers after the checkpoint must all be downloaded and validated as before. The RPC calls return the merkle root, so to embed a checkpoint in a client simply make an RPC request to a couple of trusted servers for the greatest height to which a reorganisation of the chain is infeasible, and confirm the returned roots match.
with 500,000 headers of 80 bytes each, a naïve server implementation would require hashing approximately 88MB of data to provide a single merkle proof. ElectrumX implements an optimization such that it hashes only approximately 180KB of data per proof.