Neil Booth
8 years ago
1 changed files with 738 additions and 0 deletions
@ -0,0 +1,738 @@ |
|||
================= |
|||
Electrum Protocol |
|||
================= |
|||
|
|||
Until now there was no written specification of the Electrum protocol |
|||
that I am aware of; this document is an attempt to fill that gap. It |
|||
is intended to be a reference for client and server authors alike. |
|||
|
|||
I have attempted to ensure what is written is correct for the three |
|||
known server implementations: electrum-server, jelectrum and |
|||
ElectrumX, and also for Electrum clients of the 2.x series. We know |
|||
other clients exist but I am not aware of the source of any being |
|||
publicly available. |
|||
|
|||
|
|||
Message Stream |
|||
-------------- |
|||
|
|||
Clients and servers communicate using JSON RPC over an unspecified |
|||
underlying stream transport protocol, typically TCP or SSL. |
|||
|
|||
`JSON RPC 1.0`_ and `JSON RPC 2.0`_ are specified; use of version 2.0 |
|||
is encouraged but not required. Server support of batch requests is |
|||
encouraged for version 1.0 but not required. 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. |
|||
|
|||
RPC calls and responses are separated by newlines in the stream. 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 protocol versions's description 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. |
|||
|
|||
Notifications |
|||
------------- |
|||
|
|||
Some methods are subscriptions, which will respond with notifications |
|||
when 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. |
|||
|
|||
|
|||
Protocol Negotiation |
|||
-------------------- |
|||
|
|||
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 negotiation is not implemented in any client or server at |
|||
present to the best of my knowledge, so care is needed to ensure |
|||
current clients and servers continue to operate as expected. |
|||
|
|||
Protocol versions are denoted by [major_number, minor_number] pairs, |
|||
for example protocol version 1.15 is [1, 15] as a pair. |
|||
|
|||
A party to a connection speak all protocol versions in a range, say |
|||
from `protocol_min` to `protocol_max`. This min and max 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 the |
|||
negotiated protocol version. |
|||
|
|||
|
|||
Protocol Version 1.0 |
|||
-------------------- |
|||
|
|||
server.version |
|||
============== |
|||
|
|||
Identifies the client to the server. |
|||
|
|||
server.version(**client_name**, **protocol_version**) |
|||
|
|||
**client_name** |
|||
|
|||
An optional string identifying the connecting client software. |
|||
|
|||
**protocol_verion** |
|||
|
|||
Optional. The value passed is ignored. |
|||
|
|||
**Response** |
|||
|
|||
A string identifying the server software. |
|||
|
|||
**Example**:: |
|||
|
|||
server.version("2.7.11", "1.0") |
|||
|
|||
|
|||
blockchain.address.get_balance |
|||
============================== |
|||
|
|||
Return the confirmed and unconfirmed balances of a bitcoin address. |
|||
|
|||
blockchain.address.get_balance(**address**) |
|||
|
|||
**address** |
|||
|
|||
The address as a Base58 string. |
|||
|
|||
**Response** |
|||
|
|||
A dictionary with keys *confirmed* and *unconfirmed*. The value of |
|||
each is the appropriate balance in coin units as a string. |
|||
|
|||
**Response Example**:: |
|||
|
|||
{ |
|||
"confirmed": "1.03873966", |
|||
"unconfirmed": "0.236844" |
|||
} |
|||
|
|||
|
|||
blockchain.address.get_history |
|||
============================== |
|||
|
|||
Return the confirmed and unconfirmed history of a bitcoin address. |
|||
|
|||
blockchain.address.get_history(**address**) |
|||
|
|||
**address** |
|||
|
|||
The address as a Base58 string. |
|||
|
|||
**Response** |
|||
|
|||
A list of confirmed transactions in blockchain order, with the |
|||
output of *blockchain.address.get_mempool* appended to the list. |
|||
Each transaction is a dictionary with keys *height* and *tx_hash*. |
|||
*height* is the integer height of the block the transaction was |
|||
confirmed in, and *tx_hash* the transaction hash in hexadecimal. |
|||
|
|||
**Response Examples** |
|||
|
|||
:: |
|||
|
|||
[ |
|||
{ |
|||
"height": 200004, |
|||
"tx_hash": "acc3758bd2a26f869fcc67d48ff30b96464d476bca82c1cd6656e7d506816412" |
|||
}, |
|||
{ |
|||
"height": 215008, |
|||
"tx_hash": "f3e1bf48975b8d6060a9de8884296abb80be618dc00ae3cb2f6cee3085e09403" |
|||
} |
|||
] |
|||
|
|||
:: |
|||
|
|||
[ |
|||
{ |
|||
"fee": 20000, |
|||
"height": 0, |
|||
"tx_hash": "9fbed79a1e970343fcd39f4a2d830a6bde6de0754ed2da70f489d0303ed558ec" |
|||
} |
|||
] |
|||
|
|||
|
|||
blockchain.address.get_mempool |
|||
============================== |
|||
|
|||
Return the unconfirmed transactions of a bitcoin address. |
|||
|
|||
blockchain.address.get_mempool(**address**) |
|||
|
|||
**address** |
|||
|
|||
The address as a Base58 string. |
|||
|
|||
**Response** |
|||
|
|||
A list of mempool transactions in arbitrary order. Each |
|||
transaction is a dictionary with keys *height* , *tx_hash* and |
|||
*fee*. *tx_hash* the transaction hash in hexadecimal, *height* is |
|||
`0` if all inputs are confirmed, and `-1` otherwise, and *fee* is |
|||
the transaction fee in coin units. |
|||
|
|||
**Response Examples** |
|||
|
|||
:: |
|||
|
|||
[ |
|||
{ |
|||
"tx_hash": "45381031132c57b2ff1cbe8d8d3920cf9ed25efd9a0beb764bdb2f24c7d1c7e3", |
|||
"height": 0, |
|||
"fee": 24310 |
|||
} |
|||
] |
|||
|
|||
|
|||
blockchain.address.get_proof |
|||
============================ |
|||
|
|||
This method is optional and deprecated, and hence its response will |
|||
not be described here. |
|||
|
|||
blockchain.address.get_proof(**address**) |
|||
|
|||
**address** |
|||
|
|||
The address as a Base58 string. |
|||
|
|||
|
|||
blockchain.address.listunspent |
|||
============================== |
|||
|
|||
Return an ordered list of UTXOs sent to a bitcoin address. |
|||
|
|||
blockchain.address.listunspent(**address**) |
|||
|
|||
**address** |
|||
|
|||
The address as a Base58 string. |
|||
|
|||
**Response** |
|||
|
|||
A list of unspent outputs in blockchain order. Each transaction |
|||
is a dictionary with keys *height* , *tx_pos*, *tx_height* and |
|||
*value* keys. *height* is the integer height of the block the |
|||
transaction was confirmed in, *tx_hash* the transaction hash in |
|||
hexadecimal, *tx_pos* the zero-based index of the output in the |
|||
transaction's list of outputs, and *value* its integer value in |
|||
minimum coin units (satoshis in the case of Bitcoin). |
|||
|
|||
**Response Example** |
|||
|
|||
:: |
|||
|
|||
[ |
|||
{ |
|||
"tx_pos": 0, |
|||
"value": 45318048, |
|||
"tx_hash": "9f2c45a12db0144909b5db269415f7319179105982ac70ed80d76ea79d923ebf", |
|||
"height": 437146 |
|||
}, |
|||
{ |
|||
"tx_pos": 0, |
|||
"value": 919195, |
|||
"tx_hash": "3d2290c93436a3e964cfc2f0950174d8847b1fbe3946432c4784e168da0f019f", |
|||
"height": 441696 |
|||
} |
|||
] |
|||
|
|||
|
|||
blockchain.address.subscribe |
|||
============================ |
|||
|
|||
Subscribe to a bitcoin address. |
|||
|
|||
blockchain.address.subscribe(**address**) |
|||
|
|||
**address** |
|||
|
|||
The address as a Base58 string. |
|||
|
|||
**Response** |
|||
|
|||
The *status* [1]_ of the address. |
|||
|
|||
**Notifications** |
|||
|
|||
As this is a subcription, the client will receive a notification |
|||
when the status of the address changes. The parameters are: |
|||
|
|||
[**address**, **status**] |
|||
|
|||
.. [1] To calculate the *status* of an address, order confirmed |
|||
transactions touching the address by height (and position in |
|||
the block if there are more than one in a block). Form a |
|||
string that is the concatenation of strings 'tx_hash:height:' |
|||
for each transaction in order. *tx_hash* is the transaction |
|||
hash in hexadecimal, *height* the height of the block it is in. |
|||
Next, with mempool transactions in any order, append a string |
|||
that is the same, but where *height* is `-1` if the transaction |
|||
has at least one unconfirmed input, and `0` if all inputs are |
|||
confirmed. The *status* is the **sha256** hash of this string |
|||
expressed in hexadecimal. |
|||
|
|||
|
|||
blockchain.block.get_header |
|||
=========================== |
|||
|
|||
Return the *deserialized header* [2]_ of the block at the given height. |
|||
|
|||
blockchain.block.get_chunk(**height**) |
|||
|
|||
**height** |
|||
|
|||
The height of the block, an integer. |
|||
|
|||
**Response** |
|||
|
|||
.. [2] The *deserialized header* of a block is a dictionary like |
|||
so:: |
|||
|
|||
{ |
|||
"block_height": <integer>, |
|||
'version': <integer>, |
|||
'prev_block_hash': <hexadecimal string>, |
|||
'merkle_root': <hexadecimal string>, |
|||
'timestamp': <integer>, |
|||
'bits': <integer>, |
|||
'nonce': <integer>, |
|||
} |
|||
|
|||
|
|||
blockchain.block.get_chunk |
|||
========================== |
|||
|
|||
Return a concatenated chunk of block headers. A chunk consists of a |
|||
fixed number of block headers over at the end of which difficulty is |
|||
retargeted. |
|||
|
|||
So in the case of Bitcoin a chunk is 2,016 headers, each of 80 bytes, |
|||
and chunk 5 is the block headers from height 10,080 to 12,095 |
|||
inclusive. When encoded as hexadecimal, the response string is twice |
|||
as long, so for Bitcoin it is 322,560 bytes long, making this a |
|||
bandwidth-intensive request. |
|||
|
|||
blockchain.block.get_chunk(**index**) |
|||
|
|||
**index** |
|||
|
|||
The zero-based index of the chunk, an integer. |
|||
|
|||
**Response** |
|||
|
|||
The binary block headers, as hexadecimal strings, in order |
|||
concatenated together. |
|||
|
|||
|
|||
blockchain.estimatefee |
|||
====================== |
|||
|
|||
Return the estimated transaction fee per kilobyte for a transaction to |
|||
be confirmed within a certain number of blocks. |
|||
|
|||
blockchain.block.get_chunk(**number**) |
|||
|
|||
**number** |
|||
|
|||
The number of blocks to target for confirmation. |
|||
|
|||
**Response** |
|||
|
|||
The estimated transaction fee in coin units per kilobyte, as a |
|||
floating point number. If the daemon does not have enough |
|||
information to make an estimate, the integer `-1` is returned. |
|||
|
|||
**Example Response** |
|||
|
|||
:: |
|||
0.00101079 |
|||
|
|||
|
|||
blockchain.headers.subscribe |
|||
============================ |
|||
|
|||
Subscribe to receive block headers when a new block is found. |
|||
|
|||
blockchain.headers.subscribe() |
|||
|
|||
**Response** |
|||
|
|||
The *deserialized header* [2]_ of the current block. |
|||
|
|||
**Notification Parameters** |
|||
|
|||
As this is a subcription, the client will receive a notification |
|||
when a new block is found. The parameters are: |
|||
|
|||
[**header**] |
|||
|
|||
|
|||
blockchain.numblocks.subscribe |
|||
============================== |
|||
|
|||
Subscribe to receive the block height when a new block is found. This |
|||
subscription is deprecated in favour of *blockchain.headers.subscribe* |
|||
which provides more detailed information. |
|||
|
|||
blockchain.numblocks.subscribe() |
|||
|
|||
**Response** |
|||
|
|||
The height of the current block, an integer |
|||
|
|||
**Notification Parameters** |
|||
|
|||
As this is a subcription, the client will receive a notification |
|||
when a new block is found. The parameters are: |
|||
|
|||
[**height**] |
|||
|
|||
|
|||
blockchain.relayfee |
|||
=================== |
|||
|
|||
Return the minimum fee a low-priority tx must pay in order to be accepted |
|||
to the daemon's memory pool. |
|||
|
|||
blockchain.relayfee() |
|||
|
|||
**Response** |
|||
|
|||
The fee in coin units as a floating point number. |
|||
|
|||
**Example Responses** |
|||
|
|||
:: |
|||
1e-05 |
|||
|
|||
:: |
|||
0.0 |
|||
|
|||
blockchain.transaction.broadcast |
|||
================================ |
|||
|
|||
Broadcast a transaction to the network. |
|||
|
|||
blockchain.transaction.broadcast(**raw_tx**) |
|||
|
|||
**raw_tx** |
|||
|
|||
The raw transaction as a hexadecimal string. |
|||
|
|||
**Response** |
|||
|
|||
Unfortunately the protocol version 1.0 API does not obey the JSON |
|||
specification for the response; this will be fixed in a future |
|||
version of the protocol. |
|||
|
|||
If the daemon accepts the transaction, return the transaction hash |
|||
as a hexadecimal string. If the daemon rejects the transaction, the |
|||
server must not return an error, but instead return the error |
|||
message string as the result. The client needs to determine if an |
|||
error occurred by comparing the result to the expected transaction |
|||
hash. |
|||
|
|||
**Response Examples** |
|||
|
|||
:: |
|||
|
|||
'a76242fce5753b4212f903ff33ac6fe66f2780f34bdb4b33b175a7815a11a98e' |
|||
|
|||
:: |
|||
|
|||
'258: txn-mempool-conflict' |
|||
|
|||
|
|||
blockchain.transaction.get |
|||
========================== |
|||
|
|||
Return a raw transaction. |
|||
|
|||
blockchain.transaction.get(**tx_hash**, **height**) |
|||
|
|||
**tx_hash** |
|||
|
|||
The transaction hash as a hexadecimal string. |
|||
|
|||
**height** |
|||
|
|||
The height at which it was confirmed, an integer. This parameter |
|||
is optional and ignored; it is recommended that clients do not |
|||
send it as it will be removed in a future protocol version. |
|||
|
|||
**Response** |
|||
|
|||
The raw transaction as a hexadecimal string. |
|||
|
|||
|
|||
blockchain.transaction.get_merkle |
|||
================================= |
|||
|
|||
Return the markle branch to a confirmed transaction given its hash |
|||
and height. |
|||
|
|||
blockchain.transaction.get(**tx_hash**, **height**) |
|||
|
|||
**tx_hash** |
|||
|
|||
The transaction hash as a hexadecimal string. |
|||
|
|||
**height** |
|||
|
|||
The height at which it was confirmed, an integer. |
|||
|
|||
**Response** |
|||
|
|||
A dictionary with keys *block_height*, *merkle* and *pos*. |
|||
*block_height* is the height of the block the transaction was |
|||
confirmed in. *merkle* is a list of transaction hashes the current |
|||
hash is paired with, recursively, in order to trace up to obtain |
|||
merkle root of the block, deepest pairing first. *pos* is the |
|||
0-based index of the position of the transaction in the ordered list |
|||
of transactions in the block. |
|||
|
|||
**Response Examples** |
|||
|
|||
:: |
|||
|
|||
{ |
|||
"merkle": |
|||
[ |
|||
"713d6c7e6ce7bbea708d61162231eaa8ecb31c4c5dd84f81c20409a90069cb24", |
|||
"03dbaec78d4a52fbaf3c7aa5d3fccd9d8654f323940716ddf5ee2e4bda458fde", |
|||
"e670224b23f156c27993ac3071940c0ff865b812e21e0a162fe7a005d6e57851", |
|||
"369a1619a67c3108a8850118602e3669455c70cdcdb89248b64cc6325575b885", |
|||
"4756688678644dcb27d62931f04013254a62aeee5dec139d1aac9f7b1f318112", |
|||
"7b97e73abc043836fd890555bfce54757d387943a6860e5450525e8e9ab46be5", |
|||
"61505055e8b639b7c64fd58bce6fc5c2378b92e025a02583303f69930091b1c3", |
|||
"27a654ff1895385ac14a574a0415d3bbba9ec23a8774f22ec20d53dd0b5386ff", |
|||
"5312ed87933075e60a9511857d23d460a085f3b6e9e5e565ad2443d223cfccdc", |
|||
"94f60b14a9f106440a197054936e6fb92abbd69d6059b38fdf79b33fc864fca0", |
|||
"2d64851151550e8c4d337f335ee28874401d55b358a66f1bafab2c3e9f48773d" |
|||
], |
|||
"block_height": 450538, |
|||
"pos": 710 |
|||
} |
|||
|
|||
|
|||
blockchain.utxo.get_address |
|||
=========================== |
|||
|
|||
Return the address paid to by a UTXO. This method is optional and |
|||
deprecated. |
|||
|
|||
blockchain.utxo.get_address(**tx_hash**, **index**) |
|||
|
|||
**tx_hash** |
|||
|
|||
The transaction hash as a hexadecimal string. |
|||
|
|||
**index** |
|||
|
|||
The zero-based index of the UTXO in the transaction. |
|||
|
|||
**Response** |
|||
|
|||
A Base58 address string, or *null*. If the transaction doesn't |
|||
exist, the index is out of range, or the output is not paid to and |
|||
address, *null* must be returned. If the output is spent *null* may |
|||
be returned. |
|||
|
|||
|
|||
server.banner |
|||
============= |
|||
|
|||
Return a banner to be shown in the Electrum console. |
|||
|
|||
server.banner() |
|||
|
|||
The return value is a string. |
|||
|
|||
|
|||
server.donation_address |
|||
======================= |
|||
|
|||
Return a server donation address. |
|||
|
|||
server.donation_address() |
|||
|
|||
The return value is a string. |
|||
|
|||
|
|||
server.peers.subscribe |
|||
====================== |
|||
|
|||
Return a list of peer servers. Despite the name this is not a |
|||
subscription and the server must send no notifications. |
|||
|
|||
server.peers.subscribe() |
|||
|
|||
**Response** |
|||
|
|||
An array of peer servers. Each entry is a triple like |
|||
|
|||
["107.150.45.210", "e.anonyhost.org", ["v1.0", "p10000", "t", "s995"]] |
|||
|
|||
The first element is the IP address, the second is the host name |
|||
(which might also be an IP address), and the third is a list of |
|||
server features. Each feature and starts with a letter. 'v' |
|||
indicates the server minimum protocol version, 'p' its pruning limit |
|||
and is omitted if it does not prune, 't' is the TCP port number, and |
|||
's' is the SSL port number. If a port is not given for 's' or 't' |
|||
the default port for the coin network is implied. If 's' or 't' is |
|||
missing then the server does not support that transport. |
|||
|
|||
|
|||
Version 1.1 (provisional) |
|||
------------------------- |
|||
|
|||
This protocol version is the same as version `1.0` except for the |
|||
following changes: |
|||
|
|||
* improved semantics of `server.version` to aid protocol negotiation |
|||
* deprecated methods `blockchain.address.get_proof`, |
|||
'blockchain.utxo.get_address' and `blockchain.numblocks.subscribe` |
|||
have been removed. |
|||
* method `blockchain.transaction.get` no longer takes a *height* |
|||
argument |
|||
* method `blockchain.transaction.broadcast` returns errors like any |
|||
other JSON RPC call. A *tx_hash* result is only returned on |
|||
success. |
|||
* new methods `server.features` and `server.add_peer` |
|||
|
|||
|
|||
server.version |
|||
============== |
|||
|
|||
Identify the client and inform the server the range of understood |
|||
protocol versions. |
|||
|
|||
server.version(**client_name**, **protocol_version** = ((1, 1), (1, 1))) |
|||
|
|||
**client_name** |
|||
|
|||
An optional string identifying the connecting client software. |
|||
|
|||
**protocol_verion** |
|||
|
|||
Optional with default value ((1, 1), (1, 1)). |
|||
|
|||
It must be a pair [`protocol_min`, `protocol_max`], each of which is |
|||
itself a [major_version, minor_version] pair. |
|||
|
|||
If a string was passed it should be interpreted as `protocol_min` and |
|||
`protocol_max` both being [1, 0]. |
|||
|
|||
The server should use the highest protocol version both support: |
|||
|
|||
protocol_version_to_use = min(client.protocol_max, server.protocol_max) |
|||
|
|||
If this is below |
|||
|
|||
min(client.protocol_min, server.protocol_min) |
|||
|
|||
there is no protocol version in common and the server must close the |
|||
connection. Otherwise it should send a response appropriate for that |
|||
protocol version. |
|||
|
|||
**Response** |
|||
|
|||
A pair |
|||
|
|||
[identifying_string, protocol_version] |
|||
|
|||
identifying the server and the protocol version that will be used |
|||
for future communication. |
|||
|
|||
**Example** |
|||
|
|||
:: |
|||
|
|||
server.version('2.7.11', ((1, 0), (2, 0))) |
|||
|
|||
|
|||
server.add_peer |
|||
=============== |
|||
|
|||
This call is intended for a new server to get itself into the server's |
|||
peers list. |
|||
|
|||
server.add_peer(**features**) |
|||
|
|||
* **features** |
|||
|
|||
The same information as a call to the client server's |
|||
**server.features** RPC call would return. |
|||
|
|||
|
|||
server.features |
|||
=============== |
|||
|
|||
Get a list of features and services supported by the server. |
|||
|
|||
server.features() |
|||
|
|||
**Response** |
|||
|
|||
A dictionary of keys and values. Each key represents a feature or |
|||
service of the server, and the value gives additional information. |
|||
|
|||
The following features MUST be reported by the server. Additional |
|||
key-value pairs may be returned. |
|||
|
|||
* **hosts** |
|||
|
|||
A dictionary of host names the server can be reached at. Each |
|||
value is a dictionary with keys "ssl_port" and "tcp_port" at which |
|||
the given host can be reached. If there is no open port for a |
|||
transport, its value should be *null*. |
|||
|
|||
* **server_version** |
|||
|
|||
The same identifying string as returned in response to *server.version*. |
|||
|
|||
* **protocol_version** |
|||
|
|||
A pair [`protocol_min`, `protocol_max`] of the protocols supported |
|||
by the server, each of which is itself a [major_version, |
|||
minor_version] pair. |
|||
|
|||
* **pruning** |
|||
|
|||
The history pruning limit of the server. If the server does not |
|||
prune return *null*. |
|||
|
|||
**Example Response** |
|||
|
|||
:: |
|||
|
|||
{ |
|||
"server_version": "ElectrumX 0.10.14", |
|||
"protocol_version": [[1, 0], [1, 1]], |
|||
"hosts": {"14.3.140.101": {"ssl_port": 50002, "tcp_port": 50001}}, |
|||
"pruning": null |
|||
} |
|||
|
|||
.. _JSON RPC 1.0: http://json-rpc.org/wiki/specification |
|||
.. _JSON RPC 2.0: http://json-rpc.org/specification |
Loading…
Reference in new issue