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. 20
      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 the defaults around resource usage are conservative; I encourage you
to review them. 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 Required
-------- --------
@ -53,6 +58,7 @@ The following are required if you use the `run` script:
The username the server will run as. The username the server will run as.
Miscellaneous Miscellaneous
------------- -------------
@ -63,12 +69,6 @@ These environment variables are optional:
Must be a *NET* from one of the **Coin** classes in `lib/coins.py`_. Must be a *NET* from one of the **Coin** classes in `lib/coins.py`_.
Defaults to `mainnet`. 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** * **DB_ENGINE**
Database engine for the UTXO and history database. The default is 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 install the appropriate python package for your engine. The value
is not case sensitive. 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** * **HOST**
The host or IP address that the TCP and SSL servers will use when 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 If set then SSL_CERTFILE and SSL_KEYFILE must be defined and be
filesystem paths to those SSL files. filesystem paths to those SSL files.
* **RPC_PORT** * **RPC_HOST**
ElectrumX will listen on this port for local RPC connections. The host or IP address that the RPC server will listen on and
ElectrumX listens for RPC connections unless this is explicitly set defaults to `localhost`. To listen on multiple specific addresses
to blank. The default is appropriate for **COIN** and **NET** specify a comma-separated list. Servers with unusual networking
(e.g., 8000 for Bitcoin mainnet) if not set. 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 * **RPC_PORT**
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. 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** * **DONATION_ADDRESS**
@ -159,6 +157,22 @@ These environment variables are optional:
that **ANON_LOGS** is honoured. Defaults to 3600. Set to zero to that **ANON_LOGS** is honoured. Defaults to 3600. Set to zero to
suppress this logging. 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 Resource Usage Limits
--------------------- ---------------------

7
server/controller.py

@ -204,7 +204,7 @@ class Controller(util.LoggedClass):
async def main_loop(self): async def main_loop(self):
'''Controller main loop.''' '''Controller main loop.'''
if self.env.rpc_port is not None: 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.env.rpc_port)
self.ensure_future(self.bp.main_loop()) self.ensure_future(self.bp.main_loop())
self.ensure_future(self.wait_for_bp_catchup()) self.ensure_future(self.wait_for_bp_catchup())
@ -292,7 +292,7 @@ class Controller(util.LoggedClass):
self.state = self.LISTENING self.state = self.LISTENING
env = self.env env = self.env
host = env.cs_host() host = env.cs_host(for_rpc=False)
if env.tcp_port is not None: if env.tcp_port is not None:
await self.start_server('TCP', host, env.tcp_port) await self.start_server('TCP', host, env.tcp_port)
if env.ssl_port is not None: if env.ssl_port is not None:
@ -317,7 +317,8 @@ class Controller(util.LoggedClass):
self.header_cache.clear() self.header_cache.clear()
# Make a copy; self.sessions can change whilst await-ing # 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: for session in sessions:
await session.notify(self.bp.db_height, touched) 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.obsolete(['UTXO_MB', 'HIST_MB', 'NETWORK'])
self.db_dir = self.required('DB_DIRECTORY') self.db_dir = self.required('DB_DIRECTORY')
self.daemon_url = self.required('DAEMON_URL') self.daemon_url = self.required('DAEMON_URL')
coin_name = self.required('COIN') coin_name = self.required('COIN').strip()
network = self.default('NET', 'mainnet') network = self.default('NET', 'mainnet').strip()
self.coin = Coin.lookup_coin_class(coin_name, network) self.coin = Coin.lookup_coin_class(coin_name, network)
self.cache_MB = self.integer('CACHE_MB', 1200) self.cache_MB = self.integer('CACHE_MB', 1200)
self.host = self.default('HOST', 'localhost') self.host = self.default('HOST', 'localhost')
@ -43,6 +43,7 @@ class Env(lib_util.LoggedClass):
if self.ssl_port: if self.ssl_port:
self.ssl_certfile = self.required('SSL_CERTFILE') self.ssl_certfile = self.required('SSL_CERTFILE')
self.ssl_keyfile = self.required('SSL_KEYFILE') self.ssl_keyfile = self.required('SSL_KEYFILE')
self.rpc_host = self.default('RPC_HOST', 'localhost')
self.rpc_port = self.integer('RPC_PORT', 8000) self.rpc_port = self.integer('RPC_PORT', 8000)
self.max_subscriptions = self.integer('MAX_SUBSCRIPTIONS', 10000) self.max_subscriptions = self.integer('MAX_SUBSCRIPTIONS', 10000)
self.banner_file = self.default('BANNER_FILE', None) self.banner_file = self.default('BANNER_FILE', None)
@ -184,11 +185,20 @@ class Env(lib_util.LoggedClass):
return uvloop.EventLoopPolicy() return uvloop.EventLoopPolicy()
raise self.Error('unknown event loop policy "{}"'.format(policy)) 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 '''Returns the 'host' argument to pass to asyncio's create_server
call. The result can be a single host name string, a list of 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.''' host name strings, or an empty string to bind to all interfaces.
result = self.host.split(',')
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: if len(result) == 1:
result = result[0] 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 return result

2
server/peers.py

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

20
tests/server/test_env.py

@ -82,6 +82,9 @@ def test_COIN_NET():
os.environ['NET'] = 'testnet' os.environ['NET'] = 'testnet'
e = Env() e = Env()
assert e.coin == lib_coins.BitcoinCashTestnet assert e.coin == lib_coins.BitcoinCashTestnet
os.environ['NET'] = ' testnet '
e = Env()
assert e.coin == lib_coins.BitcoinCashTestnet
os.environ.pop('NET') os.environ.pop('NET')
os.environ['COIN'] = ' Litecoin ' os.environ['COIN'] = ' Litecoin '
e = Env() e = Env()
@ -97,10 +100,23 @@ def test_HOST():
assert_default('HOST', 'host', 'localhost') assert_default('HOST', 'host', 'localhost')
os.environ['HOST'] = '' os.environ['HOST'] = ''
e = Env() 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(for_rpc=False) == ['192.168.0.1', '23.45.67.89']
os.environ['HOST'] = '192.168.0.1 , 23.45.67.89 ' os.environ['HOST'] = '192.168.0.1 , 23.45.67.89 '
e = Env() 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']
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(): def test_REORG_LIMIT():
assert_integer('REORG_LIMIT', 'reorg_limit', assert_integer('REORG_LIMIT', 'reorg_limit',

Loading…
Cancel
Save