Browse Source

Minor environment variable improvements

- COIN and NET strip surrounding whitespace
- new environment variable RPC_HOST, similar to HOST, but
  for RPC.  Permits fine-grained control of which addresses
  the RPC server listens on.
- HOST and RPC_HOST strip surrounding whitespace from hostnames
  and IP addresses
- tests and documentation updated to match
master
Neil Booth 7 years ago
parent
commit
131344715a
  1. 60
      docs/ENVIRONMENT.rst
  2. 7
      server/controller.py
  3. 20
      server/env.py
  4. 2
      server/peers.py
  5. 22
      tests/server/test_env.py

60
docs/ENVIRONMENT.rst

@ -8,6 +8,11 @@ given, the rest will have sensible defaults if not specified. Many of
the defaults around resource usage are conservative; I encourage you
to review them.
Note: by default the server will only serve to connections from the
same machine. To be accessible to other users across the internet you
must set **HOST** appropriately; see below.
Required
--------
@ -53,6 +58,7 @@ The following are required if you use the `run` script:
The username the server will run as.
Miscellaneous
-------------
@ -63,12 +69,6 @@ These environment variables are optional:
Must be a *NET* from one of the **Coin** classes in `lib/coins.py`_.
Defaults to `mainnet`.
Note: if you are using Bitcoin Core post the August 1st fork, you
should have NET be `bitcoin-segwit`, and if on the Bitcoin Cash
chain NET should be `mainnet`.
Note Bitcoin Core >= 0.13.1 requires a special *NET* for testnet:
`testnet-segwit`.
* **DB_ENGINE**
Database engine for the UTXO and history database. The default is
@ -76,13 +76,6 @@ These environment variables are optional:
install the appropriate python package for your engine. The value
is not case sensitive.
* **REORG_LIMIT**
The maximum number of blocks to be able to handle in a chain
reorganisation. ElectrumX retains some fairly compact undo
information for this many blocks in levelDB. The default is a
function of **COIN** and **NET**; for Bitcoin mainnet it is 200.
* **HOST**
The host or IP address that the TCP and SSL servers will use when
@ -101,20 +94,25 @@ These environment variables are optional:
If set then SSL_CERTFILE and SSL_KEYFILE must be defined and be
filesystem paths to those SSL files.
* **RPC_PORT**
* **RPC_HOST**
ElectrumX will listen on this port for local RPC connections.
ElectrumX listens for RPC connections unless this is explicitly set
to blank. The default is appropriate for **COIN** and **NET**
(e.g., 8000 for Bitcoin mainnet) if not set.
The host or IP address that the RPC server will listen on and
defaults to `localhost`. To listen on multiple specific addresses
specify a comma-separated list. Servers with unusual networking
setups might want to specify e.g. `::1` or `127.0.0.1` explicitly
rather than defaulting to `localhost`.
* **EVENT_LOOP_POLICY**
An empty string (normally indicating all interfaces) is interpreted
as `localhost`, because allowing access to the server's RPC
interface to arbitrary connections aacross the internet is not a
good idea.
The name of an event loop policy to replace the default asyncio
policy, if any. At present only `uvloop` is accepted, in which case
you must have installed the `uvloop`_ Python package.
* **RPC_PORT**
If you are not sure what this means leave it unset.
ElectrumX will listen on this port for local RPC connections.
ElectrumX listens for RPC connections unless this is explicitly set
to blank. The default depends on **COIN** and **NET** (e.g., 8000
for Bitcoin mainnet) if not set, as indicated in `lib/coins.py`_.
* **DONATION_ADDRESS**
@ -159,6 +157,22 @@ These environment variables are optional:
that **ANON_LOGS** is honoured. Defaults to 3600. Set to zero to
suppress this logging.
* **REORG_LIMIT**
The maximum number of blocks to be able to handle in a chain
reorganisation. ElectrumX retains some fairly compact undo
information for this many blocks in levelDB. The default is a
function of **COIN** and **NET**; for Bitcoin mainnet it is 200.
* **EVENT_LOOP_POLICY**
The name of an event loop policy to replace the default asyncio
policy, if any. At present only `uvloop` is accepted, in which case
you must have installed the `uvloop`_ Python package.
If you are not sure what this means leave it unset.
Resource Usage Limits
---------------------

7
server/controller.py

@ -204,7 +204,7 @@ class Controller(util.LoggedClass):
async def main_loop(self):
'''Controller main loop.'''
if self.env.rpc_port is not None:
await self.start_server('RPC', ('127.0.0.1', '::1'),
await self.start_server('RPC', self.env.cs_host(for_rpc=True),
self.env.rpc_port)
self.ensure_future(self.bp.main_loop())
self.ensure_future(self.wait_for_bp_catchup())
@ -292,7 +292,7 @@ class Controller(util.LoggedClass):
self.state = self.LISTENING
env = self.env
host = env.cs_host()
host = env.cs_host(for_rpc=False)
if env.tcp_port is not None:
await self.start_server('TCP', host, env.tcp_port)
if env.ssl_port is not None:
@ -317,7 +317,8 @@ class Controller(util.LoggedClass):
self.header_cache.clear()
# Make a copy; self.sessions can change whilst await-ing
sessions = [s for s in self.sessions if isinstance(s, self.coin.SESSIONCLS)]
sessions = [s for s in self.sessions
if isinstance(s, self.coin.SESSIONCLS)]
for session in sessions:
await session.notify(self.bp.db_height, touched)

20
server/env.py

@ -31,8 +31,8 @@ class Env(lib_util.LoggedClass):
self.obsolete(['UTXO_MB', 'HIST_MB', 'NETWORK'])
self.db_dir = self.required('DB_DIRECTORY')
self.daemon_url = self.required('DAEMON_URL')
coin_name = self.required('COIN')
network = self.default('NET', 'mainnet')
coin_name = self.required('COIN').strip()
network = self.default('NET', 'mainnet').strip()
self.coin = Coin.lookup_coin_class(coin_name, network)
self.cache_MB = self.integer('CACHE_MB', 1200)
self.host = self.default('HOST', 'localhost')
@ -43,6 +43,7 @@ class Env(lib_util.LoggedClass):
if self.ssl_port:
self.ssl_certfile = self.required('SSL_CERTFILE')
self.ssl_keyfile = self.required('SSL_KEYFILE')
self.rpc_host = self.default('RPC_HOST', 'localhost')
self.rpc_port = self.integer('RPC_PORT', 8000)
self.max_subscriptions = self.integer('MAX_SUBSCRIPTIONS', 10000)
self.banner_file = self.default('BANNER_FILE', None)
@ -184,11 +185,20 @@ class Env(lib_util.LoggedClass):
return uvloop.EventLoopPolicy()
raise self.Error('unknown event loop policy "{}"'.format(policy))
def cs_host(self):
def cs_host(self, *, for_rpc):
'''Returns the 'host' argument to pass to asyncio's create_server
call. The result can be a single host name string, a list of
host name strings, or an empty string to bind to all interfaces.'''
result = self.host.split(',')
host name strings, or an empty string to bind to all interfaces.
If rpc is True the host to use for the RPC server is returned.
Otherwise the host to use for SSL/TCP servers is returned.
'''
host = self.rpc_host if for_rpc else self.host
result = [part.strip() for part in host.split(',')]
if len(result) == 1:
result = result[0]
# An empty result indicates all interfaces, which is not
# permitted for the RPC server.
if for_rpc and not result:
result = 'localhost'
return result

2
server/peers.py

@ -524,7 +524,7 @@ class PeerManager(util.LoggedClass):
# Use our listening Host/IP for outgoing connections so our
# peers see the correct source.
host = self.env.cs_host()
host = self.env.cs_host(for_rpc=False)
if isinstance(host, list):
host = host[0]
local_addr = (host, None) if host else None

22
tests/server/test_env.py

@ -82,8 +82,11 @@ def test_COIN_NET():
os.environ['NET'] = 'testnet'
e = Env()
assert e.coin == lib_coins.BitcoinCashTestnet
os.environ['NET'] = ' testnet '
e = Env()
assert e.coin == lib_coins.BitcoinCashTestnet
os.environ.pop('NET')
os.environ['COIN'] = 'Litecoin'
os.environ['COIN'] = ' Litecoin '
e = Env()
assert e.coin == lib_coins.Litecoin
os.environ['NET'] = 'testnet'
@ -97,10 +100,23 @@ def test_HOST():
assert_default('HOST', 'host', 'localhost')
os.environ['HOST'] = ''
e = Env()
assert e.cs_host() == ''
assert e.cs_host(for_rpc=False) == ''
os.environ['HOST'] = '192.168.0.1,23.45.67.89'
e = Env()
assert e.cs_host() == ['192.168.0.1', '23.45.67.89']
assert e.cs_host(for_rpc=False) == ['192.168.0.1', '23.45.67.89']
os.environ['HOST'] = '192.168.0.1 , 23.45.67.89 '
e = Env()
assert e.cs_host(for_rpc=False) == ['192.168.0.1', '23.45.67.89']
def test_RPC_HOST():
assert_default('RPC_HOST', 'rpc_host', 'localhost')
os.environ['RPC_HOST'] = ''
e = Env()
# Blank reverts to localhost
assert e.cs_host(for_rpc=True) == 'localhost'
os.environ['RPC_HOST'] = '127.0.0.1, ::1'
e = Env()
assert e.cs_host(for_rpc=True) == ['127.0.0.1', '::1']
def test_REORG_LIMIT():
assert_integer('REORG_LIMIT', 'reorg_limit',

Loading…
Cancel
Save