From 31755e1dace318c4e5475a73097cc78e9117d2de Mon Sep 17 00:00:00 2001 From: Neil Booth Date: Sat, 25 Mar 2017 12:06:53 +0900 Subject: [PATCH] Update PROTOCOL.rst and PEER_DISCOVERY.rst --- docs/PEER_DISCOVERY.rst | 56 +++++++++++++++++++++++++++++-------- docs/PROTOCOL.rst | 61 +++++++++++++++++++++++++++++------------ 2 files changed, 87 insertions(+), 30 deletions(-) diff --git a/docs/PEER_DISCOVERY.rst b/docs/PEER_DISCOVERY.rst index a69b9c3..699f9e2 100644 --- a/docs/PEER_DISCOVERY.rst +++ b/docs/PEER_DISCOVERY.rst @@ -72,11 +72,12 @@ Maintaining the Peer Database In order to keep its peer database up-to-date and fresh, if some time has passed since the last successful connection to a peer, an Electrum server should make an attempt to connect, choosing either the TCP or -SSL port. On connecting it should issue **server.peers.subscribe** -and **server.features** RPC calls to collect information about the -server and its peers, and if it is the first time connecting to this -peer, a **server.add_peer** call to advertise itself. Once this is -done and replies received it should terminate the connection. +SSL port. On connecting it should issue **server.peers.subscribe**, +**blockchain.headers.subscribe**, and **server.features** RPC calls to +collect information about the server and its peers. If the peer seems +to not know of you, you can issue a **server.add_peer** call to +advertise yourself. Once this is done and replies received it should +terminate the connection. The peer database should view information obtained from an outgoing connection as authoritative, and prefer it to information obtained @@ -84,13 +85,12 @@ from any other source. On connecting, a server should confirm the peer is serving the same network, ideally via the genesis block hash of the **server.features** -RPC call below. If the peer does not implement that call, perhaps -instead check the **blockchain.headers.subscribe** RPC call returns a -peer block height within a small number of the expected value. If a -peer is on the wrong network it should never be advertised to clients -or other peers. Such invalid peers should perhaps be remembered for a -short time to prevent redundant revalidation if other peers persist in -advertising them, and later forgotten. +RPC call below. Also the height reported by the peer should be within +a small number of the expected value. If a peer is on the wrong +network it should never be advertised to clients or other peers. Such +invalid peers should perhaps be remembered for a short time to prevent +redundant revalidation if other peers persist in advertising them, and +later forgotten. If a connection attempt fails, subsequent reconnection attempts should follow some kind of exponential backoff. @@ -200,3 +200,35 @@ the hard-coded peer list used to seed this process should suffice. Any peer on IRC will report other peers on IRC, and so if any one of them is known to any single peer implementing this protocol, they will all become known to all peers quite rapidly. + + +Notes to Implementators +----------------------- + +* it is very important to only accept peers that appear to be on the + same network. At a minimum the genesis hash should be compared (if + the peer supports the *server.features* RPC call), and also that the + peer's reported height is within a few blocks of your own server's + height. +* care should be taken with the *add_peer* call. Consider only + accepting it once per connection. Clearnet peer requests should + check the peer resolves to the requesting IP address, to prevent + attackers from being able to trigger arbitrary outgoing connections + from your server. This doesn't work for onion peers so they should + be rate-limited. +* it should be possible for a peer to change their port assignments - + presumably connecting to the old ports to perform checks will not + work. +* peer host names should be checked for validity before accepting + them; and *localhost* should probably be rejected. If it is an IP + address it should be a normal public one (not private, multicast or + unspecified). +* you should limit the number of new peers accepted from any single + source to at most a handful, to limit the effectiveness of malicious + peers wanting to trigger arbitrary outgoing connections or fill your + peer tables with junk data. +* in the response to *server.peers.subscribe* calls, consider limiting + the number of peers on similar IP subnets to protect against sybil + attacks, and in the case of onion servers the total returned. +* you should not advertise a peer's IP address if it also advertises a + hostname (avoiding duplicates). diff --git a/docs/PROTOCOL.rst b/docs/PROTOCOL.rst index d260c76..b5d4e52 100644 --- a/docs/PROTOCOL.rst +++ b/docs/PROTOCOL.rst @@ -703,37 +703,62 @@ Get a list of features and services supported by the server. The following features MUST be reported by the server. Additional key-value pairs may be returned. - * **hosts** +* **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*. + An dictionary, keyed by host name, that this server can be reached + at. Normally this will only have a single entry; other entries can + be used in case there are other connection routes (e.g. Tor). - * **server_version** + The value for a host is itself a dictionary, with the following + optional keys: - The same identifying string as returned in response to *server.version*. + * **ssl_port** - * **protocol_version** + An integer. Omit or set to *null* if SSL connectivity is not + provided. - A pair [`protocol_min`, `protocol_max`] of the protocols supported - by the server, each of which is itself a [major_version, - minor_version] pair. + * **tcp_port** - * **pruning** + An integer. Omit or set to *null* if TCP connectivity is not + provided. - The history pruning limit of the server as an integer. If the - server does not prune return *null*. + A server should ignore information provided about any host other + than the one it connected to. + +* **genesis_hash** + + The hash of the genesis block. This is used to detect if a peer is + connected to one serving a different network. + +* **server_version** + + A string that identifies the server software. Should be the same as + the response to **server.version** RPC call. + +* **protocol_max** +* **protocol_min** + + Strings that are the minimum and maximum Electrum protcol versions + this server speaks. The maximum value should be the same as what + would suffix the letter **v** in the IRC real name. Example: "1.1". + +* **pruning** + + An integer, the pruning limit. Omit or set to *null* if there is no + pruning limit. Should be the same as what would suffix the letter + **p** in the IRC real name. **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 + "genesis_hash": "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943", + "hosts": {"14.3.140.101": {"tcp_port": 51001, "ssl_port": 51002}}, + "protocol_max": "1.0", + "protocol_min": "1.0", + "pruning": null, + "server_version": "ElectrumX 1.0.1" } .. _JSON RPC 1.0: http://json-rpc.org/wiki/specification